Displaying the framerate

Originally posted to Shawn Hargreaves Blog on MSDN, Friday, June 8, 2007

I was about to write a different article, but then I thought about what would make a good example for my planned topic, and decided a framerate component would be a perfect testbed for it. So before I get started on my next real subject, I'm going to describe how to create a reusable framerate measurement component.

There are many possible ways to measure framerate. Some people directly measure the time taken to draw each individual frame, but the exact timings tend to fluctuate so it can be hard to get a sense of the overall performance that way. Others use various kinds of rolling average to smooth out the timing data over several frames. Personally I like to just count how many times Draw is called, then copy this counter value out into my framerate display once per second. Simple, accurate, and gives a nice steady result.

Enough talk. Here's the code:

    public class FrameRateCounter : DrawableGameComponent
    {
        ContentManager content;
        SpriteBatch spriteBatch;
        SpriteFont spriteFont;

        int frameRate = 0;
        int frameCounter = 0;
        TimeSpan elapsedTime = TimeSpan.Zero;


        public FrameRateCounter(Game game)
            : base(game)
        {
            content = new ContentManager(game.Services);
        }

        
        protected override void LoadGraphicsContent(bool loadAllContent)
        {
            if (loadAllContent)
            {
                spriteBatch = new SpriteBatch(GraphicsDevice);
                spriteFont = content.Load<SpriteFont>("Font");
            }
        }


        protected override void UnloadGraphicsContent(bool unloadAllContent)
        {
            if (unloadAllContent)
                content.Unload();
        }


        public override void Update(GameTime gameTime)
        {
            elapsedTime += gameTime.ElapsedGameTime;

            if (elapsedTime > TimeSpan.FromSeconds(1))
            {
                elapsedTime -= TimeSpan.FromSeconds(1);
                frameRate = frameCounter;
                frameCounter = 0;
            }
        }


        public override void Draw(GameTime gameTime)
        {
            frameCounter++;

            string fps = string.Format("fps: {0}", frameRate);

            spriteBatch.Begin();

            spriteBatch.DrawString(spriteFont, fps, new Vector2(33, 33), Color.Black);
            spriteBatch.DrawString(spriteFont, fps, new Vector2(32, 32), Color.White);
            
            spriteBatch.End();
        }
    }

To use this, register the component inside your Game constructor:

    Components.Add(new FrameRateCounter(this));

You will also need to add a Font.spritefont file. I got a little clever with mine:

   <?xml version="1.0" encoding="utf-8"?>
   <XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics">
     <Asset Type="Graphics:FontDescription">
      <FontName>Arial</FontName>
      <Size>14</Size>
      <Spacing>2</Spacing>
      <Style>Regular</Style>
      <CharacterRegions>
        <CharacterRegion><Start>f</Start><End>f</End></CharacterRegion>
        <CharacterRegion><Start>p</Start><End>p</End></CharacterRegion>
        <CharacterRegion><Start>s</Start><End>s</End></CharacterRegion>
        <CharacterRegion><Start>:</Start><End>:</End></CharacterRegion>
        <CharacterRegion><Start> </Start><End> </End></CharacterRegion>
        <CharacterRegion><Start>0</Start><End>9</End></CharacterRegion>
      </CharacterRegions>
     </Asset>
   </XnaContent>

See what I did there? Because my framerate counter is only ever going to display the numbers 0 through 9, plus an "fps:" prefix, I optimized the size of my font by specifying CharacterRegion elements to only bother including the characters I really need.

Blog index   -   Back to my homepage