Draw Call with multi sprite sheet

Hi !

I have a problem with draw call when use more than 1 sprite sheet.

Ex:

I have sprite sheet S1 and S2

loop(10 times)

create Sprite A from S1

loop(10 times)

create Sprite B from S2

The draw call are 2.It normal.But…

loop(10 times)

create Sprite A from S1

create Sprite B from S2

The draw call are 20.

Because in drawBatchedTriangles function of Renderer class.It check previos material Id != current metarial id.And it increase draw call.How can i reduce draw call when use multi sprite sheet? Someone can help me?

you have two option.
1- Create one big SpriteSheet (S1 + S2). you can use texture packer.
2- Render spritesheets in two different node.

My game have many character.1 sprite sheet too large size.So i split it to more sprite sheet.All sprite place at game play.Cant use two node to add

Ps: Use two different node still not change your render command index.Only change global z order.

if i debug my render command show like that

Index Metarial Id

Command 1 123444422
Command 2 623442421
Command 3 123444422
Command 4 623442421
Command 5 123444422
Command 6 623442421
Command 7 123444422
Command 8 623442421
Command 9 123444422
Command 10 623442421

The answer is to addChild in order keeping same spritesheet sprites together, or if you need sprites to layer between each other then you can look into using the depth buffer and giving the sprites correct “layering” by setting their setPositionZ and then making sure you addChild all the sprites together by sprite sheet.


For more info:

Yep, without changing the engine, the order of adding children into the scene hierarchy (including the levels of child “levels”) matters with how the auto-batching system works.

1st main question: are you actually seeing performance issues on your target minimum device? While reducing draw calls is important, if you don’t need more than 2 or 3 of these spritesheets rendered at any given time then you probably don’t need to worry too much.

2nd less important Q: have you determined how much memory your target minimum device has for textures and if so even if you think it is wasteful you may want to consider using a single 2048x2048 or even 4096x4096 texture if all of your sub-spritesheets can fit into this larger one (which is even easier if you just texture pack all of the frame images together to allow for further packing).

The point here is that you need to care about performance and optimization, but you should also consider how much capability your target devices have to utilize.

If you are writing desktop only game, for example, you can look into 2D Texture Arrays, and on mobile if you’re able to customize the OpenGL startup code and otherwise enable GL ES 3.0.

Thanks for your answer. My game have more than 3 sprite sheet because too many character. As I know some iphone only allow 2048x2048 texture size,cant merge sprite sheet to one.

I need set sprites zorder in real time. So i cant add them in different nodes.You said about setPositionZ. It help layering like zorder?

Summary

1.If i use each layer to add same spritesheet sprites. It will reduce draw call. Because it render together.But i cant handle zorder sprites between layer

Ex.

Layer A add Sprite1,Sprite2 from SrpiteSheet1

Layer B add Sprite3,Sprite4 from SpriteSheet2

Cant handle zorder between Sprite1,Sprite2,Sprite3,Sprite4

2.If i use one layer to add all Sprite.I make more draw call.Because it break au-to batch

Yeah

  1. Enable depth. I believe this is done when creating GLView in AppDelegate. May also try setDepth(true) on Director or Scene or possibly the Camera
  2. then either (essentially to coerce the auto-batcher to draw all of one sheet in one call by having them ordered):
    a. Add all SSheet A to node A and all SSheet B to node B … or …
    b. addChild() all of sprites from SSheet A and then addChild all of sprites from SSheet B
  3. Call setPositionZ(depth) where depth = the range of the depth buffer (defaults to 1024 to -1024 I believe). Technically this would be your sprite’s ‘Z’ position (why it’s called that) in 3D space (yep all cocos2d games are rendered 3D)

Note: (2a) is probably smarter so you can remove/add during run-time. (2b) would just very slightly reduce the excessive layers in the node hierarchy, should that be something you need.

** you’ll probably want to read up on how the depth buffer works and read through some of the engine code and/or cpp_test example project to figure out how to use exactly.

*** you may need to look into how to use the discard statement and either change which shader is used or write a custom one.

Therefore, make sure you really need this, it’s a lot more work. Thus as I mentioned breifly, if your game is simple enough you’re not likely to notice any performance issues with higher draw calls unless you have lots of sprites on screen at once. And if that’s the case you should really re-write a lot of your rendering to not go through cocos2d’s API (search “bunnymark” or “bunny mark” within this forum).

Thanks!. I was searching about depth buffer and setPositionZ in cocos2d-x.Now i have solution for my game.:grinning:

Hi stevetranby

My solution is add all sprite same spritesheet to a node and i use depth test,positionz to layering.But have a problem

If enable depth test.Sprite transparency issues.How can fix this? Thanks!

The default shader doesn’t discard transparent pixels.

// This can probably simplified and wrapped up in a function, or whatever
// NO_MV is used by Sprites, if you use other rendering nodes you prob want the shader by trimming off "_NO_MV"
auto glcache = GLProgramCache::getInstance();
auto glprog = glcache->getGLProgram(GLProgram::SHADER_NAME_POSITION_TEXTURE_ALPHA_TEST_NO_MV);
if(glprog)
{
    // TODO: should use a custom shader uniform "u_alpha" or similar
    // - then attach to glprogstate instead of prog
    // - though this might cause batching to fail
    // - should move this into helper method(s) as to bullet proof it
    GLint alphaValueLocation = glGetUniformLocation(glprog->getProgram(), GLProgram::UNIFORM_NAME_ALPHA_TEST_VALUE);
    glprog->use();
    glprog->setUniformLocationWith1f(alphaValueLocation, 0.5f);
    CHECK_GL_ERROR_DEBUG();

    auto glstate = GLProgramState::getOrCreateWithGLProgram(glprog);
    setGLProgramState(glstate);

    CHECK_GL_ERROR_DEBUG();
}

I’m a little perplexed on this one. I’ve been using 3.10 with no problems, but upgraded to 3.16 (which has a lot of benefits!), and suddenly I’m getting an exception with Renderer::drawBatchedTriangles(). Searching brought up this post, and I’m loading multiple spritesheets as well.

  • a sheet for controls
  • a sheet for pieces that go on the football field
  • and then, depending on which teams the players select, two spritesheets of those teams’ players images

That’s never been a problem before. Is it a problem now, or is my exception unrelated?

thanks!