Optimizing Graphics(AutoBatch)

right here it says, “Try to minimize the total draw calls of your game. We should use batch draw as much as possible. Cocos2d-x 3.x has auto batch support, but it needs some effort to make it work.” im not sure what the docs mean by “It needs some effort to make it work” does this mean we have to do some type of configuration ? i thougth auto batch worked just like that automatically ? can anyone confirm ?

http://docs.cocos2d-x.org/cocos2d-x/en/advanced_topics/optimizing.html#the-cpu-is-often-limited-by-the-number-of-draw-calls-and-the-heavy-compute-operations-in-your-game-loop

My first suggestion would be: Using sprite sheets. this reduces draw calls. put as much as possible in one big sprite sheet. If you are using for each Sprite single image Files you get higher number of draw calls.

I’m using about 4-5 sprite sheets (with indexed PNG-8) in one scene … and everything seems to be fine. So auto batch is really automatic :slight_smile:

1 Like

Yes - you need sprite sheets for this. cocos2d-x automatically builds batches from the sprites to draw.

It’s easy as long as everything is on one sprite sheet. If you have too many graphics you should try to group the graphics on the sprite sheets depending on their z-order. E.g. build one sprite sheet for background objects, one for foreground and so on.

If you have interleaving use of sprite sheets this might increase draw calls. E.g. if objects of sheet A are in front of B and B in front of A and so on. This would require switching the textures and break batching.

2 Likes

ok so can we use the same sprite sheet on different scenes or its usually one sprite sheet or 2 sprite sheets for the background and forground of 1 scene ? also what if its a game like flappy bird that im making with a background some moving pipes and a flying object can all those go on the same sprite sheet ? or the background still needs its own sprite sheet and flying objects and pipe can go on the same one ?

You could put everything on one spritesheet, but you have to check the max texture size. I believe for (todays) mobile devices you shouldn’t bigger than 2048x2048px. If everything fits onto this size, you can use it :wink:

(non-paid) ad: A great tools for creating spritesheets is TexturePacker from @AndreasLoew.

2 Likes

Some basic rules:

  1. If possible: Try to put everything in 1 sprite sheet. Use 2048x2048 as max size. Bigger sizes might not work on all devices (especially Android) and/or cause artifacts because of the shader precision.

  2. If this does not work try to use the smallest number of sprite sheets. E.g. background/foreground.
    The important thing is to make sure that all sprites in one sheet stay behind all sprites of the other.

  3. If you use sprites in multiple levels or e.g. for the HUD, the main character,… you can also break rule 2 to save memory / download size by putting them in a separate sprite sheet.

Let me give you a general idea how sprite batching works (his might not be exactly how cocos2d-x is doing it, but should be close… I’ve not studied their algorithm and they might do things differently in some case). There are several ways to optimize this (e.g. checking sprites for overlaps, using z-buffers,…) but that’s the basic version:

Psudocode. This is not real code. I’ve also omitted several parts which are not required to understand what is happening.

sortBackgroundToForeground(sprites)

while(!sprites.empty())
{
	sprite = sprites.takeFirst() // select sprite with biggest distance to viewer
	if(sprite.sheet != currentSheetTexture)
	{
		// draw batched sprites
		opengl.bindTexture(currentSheetTexture) <--- set texture
		opengl.draw(batch)                      <--- the draw call

        // clear and start new batch
		batch.clear()
		currentSheetTexture = sprite.sheet
	}

    // add the sprite to the batch
	batch.appendSprite(sprite.position, sprite.scale, sprite.vertices)	
}
...

A new batch and draw call has to start each time a texture is changed. The sprites can be batched as long as all sprites use the same texture. This is why you should try to keep all sprites on the same sprite sheet (texture) but avoid switching back to an already used sheet.

3 Likes

Great explanation of the subject. It’s good to know how it’s working in general, because I didn’t know it :wink:

I think, I have to recreate my spritesheets with this knowledge.

2 Likes

Thats interesting, I’m usually set maximum to 4096, mostly it’s goes for iPad with such size and 2048 to iPhone. But what for example could be that artifact?

1 Like

What is the “HUD” so if I’m interpreting this right if we cannot fit all our sprites for our game on 1 sprite sheet use more than one , but keep all the background images of (same scene or all our scenes?) on 1 sprite sheet and then all our foreground objects of (1 scene or all our scenes?) on another sprite sheet, ?

Ok so we do then use different sprite sheets for different levels to save memory ? Would this be right for my game
MainMenuScene (just 1 background image) [1 sprite sheet for this 1 image]?
Level 1(1 background image 5 moving pipes) [1 sprite sheet for background image and 5 moving pipes]
And so on … and so on ? This is correct ?
Or should I have 2 sprite sheets in each level one for my background image (1 image only) and then 1 sprite sheet for my moving images (5 pipe images)

Could you give an example of what you mean by this?
“This is why you should try to keep all sprites on the same sprite sheet (texture) but avoid switching back to an already used sheet.”

If your background image is full screen, and adding it to a sprite sheet with the rest of the graphics (like the 5 moving pipes) would make the sprite sheet exceed the 2048 x 2048 size, then it’s pointless to put it into a sprite sheet.

