XNA/MonoGame: Getting the Frames Per Second

21,873

Solution 1

Here's an FPS counter class I wrote a while ago. You should be able to just drop it in your code and use it as is..

public class FrameCounter
{
    public FrameCounter()
    {
    }

    public long TotalFrames { get; private set; }
    public float TotalSeconds { get; private set; }
    public float AverageFramesPerSecond { get; private set; }
    public float CurrentFramesPerSecond { get; private set; }

    public const int MAXIMUM_SAMPLES = 100;

    private Queue<float> _sampleBuffer = new Queue<float>();

    public override bool Update(float deltaTime)
    {
        CurrentFramesPerSecond = 1.0f / deltaTime;

        _sampleBuffer.Enqueue(CurrentFramesPerSecond);

        if (_sampleBuffer.Count > MAXIMUM_SAMPLES)
        {
            _sampleBuffer.Dequeue();
            AverageFramesPerSecond = _sampleBuffer.Average(i => i);
        } 
        else
        {
            AverageFramesPerSecond = CurrentFramesPerSecond;
        }

        TotalFrames++;
        TotalSeconds += deltaTime;
        return true;
    }
}

All you need to do is create a member variable in your main Game class..

    private FrameCounter _frameCounter = new FrameCounter();

And call the Update method in your Game's Draw method and draw the label however you like..

    protected override void Draw(GameTime gameTime)
    {
         var deltaTime = (float)gameTime.ElapsedGameTime.TotalSeconds;

         _frameCounter.Update(deltaTime);

         var fps = string.Format("FPS: {0}", _frameCounter.AverageFramesPerSecond);

         _spriteBatch.DrawString(_spriteFont, fps, new Vector2(1, 1), Color.Black);

        // other draw code here
    }

Enjoy! :)

Solution 2

You can get the current framerate at any given moment using this formula:

framerate = (1 / gameTime.ElapsedGameTime.TotalSeconds);

Both of the other methods presented below give you a modified framerate, intended to be smoother, and have fewer fluctuations in the return value

This one works by weighting all previous frametimes on a logarithmic scale. I didn't like the idea of using an average to get a metric on game performance as framedrops aren't represented well (or at all if you have a high average) and very low/very high framerates have vastly different levels of accuracy if they are running on the same average.

To solve this, I made a SmartFramerate class (terrible name, I know)

class SmartFramerate
{
    double currentFrametimes;
    double weight;
    int numerator;

    public double framerate
    {
        get
        {
            return (numerator / currentFrametimes);
        }
    }

    public SmartFramerate(int oldFrameWeight)
    {
        numerator = oldFrameWeight;
        weight = (double)oldFrameWeight / ((double)oldFrameWeight - 1d);
    }

    public void Update(double timeSinceLastFrame)
    {
        currentFrametimes = currentFrametimes / weight;
        currentFrametimes += timeSinceLastFrame;
    }
}

You set the weight when you create the variable: (a higher weight is more accurate to the instantaneous framerate, a lower weight is smoother. I find that 3-5 is a good balance)

SmartFramerate smartFPS = new SmartFramerate(5);

Call the Update method anywhere that will be run every frame:

smartFPS.Update(gameTime.ElapsedGameTime.TotalSeconds);

The current framerate may be accessed like so:

smartFPS.framerate

or printed like so:

debuginfo.Update("\n\nᴥ" + smartFPS.framerate.ToString("0000"), true);

