Displaying the framerate


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.


Comments (29)

  1. Kurzu says:

    Shawn,

    I’m just a little worried about this code line:

    elapsedTime += gameTime.ElapsedGameTime;

    …because when the framerate is high, it’s not going to be very accurate, is it? Personally I’d use sth like:

    if (gameTime.TotalGameTime – LastFpsUpdateTime > TimeSpan.FromSeconds(1))

    {

       LastFpsUpdateTime = gameTime.TotalGameTime;

       frameRate = frameCounter;

       frameCounter = 0;

    }

    But there comes the second catch.. Both approaches are a little inaccurate:) Both got the scheme: if (elapsed>1 second) elapsed = 0, and while elapsed>1 second there is that little time above this 1 second we loose. I know I’m getting overzealous, but there are so many cool catches that make coding so much fun:) So… to get rid of this problem, we could replace:

    LastFpsUpdateTime = gameTime.TotalGameTime;

    with:

    LastFpsUpdateTime -= TimeSpan.FromSeconds(1);

    if (gameTime.TotalGameTime – LastFpsUpdateTime > TimeSpan.FromSeconds(1))

       LastFpsUpdateTime = gameTime.TotalGameTime;

    The last condition should handle cases where we got fps<1.

    Now this is super steady framerate counter! Ok.. you got me, there are case when this can get pretty inaccurate too since in the calculation we took for granted that exactly 1 second has passed.. But who cares anyway:)

  2. ShawnHargreaves says:

    I don’t understand what you think would be inaccurate about the version I posted?

    Notice that I’m not resetting the elapsed time to zero, just subtracting one second from it. This will never loose time.

    Note also that this line:

       elapsedTime += gameTime.ElapsedGameTime;

    is using TimeSpan objects, which are accurate down to a very fine tick resolution.

  3. Mykres Space says:

    Shawn Hargreaves has just posted a small code Game Component that will display the Framerate for you

  4. Mykre says:

    Looks good, I have now added this to my Game Framework Project, but I did add the ability to change the output of the component so you have the choice of Debug, Windows Title, or Game Screen.

    I have also Changed the Draw order so that the component will always be drawn last.

    http://www.virtualrealm.com.au/blogs/mykre/archive/2007/06/08/xna-reusable-fps-counter-and-spritefonts.aspx

  5. Jerry Hong says:

     Your article is so good, thanks again.

  6. Kurzu says:

    Shawn,

    You tell gameTime.ElapsedGameTime is so precise, we can sum up each one to get an accurate game time? I didn’t know this, thought it just has a 1 ms accuracy, sorry. There still stands that minor problem when some frame updates take more than 1 second. But it’s not a big deal, specially when the fact you get <1 fps bothers you:) But it’s a cool method anyway;)

  7. Mykres Space says:

    About a month ago I was looking for a small project to work on, as I needed something to fill in some

  8. GrantK says:

    I might be being stupid here but can’t you get the framerate using ElapsedRealTime? ElapsedRealTime is the amount of elapsed real time since the last frame.  Surely 1 / gameTime.ElapsedRealTime.TotalSeconds will therefore give the current framerate.

  9. ShawnHargreaves says:

    > Surely 1 / gameTime.ElapsedRealTime.TotalSeconds

    > will therefore give the current framerate.

    That will tell you how long it was since the previous call to Update, but that is not the same thing as your framerate!

    a) If the game is dropping frames, Update will be called more frequently in order to catch up. You want to time the number of actual draws that are taking place, not just these extra catch-up logic frames.

    b) The time for a single Update can fluctuate widely, so the figure you get out of that will be too flickery to be easily readable.

  10. GrantK says:

    I see your point about the result being too flickery to read but as far as I can tell ElapsedRealTime gives the amount of time since Draw was last called not the time since Update was called.  ElapsedGameTime gives the time since the Update was called.  If you set the TargetElapsedTime to a high value you can easily see the difference between the two time values

  11. barret232hxc says:

    I am just curious about the content loader. I have not been able to load the spritefont with the content manager.

    I first just used my spritefont that I was using to display the game score but it kept arguing that the file was not found but it clearly finds it find in my main game file.

    I then tried making a seperate sprite font. I’m using the method you mentioned in a newer blog where you add

    #if PROFILE

    this.IsFixedTimeStep=false;

    graphics.SynchronizeWithVerticalRetrace = false;

    #endif

    I’m not sure if somehow when the

           protected override void LoadContent()

           {

                   base.LoadContent();

    spriteBatch = new SpriteBatch(GraphicsDevice);

    spriteFont  = content.Load<SpriteFont>("Content/SpriteFont2");

           }

    gets called that somehow it doesn’t know the file structure yet? Is there a trick that I’m missing. I assumed using the same way I load a spritefont in my main game would be the same.

    any help is greatly appreciated

  12. barret232hxc says:

    update.

    apparently when building in release and debug it works fine only when I select the special profile mode that you mentioned in a newer blog that I get the error that it can’t find the file path. I’m guessing there is something special I need to do

  13. Shawn Hargreaves has just posted a small code Game Component that will display the Framerate for you application using the spritefont. One of the good things about this is that Shawn shows you a simple method of reducing the size of the font files by

  14. About a month ago I was looking for a small project to work on, as I needed something to fill in some time. When on the Creators Forums a user posted a question about the Starter Kits and what they wanted to see next. What was talked about was a starting

  15. BenS1 says:

    In my code I just use:

           protected override void Draw(GameTime gameTime)

           {

               // Clear the screen

               m_Graphics.GraphicsDevice.Clear(Color.CornflowerBlue);

               // Other stuff here…..

               // Draw the text

               m_SpriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.None);

               // Debug output

               if (m_ShowDebug)

               {

                   int fps = (int)Math.Round(1.0 / gameTime.ElapsedGameTime.TotalSeconds);

                   m_SpriteBatch.DrawString(m_ArialFont, "FPS: " + fps, new Vector2(500, 50), Color.Yellow);

               }

               m_SpriteBatch.End();

               // Done

               base.Draw(gameTime);

           }

    So the real meat is just 2 lines:

    To calc the framerate:

    int fps = (int)Math.Round(1.0 / gameTime.ElapsedGameTime.TotalSeconds);

    And to display it:

    m_SpriteBatch.DrawString(m_ArialFont, "FPS: " + fps, new Vector2(500, 50), Color.Yellow);

    Thanks

    Ben

  16. bertino12 says:

    I just started messing with XNA and the tutorial works fine, but I do get 2 warnings. Im not sure I understand this error and how can I make it go away? Thanks!

    Warning 1 Member ‘FrameRateCounter.LoadGraphicsContent(bool)’ overrides obsolete member ‘Microsoft.Xna.Framework.DrawableGameComponent.LoadGraphicsContent(bool)’. Add the Obsolete attribute to ‘FrameRateCounter.LoadGraphicsContent(bool)’. C:Documents and SettingsasdfMy DocumentsVisual Studio 2005ProjectsWindowsGame1WindowsGame1Class1.cs 32 29 WindowsGame1

    Warning 2 Member ‘FrameRateCounter.UnloadGraphicsContent(bool)’ overrides obsolete member ‘Microsoft.Xna.Framework.DrawableGameComponent.UnloadGraphicsContent(bool)’. Add the Obsolete attribute to ‘FrameRateCounter.UnloadGraphicsContent(bool)’. C:Documents and SettingsasdfMy DocumentsVisual Studio 2005ProjectsWindowsGame1WindowsGame1Class1.cs 42 29 WindowsGame1

  17. aaron says:

    the warnings mean exactly what they say, LoadGraphicsContent is now obsolete.

    replace the two methods with:

     protected override void LoadContent()

           {

               if (loadAllContent)

               {

                   spriteBatch = new SpriteBatch(GraphicsDevice);

                   //spriteFont = _content.Load<SpriteFont>(@"Fontsgamefont");

                   //spriteFont = content.Load<SpriteFont>(@"Fontsgamefont");

                   spriteFont = content.Load<SpriteFont>(@"Fontsgamefont");

               }

           }

           protected override void UnloadContent()

           {

               if (unloadAllContent)

                   content.Unload();

           }

  18. Sjaak says:

    Hai,

    I try to use your framecounter but the problem is the spritefont-file. Error loading "Font". File not found.

    I’m sure I named everthing alright. But the strange thing is dat the spritefont extension isn’t recognized. There’s no icon.

    I use XNA 3.1 and VS2008.

    Is this known problem?

    kid regards

    Sjaak

  19. Marc says:

    Hi,

    I had the same problem as Sjaak, and I could get the loading work by changing :

    public FrameRateCounter(Game game)

               : base(game)

           {

               content = new ContentManager(game.Services);

           }

    to :

    public FrameRateCounter(Game game)

       : base(game)

    {

      content = Game.Content;

    }

    I don't know if it is a bad practice, but it works.

    Best regards,

    Marc

  20. Piro says:

    Hi,

    Nice little frame rate counter.

    I have used this in a tutorial i followed which uses 3D in the Game class. Using this class however made the 3D drawing go wrong, some triangles that were behind others were visible. I think this is because using a SpriteBatch only works in 2D (screen coordinates) and therefore turns off the depthbuffer in your graphics device.

    I use XNA 3.1 and VS2008 by the way, don't know if the line of code at the end of the post is possible in older versions of XNA.

    I worked around this problem by adding this line at the very end of the FrameRateCounter Draw method:

       Game.GraphicsDevice.RenderState.DepthBufferEnable = true;

  21. Ian Mallett says:

    Seems like my framerate is always capped at 60 f/s.  I disabled vSync (I think):

    _graphics_device_manager.PreferredBackBufferWidth = 1024;

    _graphics_device_manager.PreferredBackBufferHeight = 600;

    _graphics_device_manager.IsFullScreen = false;

    _graphics_device_manager.SynchronizeWithVerticalRetrace = false;

    _graphics_device_manager.PreferredDepthStencilFormat = DepthFormat.Depth24Stencil8;

    _graphics_device_manager.ApplyChanges();

    But it remains at 60.  Ideas?

  22. Serenissima says:

    @Ian Mallet

    try add this before ApplyChanges

    IsFixedTimeStep = false;

    @Shawn

    btw great Frame Counter Thank You. Just ported it to XNA 4 and it works great

  23. Darknuke says:

    Can someone post their ported 4.0 version? I can't seem to port this right…

  24. Slay_mithos says:

    Thank you for the fast way to have a frame rate, it will help in finding when the game starts slowing and on what basis.

    As for 4.0 version, the posted versions seems to work just fine by just copying the wanted part in my scene (sort of custom drawable element).

  25. Klex says:

    This working in XNA 4.0:

    http://pastebin.com/pziMpNwf

    new FrameRateCounter(this, new Vector2(25, 25), Color.White, Color.Black);

    Thanks Shawn.

  26. owen barker says:

    i have an issue with your sample. I load it properly but when i try to load the font i get an error the font doesnt exist BUT but .spritefont is at the top root directory of my content folder.

  27. ShawnHargreaves says:

    Hi Owen,

    This article explains how to investigate such problems: blogs.msdn.com/…/why-does-contentmanager-load-or-titlecontainer-openstream-say-file-not-found.aspx

  28. Mordi says:

    Hi, this is a good and understandable example. However, I can only seem to get it to max out at 50 frames per second. I have a pretty decent computer, which should be able to run a blank window at sixty frames per second, but still I get 50. Could this be an issue out of my hands, or is there something wrong with my code?

  29. ShawnHargreaves says:

    Mordi – perhaps your monitor is running at 50hz?