Short Answer
Try to use the same Begin/End
block as much as possible in order to maximize the amount of sprites that are batched and reduce the actual number of Draw calls made to the graphics device. In general, you only really need to use a different Begin/End
block when you want to change some of the parameters to SpriteBatch.Begin
.
Long Answer
I'll start with the second question:
Secondly, and this question is a a bit more subjective but, which design is generally preferable?
Take a look at the longest version of SpriteBatch.Begin
:
public void Begin (
SpriteSortMode sortMode,
BlendState blendState,
SamplerState samplerState,
DepthStencilState depthStencilState,
RasterizerState rasterizerState,
Effect effect,
Matrix transformMatrix
)
In general, as long as I don't need to change any of these parameters I try to use the same Begin/End
block for all of my Draw
operations. The reason should become evident when I address your other question later, but to sum it up, it's to reduce the amount of calls (render stage changes and draw operations) made to the graphics device.
Conversely, whenever I start a new Begin/End
block it's usually for a very specific reason. For instance, I might use separate blocks when:
- I need a group of sprites to be drawn with additive blending instead of alpha blending.
- I have a group of sprites that use a view matrix for camera movement, but there's another group of sprites that should always be drawn on top as an overlay, so the view matrix shouldn't affect them.
- I have different layers of sprites with parallax and implement parallax by passing a different view matrix to each layer.
- I need to use different shaders for different groups of sprites.
All of these cases fall under the general case above - one of the parameters to SpriteBach.Begin
needs to be changed.
As for the first question:
What are the performance implications of calling SpriteBatch.Begin/End in all these separate classes so often (once per class per drawing update) versus just calling it the one time each update.
The main implication is that you won't be taking advantage of the SpriteBatch
class that way - in particular you might end up with many more render stage changes and draw operations happening on the graphics device than what is really needed.
The reason for this is that the SpriteBatch
is not just a class for rendering sprites; it also tries to group as many sprites as it can into a single graphics device operation. For instance, if you draw ten sprites in a row using the same texture with the default SpriteBatch
parameters, the class will actually store the vertices for all of those sprites in the same vertex buffer, and submit it to the graphics device only once.
If you Begin/End
for each Draw
call then you'd get ten individual draw calls instead of just one. The performance implications should be clear from that.
Also the bit about using the same texture is important - as soon as you change to another texture it needs to stop batching, submit all previous vertices that have been batched so far, and start with an empty vertex buffer for the next sprites. This is why to get the best performance when using SpriteBatch
you should use spritesheets and group as many sprites as you can in the same texture (since changing source rectangle does not count as a change of texture).