سؤال

I have seen in several places, including the source code of CCSpriteBatchNode that it is "expensive" to add/remove childs from it. My understanding is that the whole point of using batch nodes is to prevent expensive OpenGL calls from happening over and over when many sprites from the same sprite sheet are being added to the same container.

What I am wondering is 1) how "expensive" is adding / removing childs to a sprite batch node, and 2) when is it considered a appropriate to make use of one?

For example, I have a laser object which creates ten sprites... as it moves across the screen, it shows/hides the current sprite for that given screen position. When it reaches the far right edge of the screen, the laser object is discarded, and so are the ten sprites. So, I was wondering, is this a case where a sprite batch node would be not appropriate to use because it's only 10 sprites, and it happens so fast-- The move animation is 0.2 seconds, so that if the player were to rapidly fire, that would mean adding/removing 10 sprites to a batch node over and over...

In other cases, I have a SpriteBatchNode already setup for various objects, and occasionally I come across a one-off sprite that needs to be added, and it just happens to be part of the same sprite sheet, so I am tempted to add it to that batch node since it's there, and it's designated to that particular sprite sheet already...... Anyway, I'd love to get some clarification on this topic.

هل كانت مفيدة؟

المحلول

The main difference between a CCSpriteBatchNode and a normal CCSprite is the fact that a CCSpriteBatchNode sends all the data of all the sprites at once to the GPU instead that doing it for each sprite.

A CCSprite draw call works in the following way:

glVertexAttribPointer(kCCVertexAttrib_Position, 3, GL_FLOAT, GL_FALSE, kQuadSize, (void*) (offset + diff));
glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, kQuadSize, (void*)(offset + diff));
glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_UNSIGNED_BYTE, GL_TRUE, kQuadSize, (void*)(offset + diff));
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

Basically 3 calls are made to set the data of the sprite and then a call to glDrawArrays is done. If you have 100 sprite this code is executed 100 times.

Now let's look at CCSpriteBatchNode (I chose the implementation without VAO, which is another possible optimization):

glVertexAttribPointer(kCCVertexAttrib_Position, 3, GL_FLOAT, GL_FALSE, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, vertices));
glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_UNSIGNED_BYTE, GL_TRUE, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, colors));
glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, texCoords));
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffersVBO_[1]);
glDrawElements(GL_TRIANGLE_STRIP, (GLsizei) n*6, GL_UNSIGNED_SHORT, (GLvoid*) (start*6*sizeof(indices_[0])) );

Now this code sets all the data of all the sprites at once, since it's stored in contiguous memory. This call is the same for 1, 10, 100, whatever amount of sprites.

That's why it is more efficient, but at the same time, since the data is stored contiguosly in memory, when a child is removed or added or modifiyed, the array must be changed accordingly and updated in the GPU. That's where the cost of adding and removing comes from (or even the fact that a hidden CCSprite just skips the rendering phase while a hidden CCSprite in a batch node doesn't)

From personal experience I can tell you that the cost is usually negligible and you should always use a CCSpriteBatchNode when you can (since they have their limits, like blending over the whole node and not on a per sprite basis and similar things) and when you are drawing more than a bunch of sprites of the same kind/reason.

Benchmarking it for your self should be easy though.

نصائح أخرى

1) how "expensive" is adding / removing childs to a sprite batch node

The only scenario I am aware that it can be "expensive" is when you have to increase the atlas capacity. You see, batch nodes have a capacity, and if you add a child that surpasses it, the node will have to increase its capacity and recalculate texture coordinates for all sprites.

To fix this, you simply give your batch node a reasonable capacity to begin with - not too little and not too much. It's up to you to identify such number, depending on your needs.

2) when is it considered a appropriate to make use of one?

Whenever you have several sprites that can use the same texture source. For a Mario game, it is clear that you will need several coins on the screen. This would be a good use case for a batch node: have a batch node for the coin image, and then all your coin sprites will use this batch node.

Sometimes you can pack several elements into the same texture. Say, you could fit a coin image, a monster image, and a mushroom image all in the same texture. This way, all your coins, monsters and mushrooms could use the same batch node.

You shouldn't need batch nodes for things like background textures, because you probably only need one background sprite anyway.

So, I was wondering, is this a case where a sprite batch node would be not appropriate to use because it's only 10 sprites, and it happens so fast-- The move animation is 0.2 seconds, so that if the player were to rapidly fire, that would mean adding/removing 10 sprites to a batch node over and over...

This is a valid use case for a batch node. 10 sprites are drawn simulatenously, after all. And, if you know that you won't be using a laser object anymore, you can always unload the corresponding batch node. I imagine that you may have several laser objects in your game, so a batch node is a good idea.

Frankly, don't worry much about performance. I use dozens in my game all the time for all sorts of things (characters, weather particles, map objects, collectibles, interface, etc), and thanks to them I rarely ever see it fall below 55fps.

In fact, I find it hard to argue against using batch nodes. They rarely cause any harm.

As previously said, a sprite batch node batches the calls to GPU for all its children (since they use the same texture). However for that to make an impact on the performance, a good amount of sprite must be involved. For 10 sprites, I do not think it would make a difference...

That said, please note that if you are using a new version of Cocos2d (like 3.0), the 3.1 which is now in beta offers automatic batching so you do not need to waste your time playing around with CCSpriteBatchNode. Cocos2d will batch the data sent to the GPU automatically.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top