Saturday, August 14, 2010

Fps - but not first person shooter

Alright, I have a camera and I can observe things in the world but an another thing would be useful as well: to see what kind of impact a new feature or modification has on performance.

What I need is the frame per seconds (fps) to be displayed.

I create a separate GameComponent for this task. The implementation is quite simple: it uses a SpriteBatch and SpriteFont instance to display text on screen with following logic:

  • render last set fps value (a class member) in every Draw() invocation
  • update fps value in every 0.5 sec based on 1 / (now [sec]  - last time Draw() was invoked [sec])

The fps value gets displayed flawlessly but there is one surprise. Do you notice anything one the picture?

2 triangles with disabled depth buffer
You are right: we can see parts of one triangle that should have been occluded by another triangle. This happened because the depth buffer (z-buffer) got disabled. 

This happens because SpriteBatch.DrawString(..) changes different render states of the graphics device e.g. cull mode, depth buffer, alpha blending. Which is ok and makes sense because, for instance, text needs to be displayed on top of "everything" thus depth buffer reading needs to be disabled.

To solve it I could either pass SaveStateMode.SaveState to SpriteBatch.Begin(..) to save and restore device state but I try to stick to my rule of thumb and prefer performance where possible. Besides it makes more sense to me to always set whatever render states needed before rendering something.

Render states are explicitly set (every time) before rendering triangles

One more thing. Microsoft.Xna.Framework.Game instances are initialized to call Draw(..) method not faster than the refresh rate of you screen (monitor). Usually it makes sense because, for instance, why render more than 60 frames per sec when your monitor won't update the screen more than 60 times per sec.

But in our case I want to use the fps counter as a performance indicator therefore I don't need vertical synchronization. To do this I disable vertical sync and the fixed time step between updates/draws.

graphics.SynchronizeWithVerticalRetrace = false;
IsFixedTimeStep = false;

Oops, but as one problem solved a new arises. Now I have around 1500 frames per sec and because it is so high it jumps a lot and too fast between values 800-1800. To make it more steady I change the counting logic following way:

  • render last set fps value in every Draw() invocation
  • update fps value in every 0.5 sec based on

    • total draws are counted between fps value updates
    • set fps value to (total draws counted) / (now [sec] - last time fps value was updated [sec])

That's it. Check out the video to see it in action. It is not very exciting, though.
Note that 480p recording has some impact on frame rate. :(

No comments:

Post a Comment