Helper class to batch together static sprites and boost performance.
MonoGame.StaticBatch
allow you to batch together multiple static sprites, to reduce significantly their drawing times (and drawing calls).
As an added bonus, it also add grid-based culling to skip sprites that are not currently visible in screen.
A test scene showed improvement from 10-300 FPS* without static batch to 6000+ FPS with static batch.
*10 FPS when drawing all scene, 300 when drawing only what's in screen.
Install via NuGet:
Install-Package MonoGame.StaticBatch
Or clone the repo and build manually.
The class will divide your scene into a grid of render targets, and will draw all your static sprites during the initialization (hence they need to be static).
When drawing the static batch, instead of drawing all the individual sprites (lots of draw calls!) it will only draw a small subset of the static textures grid.
The following screenshot illustrates a static batch with textures sized 512x512. There are also lots of sprites outside of screen, not rendered. As you can see with static batch we only need 9 draw calls!
When drawing lots of sprites, using the static batch boost performance significantly. More specifically, you enjoy the following benefits:
- Much less draw calls.
- Automatic grid-based culling of invisible pixels.
- Zero work on hidden pixels (pixels covered by other sprites).
MonoGame.StaticBatch
doesn't come without a price.
- Sprites should be static (updating is possible but less fun).
- Takes up more memory (depending on how many textures you end up using).
- Requires some build time, but usually unnoticeable if only done in init.
To use the static batch you need to add sprites to it, then build it (convert list of sprites into textures grid) and finally draw the batch.
For example:
// create the static batch with 512x512 textures
StaticBatch staticBatch = new StaticBatch(GraphicsDevice, new Point(512, 512));
// add some sprites to it
staticBatch.AddSprites(new StaticBatch.StaticSprite(texture, destRect, srcRect, ....));
// build the batch
staticBatch.Build(spriteBatch);
// draw the batch
staticBatch.Draw(spriteBatch);
If your scene exceed the screen boundaries, you'll want to cull out invisible textures. To do so, provide a viewport param when drawing the batch:
var viewport = GraphicsDevice.Viewport.Bounds;
staticBatch.Draw(spriteBatch, viewport);
To add a simple 2d camera you can use the viewport combined with offset:
var viewport = GraphicsDevice.Viewport.Bounds;
viewport.Location += cameraPos.ToPoint();
staticBatch.Draw(spriteBatch, viewport, offset: (-cameraPos).ToPoint());
For more complicated stuff you can just begin the spritebatch yourself, and provide whatever transformation matrix you'd want with it.
spriteBatch.Begin(...);
staticBatch.Draw(spriteBatch, viewport, beginAndEnd: false);
Just remember that if you want to use culling its your responsibility to match the viewport to the transformation matrix.
You can change the sampler state, blend state and Sort mode used when building and drawing the batch:
staticBatch.DefaultBlend = BlendState.AlphaBlend;
staticBatch.DefaultSamplerState = SamplerState.PointClamp;
staticBatch.DefaultSortMode = SpriteSortMode.BackToFront;
Be sure to set those values before building the static batch.
To add sprites to an existing, already built batch, you can simple call AddSprite() and Build() again, but without the clear flag. This will append new sprites on existing textures:
staticBatch.AddSprites(new StaticBatch.StaticSprite(texture, destRect, srcRect, ....));
staticBatch.Build(spriteBatch, clear: false);
To see a working example please check out the Tester code.
To watch a live demo of the static batch, clone this repo and Build & Run the project as Window / Console application.
What you'll see is a scene with tilemap, lots of bones, skeletons, and trees. By default, the scene will be drawn using the static batch. However, you can switch between drawing methods and compare performance and FPS.
Sprites used in demo are from the following sources:
- https://opengameart.org/content/skeleton-warrior-1
- https://opengameart.org/content/32x32-tilemap-grass-dungeon-floors-snow-water
- https://opengameart.org/content/lpc-all-seasons-apple-tree
MonoGame.StaticBatch
is distributed under the permissive MIT license and is absolutely free to use for any purpose.