Confused: why is libgdx 12x faster than slick2d?

Hey all, I hope someone is able to shed some light on a thing that baffles me. So far I’ve been using slick2d to build a game, which is nice because it gives me hardware accelleration and all. However, I just played around a bit with libdgx, and the way I’m doing things it appears libdgx is about 12x faster! I’m not sure if this is because I’m using slick2d wrong, my test is flawed, or whether there simply is such a difference. Can anyone help me with this?

Here’s some code I used to test this. Both run at roughly the same FPS on my machine, even though the libgdx one draws 12 x as many images. I draw the images in a circle so every image is drawn visibly to avoid differences in clipping or whatever (just so every image is actually rendered in the viewport and not all on the same spot).

Slick2d:

import org.newdawn.slick.AppGameContainer;
import org.newdawn.slick.BasicGame;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.SlickException;

public class SlickTest extends BasicGame {

	private Image image;
	private long lastFpsTime;
	private float angle;

	public SlickTest() {
		super("Test");
	}

	@Override
	public void render(GameContainer container, Graphics graphics) throws SlickException {
		for (int i = 0; i < 2500; i++) {
			angle += 6.28f / 200f;
			image.draw(200 + (float) Math.cos(angle) * 100f, 200 + (float) Math.sin(angle) * 100f);
		}
	}

	@Override
	public void init(GameContainer container) throws SlickException {
		image = new Image("image/test.png");
		lastFpsTime = System.nanoTime();
	}

	@Override
	public void update(GameContainer container, int delta) throws SlickException {
		if (System.nanoTime() - lastFpsTime > 1000000000) {
			System.out.println(container.getFPS());
			lastFpsTime = System.nanoTime();
		}
	}

	public static void main(String[] args) throws SlickException {
		AppGameContainer app = new AppGameContainer(new SlickTest());
		app.setShowFPS(false);

		app.setDisplayMode(800, 600, false);
		app.start();
	}
}

And for libgdx:

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;

public class ConstellationControl implements ApplicationListener {
	private OrthographicCamera camera;
	private SpriteBatch batch;
	private Texture texture;
	private Sprite sprite;
	long lastFpsTime;
	private float angle;

	@Override
	public void create() {
		float w = Gdx.graphics.getWidth();
		float h = Gdx.graphics.getHeight();

		camera = new OrthographicCamera(w, h);
		batch = new SpriteBatch();

		texture = new Texture(Gdx.files.internal("data/test.png"));

		sprite = new Sprite(texture);

		lastFpsTime = System.nanoTime();
	}

	@Override
	public void dispose() {
		batch.dispose();
		texture.dispose();
	}

	@Override
	public void render() {
		if (System.nanoTime() - lastFpsTime > 1000000000) {
			System.out.println(Gdx.graphics.getFramesPerSecond());
			lastFpsTime = System.nanoTime();
		}

		Gdx.gl.glClearColor(0, 0, 0, 1);
		Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);

		batch.setProjectionMatrix(camera.combined);
		batch.begin();
		for (int i = 0; i < 30000; i++) {
			angle += 6.28f / 200f;
			sprite.setPosition(100 + (float) Math.cos(angle) * 100f, 100 + (float) Math.sin(angle) * 100f);
			sprite.draw(batch);
		}
		batch.end();
	}

	@Override
	public void resize(int width, int height) {
	}

	@Override
	public void pause() {
	}

	@Override
	public void resume() {
	}
}

import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;

public class Main {
	public static void main(String[] args) {
		LwjglApplicationConfiguration cfg = new LwjglApplicationConfiguration();
		cfg.title = "constellationcontrol";
		cfg.useGL20 = false;
		cfg.width = 800;
		cfg.height = 600;

		new LwjglApplication(new ConstellationControl(), cfg);
	}
}

LibGDX: You write your own loop.
Slick2D: Controls the loop for you. Try using… app.setTargetFrameRate(300) in your main and see how much quicker it seems to be.

Hm I couldn’t get slick to go faster than vsync no matter what I tried.

Cas :slight_smile:

I haven’t actually tried it! So I wouldn’t know if it works.

However, I do know that Slick2D has a strict manager for the frame rate/update call rate, which is why the two versions are probably showing different speeds. I’d try them both with the same number of images, then scale the number up and up, and see at what point you start to get Slick2D going below the ‘normal’ frame rate.

