Grunnt
|
 |
«
Posted
2012-08-09 15:33:03 » |
|
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: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| 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: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| 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() { } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 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); } } |
|
|
|
|
UprightPath
|
 |
«
Reply #1 - Posted
2012-08-09 16:05:38 » |
|
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.
|
|
|
|
princec
|
 |
«
Reply #2 - Posted
2012-08-09 16:28:24 » |
|
Hm I couldn't get slick to go faster than vsync no matter what I tried. Cas 
|
|
|
|
Games published by our own members! Check 'em out!
|
|
UprightPath
|
 |
«
Reply #3 - Posted
2012-08-09 16:57:09 » |
|
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.
|
|
|
|
Grunnt
|
 |
«
Reply #4 - Posted
2012-08-09 17:02:46 » |
|
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: 1
| 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.
|
|
|
|
Danny02
|
 |
«
Reply #5 - Posted
2012-08-09 17:14:00 » |
|
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.
|
|
|
|
|
teletubo
|
 |
«
Reply #6 - Posted
2012-08-09 17:24:23 » |
|
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).
|
|
|
|
sproingie
|
 |
«
Reply #7 - Posted
2012-08-09 17:43:05 » |
|
Recent versions of slick have a spritebatch class, but I'm not sure it's used by default.
|
|
|
|
|
R.D.
|
 |
«
Reply #8 - Posted
2012-08-09 19:36:40 » |
|
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  I think davedes posted once such a SpriteBatch which was really cool and worked a lot like the one in libGDX.
|
|
|
|
davedes
|
 |
«
Reply #9 - Posted
2012-08-09 20:09:03 » |
|
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).
|
|
|
|
Games published by our own members! Check 'em out!
|
|
princec
|
 |
«
Reply #10 - Posted
2012-08-09 20:12:41 » |
|
I wonder how libgdx compares to the awesomeness of the new SPGL2 sprite and effects engine. Cas 
|
|
|
|
davedes
|
 |
«
Reply #11 - Posted
2012-08-09 20:13:35 » |
|
I wonder how libgdx compares to the awesomeness of the new SPGL2 sprite and effects engine. Cas  I think it's time for another sprite-shootout... 
|
|
|
|
Riven
|
 |
«
Reply #12 - Posted
2012-08-09 20:13:45 » |
|
with fixed-function Vertex Arrays instead of VBO/GLSL.
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)
|
|
|
|
davedes
|
 |
«
Reply #13 - Posted
2012-08-09 20:24:25 » |
|
... albeit with the fixed-function pipeline and Vertex Arrays instead of VBO/GLSL and the programmable pipeline. Happy now?
|
|
|
|
Riven
|
 |
«
Reply #14 - Posted
2012-08-09 20:25:12 » |
|
Happy now? Very. You can never be too correct. Think of the children, among other things!
|
|
|
|
Mads
|
 |
«
Reply #15 - Posted
2012-08-09 22:48:20 » |
|
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.
|
|
|
|
davedes
|
 |
«
Reply #16 - Posted
2012-08-09 23:03:26 » |
|
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...  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.
|
|
|
|
badlogicgames
|
 |
«
Reply #17 - Posted
2012-08-10 02:41:57 » |
|
By all means benchmark the shitout of things cas, especially on android, doesn'tmatter on the desktop imo 
|
|
|
|
Grunnt
|
 |
«
Reply #18 - Posted
2012-08-10 08:45:50 » |
|
- 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).
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 
|
|
|
|
princec
|
 |
«
Reply #19 - Posted
2012-08-10 10:51:11 » |
|
By all means benchmark the shitout of things cas, especially on android, doesn'tmatter on the desktop imo  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  Having said that I have been thinking perhaps I should base SPGL2 on top of low level libgdx. Cas 
|
|
|
|
Grunnt
|
 |
«
Reply #20 - Posted
2012-08-10 14:27:30 » |
|
Ok, thanks a lot to davedes for the advice. I adjusted the slick code, and now it renders at roughly the same speed as the libdgx code  So the main issue appeared to be the use of the "begin" and "end" commands, and using drawEmbedded instead of draw. I just have to figure out rotation etc.. Here's the faster code: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| 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 { image.startUse(); for (int i = 0; i < 30000; i++) { angle += 6.28f / 200f; image.drawEmbedded(200 + (float) Math.cos(angle) * 100f, 200 + (float) Math.sin(angle) * 100f, image.getWidth(), image.getHeight()); } image.endUse(); }
@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.setTargetFrameRate(300); app.start(); } } |
|
|
|
|
badlogicgames
|
 |
«
Reply #21 - Posted
2012-08-10 15:30:50 » |
|
By all means benchmark the shitout of things cas, especially on android, doesn'tmatter on the desktop imo  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  Having said that I have been thinking perhaps I should base SPGL2 on top of low level libgdx. Cas  Yes, yes you should. We still haven't had your lovely skype convo. Maybe this weekend?
|
|
|
|
ReBirth
|
 |
«
Reply #22 - Posted
2012-08-10 15:32:59 » |
|
@OP maybe your faster slick code can be put on wiki.
|
|
|
|
appel
|
 |
«
Reply #23 - Posted
2012-08-10 15:39:33 » |
|
Valuable thread. I'm sure a few have wondered about the exact same question and reasons for this. And although I knew it was a question about rendering techniques, using immediate mode vs. vertex arrays/buffer, we now have an answer.
Hopefully those in charge on Slick now will revolutionize how rendering is accomplished to make it comparable to libgdx speeds.
|
|
|
|
Grunnt
|
 |
«
Reply #24 - Posted
2012-08-10 17:11:33 » |
|
Hopefully those in charge on Slick now will revolutionize how rendering is accomplished to make it comparable to libgdx speeds. I think they already did that  At least, the Slick2D " Performance & Memory Tips" wiki entry gives all the information you need.
|
|
|
|
Grunnt
|
 |
«
Reply #25 - Posted
2012-08-10 18:08:20 » |
|
This is soo cool! Now I can support 10 times as many ships (15000 simultaneously quite easily) using Slick2D. I'm so happy 
|
|
|
|
Riven
|
 |
«
Reply #26 - Posted
2012-08-10 18:30:32 » |
|
Why is nobody abusing the new try-with syntax yet?  1 2 3 4 5
| try (image.with()) { for (int i = 0; i < n; i++) { image.useEmbedded(...); } } |
|
|
|
|
R.D.
|
 |
«
Reply #27 - Posted
2012-08-11 16:47:29 » |
|
Why is nobody abusing the new try-with syntax yet?  1 2 3 4 5
| try (image.with()) { for (int i = 0; i < n; i++) { image.useEmbedded(...); } } |
Oh this is the first time I see this  What exactly does this?
|
|
|
|
ReBirth
|
 |
«
Reply #28 - Posted
2012-08-12 05:27:35 » |
|
The try-with syntax as far as I know is used for close something that should be closed, like *Stream object. I still dont know if it'll trigger close() method automatically or not.
|
|
|
|
sproingie
|
 |
«
Reply #29 - Posted
2012-08-12 07:00:03 » |
|
I still dont know if it'll trigger close() method automatically or not.
It will, as soon as the scope of the try block is exited. That's the contract of AutoClosable which is what makes the whole try-with-resource thing work.
|
|
|
|
|
|