(I'm putting it into a custom print class, so I apologize if the syntax looks funky)

However, if you wish to simply average a certain number of frames together, then this class is the most efficient way I have come up with to do so.

class SmoothFramerate
{
    int samples;
    int currentFrame;
    double[] frametimes;
    double currentFrametimes;

    public double framerate
    {
        get
        {
            return (samples / currentFrametimes);
        }
    }

    public SmoothFramerate(int Samples)
    {
        samples = Samples;
        currentFrame = 0;
        frametimes = new double[samples];
    }

    public void Update(double timeSinceLastFrame)
    {
        currentFrame++;
        if (currentFrame >= frametimes.Length) { currentFrame = 0; }

        currentFrametimes -= frametimes[currentFrame];
        frametimes[currentFrame] = timeSinceLastFrame;
        currentFrametimes += frametimes[currentFrame];
    }
}

To use it, simply initialize a SmoothFramerate variable where ever you wish to use it, passing the amount of frames you want averaged:

SmoothFramerate smoothFPS = new SmoothFramerate(1000);

Update, access, and print the current framerate exactly as you would using the SmartFramerate class above.

Thanks for reading, I hope this helps someone out.

Solution 3

Use a double to measure fps:

double frameRate = 0.0;

Modify the method Update as follows:

public override void Update(GameTime gameTime)
{        
    if(gameTime.ElapsedGameTime.TotalSeconds > 0.0)
    {
        frameRate = (double)frameCounter / gameTime.ElapsedGameTime.TotalSeconds;
    }
    frameCounter = 0;
}

I didn't test the code but you should get the idea.

Solution 4

A simple method that updates every Draw() would be:

frameRate = 1 / gameTime.ElapsedGameTime.TotalSeconds;

You could also drop that in the Update() method to see how often that fires too, if you wanted.

If you want to slow down how fast it updates...

frameRate += (((1 / gameTime.ElapsedGameTime.TotalSeconds) - frameRate) * 0.1);
Share:
21,873

Related videos on Youtube

Jeff
Author by

Jeff

I'm a software engineer at Taxfyle, making taxes suck less. I am proficient in JavaScript and C#. http://www.Twitter.com/Jeffijoe

Updated on July 01, 2022

Comments

  • Jeff
    Jeff almost 2 years

    I am trying to get the current FPS of my game, however I can only find methods that updates the FPS variable every second. E.g. https://github.com/CartBlanche/MonoGame-Samples/blob/master/Draw2D/FPSCounterComponent.cs and http://www.david-amador.com/2009/11/how-to-do-a-xna-fps-counter/

    Is there a way to have a continuously updating FPS label?

  • Jeff
    Jeff over 10 years
    I am explicitly trying to have my FPS counter update more frequently - one second is too long. I got it working if it simply updates every second - I am trying to have it update every, say, 10 milliseconds.
  • Jeff
    Jeff over 10 years
    The if-block is entered once, which is when I start the game, setting frameRate to 0.
  • Jeff
    Jeff over 10 years
    Sorry, still not there yet - I have written some code that will stress the drawing, and while debugging, I found that when frameCounter is 1, the FPS = ~60, but when frameCounter = 0, the FPS = 0.
  • Jeff
    Jeff over 10 years
    By moving everything into the Draw method, it goes from 60 to 30 to 20 to 15 to 12 whenever I add more drawing data (a lot of data)...
  • Jeff
    Jeff over 10 years
    Thank you, this works better than anything I have tried so far - it does not "feel" real-time though, but that is probably because it is averaging.
  • craftworkgames
    craftworkgames almost 10 years
    You're welcome. You can fiddle with the MAXIMUM_SAMPLES value or try the CurrentFramesPerSecond property instead. I didn't put a whole lot of effort into making it perfect. My goal was just to make something easily reusable.
  • Pip
    Pip over 9 years
    Where is the Queue collection coming from? I found one in System.Collections, but it is a non-generic type so it will not work the way intended here (with a float type). I don't need this for a project, just interested.
  • craftworkgames
    craftworkgames over 9 years
    Queue<T> is in System.Collections.Generic msdn.microsoft.com/en-us/library/7977ey2c%28v=vs.110%29.aspx
  • Groo
    Groo over 9 years
    Using a Queue for averaging stats like these is probably overkill. It's not a lot of memory and CPU, but you don't really need to keep 100 values in memory at all times, and recalculate the average on each frame update.
  • craftworkgames
    craftworkgames over 9 years
    @Groo fair point. I've never tried profiling this code, it was a quick implementation to solve the problem. It would be interesting to see how it could be improved (after profiling obviously).
  • Groo
    Groo over 9 years
    @craftworkgames: for the decaying average, the second snippet in this answer I posted a while ago would do the trick, but the most accurate way altogether would be to count the frames inside the Draw method like shown in this example by Shawn Hargreaves.
  • Talha Lodhi
    Talha Lodhi almost 3 years
    dunno why your answer is so low, this code 'simply works', and is exactly what i needed :)
  • duesterdust
    duesterdust over 2 years
    The Update method of the FrameCounter cannot use a override modifier since it has no base class.