I could be wrong though. Shortly, we’ll probably end up having KevGlass answering the question, or someone else who’s actually part of the development team.

Thanks for the replies! I did aim for an FPS of around 50, so the framerate limiting should not come into play. Anyway, I repeated the same test using:

app.setTargetFrameRate(300)

, but that did not make a difference.

Could it have something to do with using libdgx SpriteBatch.begin() and SpriteBatch.end(), and not using something similar for Slick2d? I have no idea ho to do something similar in Slick2d though.

my bet is that libgdx uses a better way to render the images. With this amount of images you are rendering, batching the does of course speed things up. And when slick doesn’t anything like that in the background it is just that why gdx is faster.

Last time I checked Slick2d code, it used immediate mode to render the sprites.

I’m pretty sure libgdx doesn’t use immediate mode, otherwise it wouldn’t make sense to call a begin/end for a batch (which probably means flushing the buffered vertex data using VBO or similar).

Recent versions of slick have a spritebatch class, but I’m not sure it’s used by default.

What? No this not true. At least I can’t find one :wink: I think davedes posted once such a SpriteBatch which was really cool and worked a lot like the one in libGDX.

Ultimately LibGDX should be more performant since it was written with low-spec in mind (Android), and uses more modern OpenGL standards (remember, Slick is now many years old). On top of that, Slick’s codebase is a bit of a mess, and there are many areas where it could be optimized.

[]As I’ve advised in the wiki, it’s best to use Image.startUse/endUse/drawEmbedded instead of Image.draw(). In short, Image.draw pushes quads individually to the GPU, whereas drawEmbedded allows you to push them altogether in a single list.
[
]Also described in the wiki is the “Vertex Array Renderer” which mimics LibGDX’s sprite batch, albeit with the fixed-function pipeline and Vertex Arrays instead of VBO/GLSL and the programmable pipeline. If you’re using the vertex array renderer, Image.draw() calls should be batched properly.
[]Slick clears the depth buffer by default (not sure why…). You may find a very slight performance increase by using container.setClearEachFrame(false) and then adding g.clear() to the beginning of your render method (it only clears the color buffer).
[
]There have been many improvements to Slick2D over the past few months, so please test with the latest version. Also be sure to use the latest version of LWJGL.
[*]You should also call container.setShowFPS(false) when benchmarking, and show FPS in some other manner (i.e. every couple seconds print it to stdout, or set the window title through Display.setTitle).

I wonder how libgdx compares to the awesomeness of the new SPGL2 sprite and effects engine.

Cas :slight_smile:

I think it’s time for another sprite-shootout… ;D

There is no such thing as ‘fixed-function vertex arrays’

You have immediate mode vs. retained mode
And fixed-function vs. shaders.

You can mix 'n match these (from immediate mode with shaders, to retained mode with fixed-function pipeline, to give you the odd extremes)

[quote]… albeit with the fixed-function pipeline and Vertex Arrays instead of VBO/GLSL and the programmable pipeline.
[/quote]
Happy now? ::slight_smile:

Very. You can never be too correct. Think of the children, among other things!

In slick2d Image.draw() binds a texture, and then draws it. The spritebatch only binds once if I recall correctly. Try to set your spritesheet in slick2d to startUse(), and drawEmbedded(), and then stopUse(). That way you avoid rebinding the same texture again and again.

Since we’re getting all technical with OpenGL language… :slight_smile:

Both draw() and startUse() bind a texture, but only when necessary. If the texture is already bound, it won’t be bound again.

The performance difference has to do with many calls to glBegin/glEnd (Image.draw) vs just one (Image.startUse/endUse). Image.draw() also has a bit of matrix math which could further slow things down.

By all means benchmark the shitout of things cas, especially on android, doesn’tmatter on the desktop imo :slight_smile:

Thanks a lot for this! I figured there must be things I could to to speed stuff up. I can’t test these improvements now since I’m on my work laptop right now, but will post the results. By the way, I do enjoy working with Slick2D a lot, it’s a very pleasant and accessible library to work with ;D

I’m 100% sure libgdx is rather a lot faster tha SPGL2 on Android coz I don’t have any native code or hax to help speed it up :smiley: Having said that I have been thinking perhaps I should base SPGL2 on top of low level libgdx.

Cas :slight_smile: