Draw Optimization

Currently I have optimized my drawing by

  1. Combining Tile-based maps to single textures with Rendertargets, done only once
  2. Skipping Objects that aren’t visible on screen

Still, according to Visual Studio Performance Tools, Drawing uses lots of resources, so:

  • Which SpriteBatch.Draw variation is fastest, or are they all “slow”?
  • I have multiple SpriteBatch.Begins to have different effects on screen. Is this a problem?
  • What are the best standard ways to optimize drawing?
1 Like

Are you rendering the whole texture? If you are try only drawing the part that is visible on screen by using a Draw overload on SpriteBatch that takes a source rectangle.

They’re all pretty fast actually :stuck_out_tongue:
If you’re on develop try specifying only the parameters you really need. A lot of optimization has been done (and still is being done) to do only necessary computation. If you’re not on develop but you want some performance improvement try switching to develop.

In general you should try to minimize the number of render state changes and minimize number of gpu draw calls, so maximize the batch size. When using SpriteBatch that means

  1. Don’t use Immediate mode.
  2. Try to minimize Begin/End calls.
  3. Sort by textures if you have a lot of duplicates.

And last but not least, the thing that always comes up when talking about optimization: don’t worry about it unless you need to!

3 Likes

Yes, I’m rendering whole texture (or all Tilelayers) with SpriteBatch.Draw(TileLayer, Vector2.Zero, Color.White);
I hadn’t thought about using Source Rectangle.

I’m still on 3.5, but moving to 3.6 before finishing my game.

I use Deferred mode a lot, is it ok?
Currently there are 8 SpriteBatch.Begin - Ends to draw the game:


//Game Drawing

SpriteBatch.Begin(samplerState: SamplerState.PointClamp, blendState: SpecialBlend);
//Creates Special Effect with RenderTarget if needed, otherwise not called
SpriteBatch.End();

SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.PointWrap, null, null, null, ScreenMatrix);
//Background Tile Layers
SpriteBatch.End();

SpriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.NonPremultiplied, SamplerState.PointClamp, null, null, null, ScreenMatrix);
//Game Objects
SpriteBatch.End();

SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.PointWrap, null, null, null, ScreenMatrix);
//Foreground Tile Layers
SpriteBatch.End();

SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.PointClamp, null, null, null, ScreenMatrix);
//Normal Effects & Particles
SpriteBatch.End();

SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, SamplerState.PointClamp, null, null, null, ScreenMatrix);
//Other Effects & Particles
SpriteBatch.End();

SpriteBatch.Begin(transformMatrix: ScreenMatrix);
//Draw the Special Effect if needed
SpriteBatch.End();

SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.PointClamp, null, null, null, ScreenMatrix);
//UI
SpriteBatch.End();

As you can see, only changes in SpriteBatch.Begins are in BlendState and/or in SamplerState, excluding “Special Effect”.

To be fair, on my 5820K / 980Ti, if I remove the FPS limit, the game runs in debug Map around 2000-2300FPS, but goes down to 500FPS with thousands of objects. In normal Map FPS is around 3200-3500 :sweat_smile:

My game have 500+ drawcalls, which runs really slow on android.
I really have to reduce the SpriteBatch.Begin and SpriteBatch.End calls.

I’d say anything less than 15 Begin-End pairs is very good if you have a lot of fx and states and deferred is what I usually prefer unless I need a sort - it’s all pretty optimized actually - and Draw count isn’t something you need to worry about – most of the actual draw work and state stuff happens in the End() call [unless that has changed]. If you end up using dozens of fx, you can also use draw with a color to signal to your combined-shader which shading function to use and discard the vertex color if not needed but I’d make sure to test shaders in “baby-steps” as you combine them. I do a lot of unnecessary optimization so I feel comfortable that I’m free to add colossal stuff and more fx later (but I try to do so in a way that will reduce complexity or confusion rather than increase it if possible). ;p
As Jjagg mentioned - source-rects from sprite-sheets and batch that kind of drawing together unless layers or fx don’t allow it to be possible in some situations. Watch out for cases where you accidentally render your scene-tiles multiple times (or entire world) - only process the span that’s close to visibility ie: (player_closest_tile_X-20 to player_closest_tile_X+20) as nested loop in same sort of thing for Y span… for stuff that’s a bit off-screen it shouldn’t hurt.