Skip to content

Cocos2D Render Order and Batching

slembcke edited this page Sep 9, 2014 · 1 revision

Automatic batching was added in Cocos2D 3.1. This means that you no longer need to use CCSpriteBatchNode in order to use efficient batched rendering. While automatic batching "just works" in that it will do the best it can to to group rendering commands together, but there is a lot you can do to make your scenes more batchable.

At the core, it's important to think of the order you are asking Cocos2D to draw things how you are asking it to draw them.

Render Order:

Remember that everything is a CCNode (including CCScene). A node's negative children are drawn (z < 0), then the node itself, then the zero and positive nodes (z >= 0). This is applied recursively to all nodes starting at the scene. When child nodes share the same z-value, the node that is inserted first gets rendered first.

Some node classes don't actually draw anything and are just used for organization or layout. Ex: CCNode, CCScene, CCParallaxNode, etc. As far as the renderer is concerned, these classes don't exist and have no effect on batching.

See [CCNode visit:parentTransform:] (https://github.com/cocos2d/cocos2d-swift/blob/release-3.1.1/cocos2d/CCNode.m#L912) for more details.

Render States:

Cocos2D is optimized for the most common usage, rendering basic sprites. A render state is a combination of blending mode, shader, and a set of shader uniforms. For nodes that don't use custom uniforms (CCNode.shaderUniforms), the render state it uses will be cached so that you are guaranteed to get the same render state object for two different nodes if they use the same blend mode, shader and texture. Since the blending mode and shader is usually left on default values, you usually only have to worry about the texture. In other words, use sprite sheets!

Custom rendering (using CCNode.shaderUniforms, custom render states, is covered later).

Examples:

Cocos2DRenderOrder.svg

Some things to note:

  • A, B, and C render before D because they have negative z-values.
  • E and F render after D because they have z-values of 0 and higher.
  • The non-renderable nodes in the scene don't interrupt batching, but that can change the render orders of their children.
  • The red nodes are split up by the blue node. By adjusting some of the z-values the red nodes could all be batched together. This might not be possible without ruining an effect or layering.
  • It's also possible that the red and blue render states could be joined simply by using a sprite sheet or doing something clever with shaders.

More examples that will batch:

  • Creating a CCNode and adding children to it that share the same texture (or sprite sheet), blending mode and shader.
  • Rendering a number of CCNodeColor or CCNodeGradient nodes in sequence.
  • Rendering several CCLabelBMFont nodes in sequence that share the same font.

More examples that won't batch:

  • Rendering several CCLabelTTF in a row. (Each one makes a unique texture.)
  • Rendering several CCSprite nodes in sequence where each has a child with an additive glow sprite (blending mode change).
  • Rendering several sprites in sequence that have a child sprite using a additive blending mode for a glow. (It will have to switch blend modes between each sprite. default, additive, default, additive, ...)

What about custom rendering?

Custom Uniform Path:

CCNodes that have custom uniforms (CCNode.shaderUniforms) cannot normally be batched. It's more expensive to check if two nodes share uniforms than it is to draw them in separate batches.

See CCNode.renderState (https://github.com/cocos2d/cocos2d-swift/blob/release-3.1.1/cocos2d/CCNode.m#L1739) for more information.

Custom Render States:

If you want to share custom uniforms between several nodes, you can override the CCNode.renderState property to return a shared value. Then you can batch their drawing even with custom uniforms.

When you create a render state, you can ask it to copy the uniform dictionary or not. If it's copied, then the render state is known to be immutable and that means that it doesn't need to be copied later. It also means that you must create a new render state if you want to change the uniforms and cannot use a mutable uniform dictionary. It's a tradeoff.

Custom Draw Methods:

If you are implementing custom drawing methods that use custom GL code, it forces the renderer to break up batches. The renderer has no way to know how you changed the GL state, so it has to reset the batch afterwards.

If you are implementing custom rendering methods using the "enqueueTriangles" method of CCRenderer, then batching will work normally. Enqueued rendering commands will be batched if they share the same render state and global sorting order as the previous command.