XNA/MonoGame: Getting the Frames Per Second
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);
Related videos on Youtube
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, 2022Comments
-
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 over 10 yearsI 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 over 10 yearsThe if-block is entered once, which is when I start the game, setting frameRate to 0.
-
Jeff over 10 yearsSorry, 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 over 10 yearsBy 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 over 10 yearsThank 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 almost 10 yearsYou'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 over 9 yearsWhere 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 over 9 yearsQueue<T> is in System.Collections.Generic msdn.microsoft.com/en-us/library/7977ey2c%28v=vs.110%29.aspx
-
Groo over 9 yearsUsing 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 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 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 almost 3 yearsdunno why your answer is so low, this code 'simply works', and is exactly what i needed :)
-
duesterdust over 2 yearsThe Update method of the FrameCounter cannot use a override modifier since it has no base class.