As an example, in your resources, you would have something like this:

/common-spritesheet-0.png (like UI related items)
/level1-background.png
/level1-spritesheet-0.png
/level2-background.png
/level2-spritesheet-0.png

Now, a few assumptions for the above example:

1 - Your background images are too large to fit into the same sprite sheet for that level.
2 - The images for different levels cannot all fit on one sprite sheet.
3 - You want to have the common UI resources always loaded in memory, without needing to add them to each individual sprite sheet.

That is just one way to approach this, but in all seriousness, do you actually have a game running at the moment? If not, then perhaps you may want to get it running first and then see where you need to optimize it.

3 Likes

I have a prototype rite now still need to do some more things , I just wanted more info on sprite sheets. So your saying it’s alright to put all images from different levels into the same sprite sheet? Or would it be wrong to do this and waste of memory? For example I load my sprite sheet into level 1 but i also have images for level 2 in that sprite sheet , or is this ok to do if I can fit all images from different levels in 1 sprite sheet ? The blocks for each level are kind of small

Is this the correct way to use SpriteFrameCache to create sprites to get AutoBatch?

auto cache = SpriteFrameCache::getInstance();
cache->addSpriteFramesWithFile("test-sheet.plist", "test-sheet.png");
auto sprite = Sprite::createWithSpriteFrameName("test-frame1");
2 Likes

It’s entirely up to you, assuming they all do fit on one sprite sheet. The only thing to consider here is the memory usage, because having unused resources loaded just takes up memory for no reason, but even then if your app has a low memory footprint then it’s nothing to worry about.

Breaking up the graphics for your levels into separate sprite sheets just helps to reduce the memory usage while each level is loaded. You can unload the sprite sheet for the previous levels etc.

1 Like

@vkreal2 Yes that’s the way to do it, but you can also use this method:

void SpriteFrameCache::addSpriteFramesWithFile(const std::string& plist) 

where you pass only the plist filename, since the plist contains the name of the sprite sheet texture file, so it loads it automatically without you explicitly passing the name of the texture.

1 Like

Does anyone have any perf numbers for using sprite sheet vs individual sprites? I ask because trying to justify the work vs the gain.

1 Like

I’m usually set maximum to 4096, mostly it’s goes for iPad with such size and 2048 to iPhone. But what for example could be that artifact?

You should not see any artifacts if the shader precision is set to highp which should be the default in cocos2d-x. If it’s mediump sprites might appear blurry or shifted. Animations might jitter. The effect also depends on the position of the sprite vertices… this all also depends on the OpenGL implementation and the graphics hardware…

We’ve made some performance tests when we’ve implemented polygon trimming for TexturePacker. It compares untrimmed sprites (sprites with transparency) with trimmed sprites (transparency removed) and polygon mesh sprites (tightly cropped sprites). These sprites were all on a sprite sheet.

You can see the results here: Performance optimization for cocos2d-x using polygon sprite meshes

If you have one sprite only the results should be identical with the results for “untrimmed sprites” - because if you only use 1 sprite everything can be painted in one draw call… the things get interesting if you use multiple sprites.

Getting measurements is a bit difficult. On iPhone it was quite terrible because it changes the CPU/GPU frequency during measuring. Starting with a boost which gives you a high framerate then the boost wears off (presumably to save battery lifetime). The second problem with the measurement is the following: The rendering runs more or less parallel to your game logic…

I’d propose the following: Follow the rules from above. Don’t be too pedantic - just get your game working.
If you have to optimize the game later switching a sprite from one sheet to another does not take much effort. Cocos2d-x does not require you to know which sprite is on which sheet. All you have to do is to pack the sheets again and make sure they all get loaded.

3 Likes

For animations I very often get jitter for circle images, like coins and specially with gradual transparency around, like blur. Playing with TP options(trim margin, alpha threshold, extrude, paddings) doesn’t fix jitter of animation. I have a flipping animation of the coins and it’s jumps by x or y axis.
Removing trim option and\or set POT mostly helps, but first is more likely cause this bug in TP.
I contacted you about that a long time ago…, also for my current game I have a lot of very small circle spites which requires trim, some of them has just missing pixels at top or bottom, looks like cut out a 0.5px or something, that even with trim margin set to 8. Setting POT helps some of them, but I don’t have a time to fight with that and sent images, well I can’t share them actually, and report that and test. But maybe you want to research that, just use circle images in various size and add blur around, on the moving scene in the game they will be cut off.

Be assured that we’ve not added any circle detection code to make them look awkward :slightly_smiling_face:

I assume that you use “trim” and not “crop”. The latter might indeed introduce jittering because of the removed transparency. Trim keeps the transparency and if your sprites are aligned properly should not jitter.

How big is your texture? Is it bigger than 2048? If so this sounds like the shader precision could be the reason. Especially if slight changes in the coordinates make a change.

Just took a look at the shader code… it seems that they use meduimp for the texture coordinates:

varying mediump vec2 v_texCoord;

So… this might be the answer. Try making your texture smaller and see if it fixes your issues.