Measuring Frametime Revisited


As I mentioned in a previous post, the number of milliseconds needed to render a frame is THE metric for measuring your game’s performance. Since then, I’ve tweaked the code I introduced in that article to further help me detect performance problems.

First of all, I packaged my frametime display into a DrawableGameComponent to make it trivially easy to include in any XNA project. If you’re just interested in the code, you can jump to the end of this post and copy it from there.

While it’s not related to frametime per se, I also display allocated memory along with frametime. Alongside CPU/GPU performance, I always want to stay aware of how much memory I’m using. There is a caveat here, however. I use String and SpriteFont to display frametime / memory usage which, in my naive implementation, results in memory allocations every frame. This means that memory usage goes up and regularly triggers garbage collection as a result of frametime display itself. This is not ideal behavior for a debug tool and I hope to fix this in the future.

Finally, I added a bit of logic that temporarily changes the color of the display text if frametime has gone over a particular threshold. This allows me to detect spikes in CPU/GPU performance that could lead to visual hitches in the game. Basically, as long as my frametime display doesn’t change from a peaceful green to an ugly magenta, I can be reasonably sure that my framerate is steady.

I find this sort of simple performance tracking code crucial for building a decent game. Hopefully, it will prove useful to other game devs as well. As usual, code follows:

    /// <summary>
    /// This is a game component that implements IUpdateable.
    /// </summary>
    public class FrametimeCounterComponent : Microsoft.Xna.Framework.DrawableGameComponent
    {
        Stopwatch FrametimeStopwatch;
        
        SpriteFont FrametimeDisplayFont;
        SpriteBatch FrametimeDisplayBatch;

        public Vector2 FrametimeDisplayPosition;
        public Color FrametimeDisplayColor;


        const double FRAMETIME_DANGER_THRESHOLD = 1000.0f / 60.0f;      //If averaged frametime goes over this threshold...
        const float DANGER_COLOR_DISPLAY_SECONDS = 2.0f;                //... the frametime display will change color for this many seconds.
        
        float DangerTimer;
        public Color FrametimeDangerDisplayColor;
        float AveragedFrameTime;

        public bool ShowAveragedFrametime;

        public FrametimeCounterComponent(Game game)
            : base(game)
        {
            ShowAveragedFrametime = true;
            FrametimeDisplayPosition = Vector2.One * 10.0f;
            FrametimeDisplayColor = Color.Green;
            FrametimeDangerDisplayColor = Color.Magenta;
        }

        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public override void Initialize()
        {
            FrametimeStopwatch = new Stopwatch();
            FrametimeStopwatch.Start();

            base.Initialize();
        }

        protected override void LoadContent()
        {
            FrametimeDisplayBatch = new SpriteBatch(Game.GraphicsDevice);
            FrametimeDisplayFont = Game.Content.Load<SpriteFont>("Debug/Fonts/Default");
            base.LoadContent();
        }

        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            base.Update(gameTime);
        }

        public override void Draw(GameTime gameTime)
        {
            TimeSpan frameTime = FrametimeStopwatch.Elapsed;
            FrametimeStopwatch.Reset();
            FrametimeStopwatch.Start();
            
            long totalMemKB = GC.GetTotalMemory(false) / 1024;

            AveragedFrameTime = AveragedFrameTime * 0.9f + (float)frameTime.TotalMilliseconds * 0.1f;
            DangerTimer = MathHelper.Clamp(DangerTimer - (float)gameTime.ElapsedGameTime.TotalSeconds, 0.0f, DANGER_COLOR_DISPLAY_SECONDS);
            if (AveragedFrameTime > FRAMETIME_DANGER_THRESHOLD)
            {
                DangerTimer = DANGER_COLOR_DISPLAY_SECONDS;
            }

            FrametimeDisplayBatch.Begin();
            FrametimeDisplayBatch.DrawString(FrametimeDisplayFont, "Frame Time: " + (ShowAveragedFrametime ? AveragedFrameTime : frameTime.TotalMilliseconds) + "ms", FrametimeDisplayPosition, DangerTimer > 0.0f ? FrametimeDangerDisplayColor : FrametimeDisplayColor);
            FrametimeDisplayBatch.DrawString(FrametimeDisplayFont, "Total Memory: " + totalMemKB + "KB", FrametimeDisplayPosition + Vector2.UnitY * FrametimeDisplayFont.LineSpacing, FrametimeDisplayColor);
            FrametimeDisplayBatch.End();

            base.Draw(gameTime);
        }
    }
Share this Article:
  • Digg
  • StumbleUpon
  • del.icio.us
  • Facebook
  • Yahoo! Buzz
  • Twitter
  • Google Bookmarks
  • Print

One thought on “Measuring Frametime Revisited

  1. Pingback: Getting Cute with Performance Metrics (and Reducing Memory Usage too!) | Game Dev Without a Cause

Comments are closed.