XNA Quick Tip – XNA SpriteBatch and Garbage

The XNA SpriteBatch class maintains an internal array to hold sprites to be drawn between calls to Begin() and End() if a sort mode is used. This array will grow as necessary, however, the reallocation will inevitably result in extra garbage that will eventually need to be collected.  On the XBox 360 this is quite expensive and so in general it is recommended to limit garbage wherever possible.

If you know the upper bound on the number of sprites to be drawn in a single batch, you can force the draw before the game begins.  This is pretty easy to do, however to make it even easier when you have multiple SpriteBatch instances for different purposes I use the following simple wrapper class in Solaroids that has a new constructor that takes an extra argument for the expected number of active sprites:

/// <summary>
/// Wrapper around a SpriteBatch that pre-allocates internal sorting buffers by drawing a set of sprites thus forcing the buffers to be expanded.
/// </summary>
public class PreallocatingSpriteBatch : SpriteBatch
{
  /// <summary>
  /// Initializes a new instance of the <see cref="PreallocatingSpriteBatch"/> class.
  /// </summary>
  /// <param name="graphicsDevice">The graphics device.</param>
  /// <param name="expectedActiveSprites">The expected number of active sprites.</param>
  public PreallocatingSpriteBatch(GraphicsDevice graphicsDevice, int expectedActiveSprites)
    : base(graphicsDevice)
  {
    ForceBufferAllocation(expectedActiveSprites);
  }

  /// <summary>
  /// Forces the buffer allocation.
  /// </summary>
  /// <param name="expectedActiveSprites">The expected number of active sprites.</param>
  private void ForceBufferAllocation(int expectedActiveSprites)
  {
    Texture2D tex = new Texture2D(GraphicsDevice, 16, 16);

    Begin(SpriteSortMode.BackToFront, BlendState.AlphaBlend);

    for (int i = 0; i < expectedActiveSprites; i++)
    {
      Draw(tex, Vector2.Zero, Color.White);
    }

    End();
  }
}