5
\$\begingroup\$

I notice some code samples only call SpriteBatch.Begin and SpriteBatch.End in the game's main draw method and then draw everything within that method through direct SpriteBatch.Draw calls or indirect calls through method calls from other classes that store a reference to the SpriteBatch object and then use its Draw method.

Another approach I see is that in these separate classes they call SpriteBatch.Begin and SpriteBatch.End before their drawing code in each of these classes and don't rely on already being contained within a drawing session (between a begin and end call).

My question is two-fold. Firstly, as the title suggests, 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. Secondly, and this question is a a bit more subjective but, which design is generally preferable?

Thanks.

\$\endgroup\$
3
  • \$\begingroup\$ I presume each time you begin and end a spritebatch that server renderstates have to pushed (during both begin and end). Changing renderstates has a cost that you would be paying many times. \$\endgroup\$
    – Nic Foster
    Feb 21, 2012 at 20:00
  • 1
    \$\begingroup\$ Measure, don't presume. In this way harmony is restored. \$\endgroup\$ Feb 21, 2012 at 20:09
  • \$\begingroup\$ @PatrickHughes: Which is why I posted it as a comment, rather than answer. ;) \$\endgroup\$
    – Nic Foster
    Feb 21, 2012 at 20:11

2 Answers 2

7
\$\begingroup\$

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 SpriteBatchyou 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).

\$\endgroup\$
0
\$\begingroup\$

I would imagine that creating a new batch for every drawable element of your game would cause a hit on performance because each call to Spritebatch.End() :

"Flushes the sprite batch and restores the device state to how it was before Begin was called." - MSDN.

But I have no proof to back up the performance claim.

As for which is preferable from a design standpoint, I'd say passing a SpriteBatch around from call to call. What is being drawn should have no idea how or where it's being drawn. What I mean by that is that when you create a SpriteBatch, you can apply effects to the SpriteBatch or redirect the SpriteBatch to write to a different render target for example. If you wanted to blur the whole screen, take a screenshot, or draw a split-screen, it would be way easier if you pass the appropriate SpriteBatch in.

\$\endgroup\$
2
  • \$\begingroup\$ "Flushes the sprite batch and restores the device state to how it was before Begin was called." Hmm this is not true. I just got done fixing bug in my GUI where DepthStencilState.None which is was set by SpriteBatch.Begin was not reset to DepthStencilState.Default which was the state before Begin was called. \$\endgroup\$ Feb 21, 2012 at 21:00
  • \$\begingroup\$ @ClassicThunder, If that's true, send Microsoft a note to either fix the bug, or update the doc. The link to MSDN was included in my original answer. \$\endgroup\$ Feb 21, 2012 at 22:07

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .