XNA/MonoGame: Getting the Frames Per Second
Asked Answered
I

5

13

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?

Invest answered 19/12, 2013 at 7:54 Comment(3)
Fps = frames per second, anyways: just decrese the ticks of the timer (f.e TimeSpan.FromMilliseconds) and project it to a second (so if you get the fps every 0,5 seconds multiply it by 2)Cochleate
I tried that, but it gives me weird results. Would you mind providing something for me to go by?Invest
I can't do it without jumping in tens (from 60 to 50, etc)Invest
W
32

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 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 MaximumSamples = 100;

    private Queue<float> _sampleBuffer = new();

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

        _sampleBuffer.Enqueue(CurrentFramesPerSecond);

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

        TotalFrames++;
        TotalSeconds += deltaTime;
    }
}

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! :)

Wept answered 19/12, 2013 at 10:59 Comment(8)
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.Invest
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.Wept
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.Hercegovina
Queue<T> is in System.Collections.Generic msdn.microsoft.com/en-us/library/7977ey2c%28v=vs.110%29.aspxWept
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.Adlar
@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).Wept
@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.Adlar
The Update method of the FrameCounter cannot use a override modifier since it has no base class.Schmooze
C
8

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.

Celestial answered 22/6, 2017 at 2:16 Comment(0)
T
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);
Transportation answered 31/7, 2016 at 0:2 Comment(1)
dunno why your answer is so low, this code 'simply works', and is exactly what i needed :)Darryldarryn
B
0

You probably trying to update FPS in every draw operation. This will be very unstable value, as sometimes you need more, sometimes less. To make value more stable - take average out of many values.

The easiest way to count FPS is probably count drawings. And have timer-bases function what take this value lets say every second, calculate new fps and clear counter. Make this timer longer (to example 2 sec), or simply take last 10 fps values and display average.

In some games I seen they also count maximum time needed to redraw the scene. This could be an interesting value too. For this you can measure how long draw function takes: register the time at start of it and at the end, difference would be the time.

Take note, if you enable sync, you delay draw operation until next sync, perhaps this is the reason of strange results?

Bitolj answered 19/12, 2013 at 8:26 Comment(1)
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.Invest
H
0

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.

Hyaloplasm answered 19/12, 2013 at 8:42 Comment(3)
The if-block is entered once, which is when I start the game, setting frameRate to 0.Invest
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.Invest
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)...Invest

© 2022 - 2024 — McMap. All rights reserved.