libgdx: Terrible performance with very few draw calls

I got a bit of a shock today when I tried the game I’ve been working on with a proper device and found that my tiny game is running at about 20fps. :frowning:

In-game, at the moment I’ve got 16 tiles, one sprite, 6 patches and 6 bitmap font draw calls. On an S4 I’d hope that’d be capable of hitting 60fps without breaking a sweat.

Simplifying it, my hacked main menu now just has 20 tiny buttons on it, which is each a single call to BitmapFont.draw and NinePatch.draw. This gets ~15fps.

I have a single libgdx Stage.draw() set to the full screen res, and a second SpriteBatch that’s just used to draw the fps text. The app is set to use GL2.0, but it doesn’t make a different to the fps either way. Just drawing the fps display gets me a solid 60fps.

Textures are only being loaded at startup. Fillrate doesn’t seem to be an issue since it occurs no matter how small I make the buttons.

DDMS’s frame time measurement says it’s taking 68ms to ‘Process’, whatever that covers, with ‘Draw’ at 9ms and ‘Execute’ at 0.7ms.

I’m fairly sure I must be doing something stupid here since I’m new to libgdx. Anyone have any pointers? Thanks.

Could you be accidentally calling some “new _____” inside your draw/render loop?

I once accidentally called new ShapeRenderer() inside my render loop and it killed my frame rate to like 5% of what it should have been.

Here’s a bunch of Android considerations:

[*]How many draw calls are you actually getting? It should be really low if you only have a few textures. Test like so:

SpriteBatch.totalRenderCalls = 0;

// ... render entire scene here ... //

int calls = SpriteBatch.totalRenderCalls;

//Log or render to screen
System.out.print(calls);

[]Your shader code needs to be highly optimized (if you are doing anything there). Avoid stuff like dependent texture reads and branches
[
]Make sure you’re packing textures as much as you can. You can use PixmapPacker to pack at runtime. Ideally, if you can fit all textures into a single atlas it should lead to significant boost in performance.
[]Try not to use two sprite batches. This leads to two shaders (and possibly two VBOs), and thus state switches which can be costly on mobile. If you must use two batches, you can pass shared shaders and Mesh buffers to the constructor.
[
]Avoid allocations like namrog84 said. Especially stuff like Strings concatenation. LibGDX includes a StringBuffer implementation which is really low on allocations, and great for rendering frequently changing text and numbers.
[]If you are using Groups for your scene2D graph, it might lead to batch flushes
[
]If you’re finding text rendering to be the slowdown, other than tightly packed atlases you can also look into using BitmapFontCache directly. This caches the vertex data for each glyph which leads to a slight boost.
[]Similarly, you can use SpriteCache instead of SpriteBatch to see if caching vertex data improves performance. This is probably negligible at this point in your game, though!
[
]Use nearest filtering where possible and POT textures

From some more poking I’m getting 40 batches per frame, which doesn’t sound like a lot but could be part of the problem.

I’m doing something like this:


class TextButton extends Actor
{
  public void draw(SpriteBatch batch)
  {
     patch.draw(batch);
     text.draw(batch);
  }
}

This seems to be causing a draw batch for every button since the nine patch and text use different textures. Is there a way to layer these at runtime via sprite batch (so all text is drawn over the top), or do I need to start merging the textures into an atlas somehow?

Also I can’t see how I’d avoid having two batches if I want to draw the fps text separately. Stage doesn’t expose the sprite batch. I guess I could add an Actor for the fps but that seems like a sledgehammer to crack a nut.

Its very easy to use the Atlas in libgdx
Can be pretty much done in 2 lines, a texture atlas, then findregion


//Art.loaded up earlier on
TextureAtlas myTextures = new TextureAtlas("data/art.txt");

TextureRegion playerFrontTR = myTextures.findRegion("playerForward");
TextureRegion playerBackTR = myTextures.findRegion("playerBack");
TextureRegion playerSideTR = myTextures.findRegion("playerSide");

Just using the texturepacker, the findRegion will match the original filenames, so for example, before I packed it, it was “playerForward.png”

Then you can just apply your TR the same way as you would textures, to your sprites/buttons or whatever

40 batches sounds like a lot for simple menu UI on mobile. Think more along the lines of 4 or 5… :stuck_out_tongue:

When creating a Stage, pass the shared SpriteBatch as the last constructor.

It probably won’t be easy to re-order the draw calls and still keep the utility of an Actor (position etc). Maybe this could be achieved with z-index, which changes the sort value of the actor.

TexturePacker GUI is pretty useful for texture packing; or TexturePacker2 if you’d rather work with a command-line utility. There is also PixmapPacker which is good during development:
http://www.badlogicgames.com/wordpress/?p=2297

Hmm. Packing the couple of textures and font at runtime means I’ve now got my 40-odd sprites and text being drawn in one batch, so that’s good - however in-game with a small tile map I’m still only seeing 20-25fps (which is an improvement, but far from 60fps). And DDMS says I’m still taking 20ms for ‘Process’ per frame.

Needs more probing…

Really weird, I get better performance than what you describe on a pretty ancient HTC Hero test device. Do you have any code to show?

It does feel like there’s something else going on I’m missing. Next step is to strip back the code to the bare minimum and see how performance changes (if at all).

Aha! Looks like the code is fine - it’s actually the debugger (more specifically, DDMS) that’s causing the drop. Running the app from the device gets 60fps, but if I then get DDMS to connect to that process the framerate drops to 20ms. Shutting down eclipse (and thus DDMS) makes the performance shoot back up to 60fps.

It looks like this old (and still open) bug from 2011: https://code.google.com/p/android/issues/detail?id=21190

ah that would explain it. I only use logcat.
I mean with libgdx you can just develop entirely on pc, debugging, profiling everything.
I only occasionally push to device. Its only really interesting for touch specifics, dpi/ppi stuff / screensize and thats about it.
Which is one of the great point about libgdx =D

I’m not sure that’s true; DDMS seems to be a required part of the debugger backend - you’re using it even if you’re not using the DDMS perspective in eclipse.

I’m not entirely convinced I’ve got all the problems out though - I’m still seeing very spiking framerate behaviour even without DDMS. I don’t know if this is standard for android and getting a smooth 60fps is impossible?

Yeah I also thought that was the case… Never had any problems thats I know of…

Well its very easy on my galaxy S3, maybe you should post your whole code or whatever, shrunk down to bare minimum where the problem still persists.
But yeah 40 batches per frame are bad. It should be 2-6 or whatever

There is a huge performance issue if you use ddms, normally our game has 60 fps, with debugger 10-30.
on android you should care about the texture you bind and how often you bind it. Put all textures which are rendered in one step in one atlas-sheet.
Your atlas shouldn’t be bigger than 1024x1024, otherwise “old” mobiles can’t bind it.