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?

Jeff
  • 12,085
  • 12
  • 82
  • 152
  • 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) – BudBrot Dec 19 '13 at 08:07
  • I tried that, but it gives me weird results. Would you mind providing something for me to go by? – Jeff Dec 19 '13 at 08:18
  • I can't do it without jumping in tens (from 60 to 50, etc) – Jeff Dec 19 '13 at 08:25

5 Answers5

31

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

Aurumaker72
  • 33
  • 1
  • 7
craftworkgames
  • 9,437
  • 4
  • 41
  • 52
  • 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. – Jeff Dec 19 '13 at 11:54
  • 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. – craftworkgames May 06 '14 at 02:45
  • 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. – Pip Dec 02 '14 at 02:33
  • Queue is in System.Collections.Generic http://msdn.microsoft.com/en-us/library/7977ey2c%28v=vs.110%29.aspx – craftworkgames Dec 02 '14 at 02:37
  • 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. – vgru Dec 04 '14 at 20:22
  • @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). – craftworkgames Dec 05 '14 at 03:15
  • 1
    @craftworkgames: for the decaying average, the second snippet in [this answer I posted a while ago](http://stackoverflow.com/a/18637230/69809) 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](http://blogs.msdn.com/b/shawnhar/archive/2007/06/08/displaying-the-framerate.aspx). – vgru Dec 05 '14 at 09:14
  • The Update method of the FrameCounter cannot use a override modifier since it has no base class. – duesterdust Nov 25 '21 at 15:33
6

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.

Darius
  • 61
  • 1
  • 3
3

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);
Genzzry
  • 31
  • 4
1

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.

helb
  • 7,609
  • 8
  • 36
  • 58
  • The if-block is entered once, which is when I start the game, setting frameRate to 0. – Jeff Dec 19 '13 at 09:03
  • 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 Dec 19 '13 at 09:16
  • 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 Dec 19 '13 at 09:18
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?

Sinatr
  • 20,892
  • 15
  • 90
  • 319
  • 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 Dec 19 '13 at 08:28