How do you calculate framerate in the form of a floating point?

I want to display the number of logic updates and rendered frames per second, both counters in floating point form. The logic is updated at a fixed interval (fixed number of steps per second), frames are rendered with or without VSync. Additionally, I want the status of these counters to be updated once or several times per second, depending on the settings (this may change at runtime).

My current solutions turned out to be unsatisfactory, so I need your advice. How do you calculate it? Please take into account the constant number of steps per second (currently 60ups) and any VSync state.

There are probably a dozen different ways.
Personally I think I would increment “size_t frameCount” every frame, and only update the output every 20-30 frames. Make sure you cast variables to floating point types at calculation time.

frameCount ++;
if(!(frameCount % 20))
{
    size_t thisTick = SDL_GetTicks();
   //  I wrote this in-editor so it's not tested.
    double fps = ((double)frameCount * 1000.0f) / ((double)(thisTick - lastTick)); 
    lastTick = thisTick;
    frameCount = 0;
   //update display of fps, I assume you know and use SDL_TTF?
}

With C++ you have std::to_string to get the double value into a string. Otherwise I think there’s the C function snprintf()

Another option is to instead use SDL_AddTimer to update the fps based on a time interval.

One of the silly things about monitoring FPS is that it slightly decreases your FPS doing the calculations and output. So if you just didn’t worry about FPS, you’d have a slightly faster game. However knowing when and where you might have reduced frame rates is critical information for a developer.

Oh, I almost forgot, here’s the lazyFoo Tutorial.

I currently have a much more sophisticated solution than you suggest, and I’m still not entirely happy with it. Maybe I’m picky or I don’t understand something. :wink:

I can’t just count frames because the more often I update the counter to display on screen, the less precision I will get. I think that it is better to calculate fps based on an array of samples and the accumulator, so that the result is sensibly averaged and has high precision.

I use sampling and accumulator for samples to calculate the time it takes to perform individual logic updates and the rendering (without flipping). I think that the topic of framerate should also be approached in this way.

I’m writing code in Free Pascal. But it doesn’t matter, no matter what language the examples are in (or even pseudo-language), I can translate them into Pascal without any problem. This is more about the algorithm itself, not the implementation, so feel free to use any syntax to illustrate your point of view. :wink:

This article shows how to refresh the counter every second, and the more often you refresh the counter, the more precision it loses.

The method I have above gives you the average over the previous 20 frames (sorry I did a lot of editing, I’m worried you saw it before this iteration)

So are you wanting to sample the frame-rate at a single frame, but only at predetermined intervals?
fps = 1000.0f / ((double)(SDL_GetTicks() - ticksAtFrameStart));

SDL3 now has a SDL_GetTicksNS() function that deals in the nano-second range, are you using SDL 2 or 3?

I haven’t used Pascal, so I’m not sure what limitations on precision might exist there…

I would like the framerate counter to be refreshed at fixed intervals, e.g. from once a second to ten times a second. By default it will be once per second, but I would like the player or me to be able to increase this frequency at runtime.

SDL2 has SDL_GetPerformanceCounter, which does the same thing. I use it e.g. to calculate how long a pure logic update takes — the result is nanosecond precision (actually down to 100-nanosecond ticks, because that’s what my PC supports), finally multiplied to get a high precision floating point number of milliseconds.

The framerate counter doesn’t have to be that precise, so it can be based on the number of milliseconds, but I would like it to be floating point and with reasonable precision.


Currently I have something like this to refresh updaterate and framerate counters:

// Calculate how much time has elapsed since the previous update of the frame rates.
TimeFramerateCurrent := Game_TimeGetTimeMilliseconds();
TimeFramerateElapsed := TimeFramerateCurrent - TimeFramerateLast;

// Update frame rate counters only if at least a second has elapsed.
if TimeFramerateElapsed >= 1000 then
begin
  // Calculate the average number of updates and rendered frames in the last second.
  UpdateNum := 1000.0 / TimeFramerateElapsed * InSecondUpdateNum;
  RenderNum := 1000.0 / TimeFramerateElapsed * InSecondRenderNum;

  // Set the time of the last update to the current time (more in one second, less in another).
  TimeFramerateLast := TimeFramerateCurrent;

  // Reset the counters and start counting activities again all the next second.
  InSecondUpdateNum := 0;
  InSecondRenderNum := 0;
end;

The result with VSync disabled is as follows:

updates: 21.93 | renders: 19.25
updates: 60.00 | renders: 60.00
updates: 60.00 | renders: 60.00
updates: 60.00 | renders: 60.00
updates: 60.00 | renders: 60.00
updates: 60.00 | renders: 60.00
updates: 60.00 | renders: 60.00

Excellent precision. I’m using FRAPS to check if my calculations are correct — it also shows 60 frames per second in the game window (but it uses integer counter).

Test with VSync on looks like this:

updates: 60.10 | renders: 59.11
updates: 60.16 | renders: 59.17
updates: 60.10 | renders: 59.11
updates: 60.16 | renders: 59.17
updates: 59.94 | renders: 58.94
updates: 59.94 | renders: 58.94
updates: 60.22 | renders: 59.23
updates: 60.10 | renders: 59.11
updates: 59.23 | renders: 59.23
updates: 60.22 | renders: 59.23
updates: 60.28 | renders: 59.29
updates: 60.00 | renders: 59.00
updates: 59.94 | renders: 58.94
updates: 60.46 | renders: 59.46
updates: 59.23 | renders: 59.23
updates: 60.10 | renders: 59.11

Looks very good — since my display apparently doesn’t have a perfect 60Hz refresh rate, in some seconds the logic updates 59 times, and some seconds it does 61 updates to catch up with what VSync loses — so sometimes it’s a little less than 60, and sometimes it’s a little more.

FRAPS seems to count frames in a special way, because it is able to update the counter (the one displayed in the game window) every frame — the value of the counter in the window increases smoothly, from 0 to 60, changing its value every frame. I’m curious how it does it.


What I have currently works quite sensibly, but I’m not very good at modifying the code into one that would maintain the current precision, but would be able to refresh the counter more often than once a second.

This increasing value from 0 to 60 actually gives a pretty good clue, they are not giving an at-the-moment frame-rate, but actually a moving average over time.
Count the amount of time that it takes to initially reach a stable value and that is likely their sample size.

Basically save a record of about 30 to 60 or so of your most recent fps calculations. Add all the samples together take the average and output the result.
Next frame: Add the newest fps sample, remove the oldest, report the average… And repeat.

This gives the appearance of a smoothly running machine even though there are peaks and dips over that time range. It gives the user a sense of security, and it is still “accurate enough” to what the performance of the program maintains per second.

I do think this is an excellent idea to program an FPS counter like this for your users so that they don’t feel like they have to question every single frame, why did that drop, what brought it back up, etc, but your original counter is more accurate from a developer standpoint to what is happening in the current frame.

Yeah, I already use this approach to calculate something else, so it shouldn’t be difficult to use it for framerate as well. However, I use eight samples to see any spikes. More than eight smoothed out the score too much. I’ll try this approach, I already have an idea of how it’s supposed to work.

I also use this for debugging. I need more precision than integer, but not too much. Basing on millisecond time and a few samples should be enough to slightly smooth the result. In release, I can always round the result to one decimal place or even to a whole integer, and use higher precision only for debugging.

I give up on calculating framerate more than once per second — it doesn’t make any sense. The very assumption of measuring an event multiple times for the purpose of only one measurement seems to be fundamentally wrong.

Especially since in my engine the measurement does not concern the number of iterations of the main loop, but the number of calls to the function updating the logic and rendering the frame. Updating game logic can be done multiple times within one iteration of the main loop, and the frame will only be rendered if the logic has been updated in the same iteration of the main loop.

Well, I’ll leave the update of the updaterate and framerate counters as they are, once per second, because it works great and provides reasonable precision, good for debugging and for players. However, refreshing counters containing the average time of a single update and rendering of a single frame will be subject to sampling, because there is always at least one sample per counter refresh. In fact, there is a minimum of 15 samples (refresh four times per second) and a maximum of 60 (refresh once per second).

Thanks for the discussion. :wink:

I’m back! :wink:

I finally came up with an idea and figured out how to implement it. It’s simpler than I expected. I implemented a test solution and it works perfectly, the framerate counters can be refreshed more often than once per second, and changing the framerate allows you to display changes smoothly, just like FRAPS does. Below is a description of how to implement it.

Samples as timestamps

If a given counter is to count the frequency of performing certain activities per second (e.g. logic updates, renders, etc.), but is to be refreshed more often than once per second, then they cannot be counted in the classic way, i.e. adding 1 to some accumulator. This is because if you multiply the accumulated number to get the overall number of activities per second, the precision of the result will be very poor.

Instead, you should collect timestamps, retrieved just after performing a given action. These timestamps should be added to a list/queue so as to accumulate them over an interval equal or greater than a second. Timestamps can be milliseconds (taken using the SDL_GetTicks64 function), or in the form of hardware counter ticks, if we want the highest precision (the SDL_GetPerformanceCounter function is used for this).

Millisecond timestamps are sufficient to create typical update counters and renders per second.

Calculating the result

Having filled the list with timestamps, you now need to calculate the final result from them. If the list is empty or contains only one item, the result is 0. This should be checked first to avoid dividing by 0 (more on this in a moment).

If there are more items, you need to get the value of the first and last item of the list and then calculate their difference — last - first. If the result is less than 1000, it means that not enough timestamps have been collected yet (the game runs for less than a second), so the result is simply the number of items in the list.

However, if the difference between the last and the first item is equal to or greater than 1000, the game runs for at least a second and additional calculations must be performed. You should remove all timestamps that are too old, i.e. those at the beginning of the list. Enough of them should be removed so that after deletion, the difference between the last and the first item on the list is not less than 1000. Finally, the number of list items is our framerate, in integer form.

To calculate framerate in floating-point form, you need to take into account not only the number of items, but also the period of time — and this period is the difference between the last and first timestamp of the list. Pseudocode calculating framerate in floating point form:

framerate = 1000.0 / (timestamp_last - timestamp_first) * timestamps_list.count;

The result will be an integer only if the difference of the last and first timestamp is perfectly 1000 — otherwise the result will be integer, e.g. 59.7. For debugging, it may be useful to round it to, for example, two decimal places, while for the player the result can be rounded to one decimal place or even to a whole integer.

Summary

The above allows you to calculate the number of actions performed per second, more often than once per second. In this way, you can display, for example, a framerate counter that will refresh its value, e.g. 5 times per second, and it will actually be possible to obtain 5 different values per second. If the framerate drops sharply (e.g. from 60fps to 20fps), the counter will show 5 different values in one second, gradually decreasing from ~60 to ~20.

Thanks to the solution proposed above, it is possible to refresh the framerate counter even in every frame of the game and this is the highest possible refresh rate of the counter. The lowest refresh rate is not specified — it could be once per 250ms, once per second, but could also be once per minute. However, it must be taken into account that the less often the counter is refreshed, the more timestamps will be added to the sample list, so the more memory the list will take up and the longer it will take to remove too old samples.

In my engine, I have specified that the range of available frequencies is from once per second (slowest) to ten times per second (fastest). More often it doesn’t make sense, it won’t be practical.

If I find time, I will try to provide complete pseudocode illustrating how to do the whole thing. The main loop in my engine is very complex and has a lot of features, so pasting its code into the post is pointless.

I wrote an article on how to measure the duration of specific operations and their number per second in floating-point form and to be able to refresh the state of such counters any number of times per second. The article is in Polish and can be found here — Implementacja precyzyjnych liczników wydajności gry. Below is the English translation. I’m not a C guy, so the code examples are an imitation of C, treat them as pseudocode.

1. Counter of the average duration of a given operation

Let’s assume that we want to regularly measure the average execution time of a given operation, e.g. the duration of a single game logic update, and obtain the number of milliseconds, with a decimal part, as a result. The state of this counter can be refreshed any number of times per second, even after each logic update. Additionally, the result should be “smoothed” so that micro-fluctuations do not change its value too rapidly (this would reduce its readability).

1.1. Measurement of operation duration

For accurate measurement, use the clock with the highest frequency available on your platform. Milliseconds are not enough, so let’s use a hardware clock. The SDL library allows you to read this counter using the SDL_GetPerformanceCounter function, and its frequency using the SDL_GetPerformanceFrequency function. These are wrappers for the Windows functions QueryPerformanceCounter and QueryPerformanceFrequency.

The frequency of the Windows system clock (scheduler) is relatively low, maximum 1000 — i.e. every millisecond. However, the high-resolution counter, even on my potato laptop (it’s 10 years old), has a frequency of 10000000 (ten million), which means it changes its state every 100 nanoseconds. The difference is gigantic.

To measure the duration of an operation, take the reading of this counter just before and after the operation, and then count the difference in samples:

int64 ticks_start;  // time just before the operation is performed, in ticks
int64 ticks_sample; // duration of the operation, in ticks

ticks_start = SDL_GetPerformanceCounter();
update_logic();
ticks_sample = SDL_GetPerformanceCounter() - ticks_start;

1.2. Samples buffer

To meet the requirements for our counter, samples should be collected in a buffer and their values counted in the accumulator. The sample collection should be something like a circular buffer, i.e. of a fixed size, initially zeroed. You add a sample and increment the index for the next one, and if the index goes beyond the buffer area, it is set to 0. In this way, you can easily overwrite the oldest sample with a new one, and so on. You don’t need any sophisticated data types to implement such a buffer — just a simple fixed-size array and a variable with an index for the next sample.

To refresh the counter and not have to sum the samples each time in the loop, you can use an auxiliary accumulator variable, which is also initially zeroed. Each time a new sample needs to be added to the buffer, the value of the oldest sample is first subtracted from the accumulator, the value of the new one is added to it, and finally the oldest sample in the buffer is overwritten with the newly acquired one. In this way, the accumulator always stores the current sum of all samples, bypassing the need for loop summing.

Sample pseudocode for getting and collecting samples:

const int32 SAMPLE_NUM = 8; // samples array size

int64 ticks_start;  // time just before the operation is performed, in ticks
int64 ticks_sample; // duration of the operation, in ticks

int64[SAMPLE_NUM] samples = {0}; // array of samples
int64 sample_index = 0;          // index of the oldest sample, to be overwritten with the new one
int64 sample_accu  = 0;          // accumulator with the sum of all samples

// calculation of the logic update duration, in ticks
ticks_start = SDL_GetPerformanceCounter();
update_logic();
ticks_sample = SDL_GetPerformanceCounter() - ticks_start;

// adding a new sample to the buffer
sample_accu           -= samples[sample_index]; // subtract the value of the oldest sample from the accumulator
sample_accu           += ticks_sample;          // add a new sample to the accumulator
samples[sample_index]  = ticks_sample;          // overwrite the oldest sample in the array with the new one
sample_index          += 1;                     // increase the index of the oldest sample by 1

if (sample_index == SAMPLE_NUM) // if the sample index exceeds array bound
  sample_index = 0; // set it to the first cell of the array

In the example above, I used an eight-sample buffer, which gives quite good smoothing. It should be noted here that the more samples, the less visible the fluctuations will be and our counter will seem more lazy.

1.3. Calculation of the new counter state

With the samples collected in the buffer and their sum in the accumulator, you need to calculate the result — the average execution time of the logic update. When and how often our counter will be refreshed depends on your preferences. The only important thing is not to refresh it more often than after each execution of a given operation (here: logic update), because its state will not change.

We are interested in the average time in milliseconds (with a decimal part), so the calculations should look like this:

float = time_per_update; // average operation execution time, in milliseconds

time_per_update = sample_accu / SAMPLE_NUM / SDL_GetPerformanceFrequency() * 1000.0f;

That’s it, now you can display the value of the time_per_update variable on the screen, with any rounding. In my engine, I use such a counter to precisely calculate the average time of a single update of the game logic and rendering a single frame.


2. Counter of the number of operations performed per second

Here the situation is slightly different. The simplest way is to count the operations performed in the form of a simple variable — after each operation, the counter is incremented by 1. Once a second, the counter’s status is displayed on the screen and reset to zero. Simple implementation, but has drawbacks — the result is an int and its value changes once per second (not more often). To be able to update our counter more often than every second and also have a floating-point result, we need to collect samples again.

2.1. Measurement

We do not count the operations performed by simply incrementing an accumulator, because it will not help. We need to rely on timestamps, milliseconds are enough because they will provide sufficient precision. The SDL_GetTicks64 function or the Windows GetTickCount64 function is used for this purpose. Timestamp should be retrieved right after performing a given operation:

uint64 timestamp; // sample with time after the operation, in milliseconds

update_logic();
time_sample = SDL_GetTicks64();

2.2. Samples buffer

In this case, the sample buffer can also be an ordinary array, but its size may change dynamically while the game is running, so in order not to declare large arrays on the stack (with a spare space), it is better to use a list (depending on what you have available in your language and its RTL). This time, old samples are not overwritten, but a new one is simply added to the end of the list, after each operation (here: logic update).

Pseudocode:

list_of_uint64 samples; // uint64 sample list (hypothetical data type)
uint64 timestamp;       // sample with time after the operation, in milliseconds

// performing the operation
update_logic();

// getting the end time of the operation and adding the sample to the end of the list
time_sample = SDL_GetTicks64();
samples.append(time_sample);

2.3. Calculation of the new counter state

The list contains samples, their number is small at the start of the game and increases over time. To be able to calculate a new counter value (at any moment), you must first determine the sample interval closest to the second — after all, we are interested in how many times the operation was performed per second. The difference between the first and last samples in the list will initially be less than a second, but after the game has been running for a while, the difference will be greater than a second and this will continue until the end of the session.

We are interested in the freshest second period, so we need to determine the range of samples of this freshest second. This range must apply to samples counted from the end of the list towards the beginning. If the difference between the first and last samples is greater than a second, there are old samples at the beginning of the list and should be removed from the list. This can be done before calculating the result, but also after — it depends on what is more convenient for you.

To calculate the result, first check whether there are already any samples — if not, we do nothing (the value of our counter remains zero by default). However, if there are already some samples, we must determine their range giving at least a second difference. After determining the range, the difference of the two samples gives the final period (a second or slightly more), while the difference of their indices gives the number of operations performed per second, in the form of an integer. To obtain the result in floating-point form, simple calculations are enough. These two pieces of information are required for this calculation — the number of milliseconds between samples and the number of samples.

Pseudocode:

// only calculate the result if there are already some samples
if (samples.count > 0)
{
  // auxiliary indexes of samples in the list, to determine their second interval
  int32 index_first = samples.count - 1;
  int32 index_last  = samples.count - 1;

  // while the sample difference is less than 1000 milliseconds, move the start
  // index of the sample range towards the top of the list until the seconds range
  // is found or until the first sample is reached
  while (index_first > 0 && samples[index_last] - samples[index_first] < 1000)
    index_first -= 1;

  // sample difference must be greater than 0 (because it is a divisor) and if so, calculate 
  // the number of operations performed per second, in floating point form
  if (samples[index_last] - samples[index_last] > 0)
    updates_num = 1000.0f / (samples[index_last] - samples[index_first]) * (index_last - index_first);

  // if there are old samples at the beginning (before "index_first"), remove them
  if (index_first > 0)
    samples.delete_range(0, index_first - 1);
}

The above pseudocode uses sample indexes in the list, but if the sample buffer cannot be handled like arrays (as in my engine), and gives access to pointers to the first and last item, you can easily use pointers and their arithmetic instead of indexes. Iterating through a buffer as a continuous block of memory and using pointers gives higher performance, while a pointer is an index on steroids — not only does it directly point to the memory of an item, but pointer arithmetic also allows you to quickly calculate the number of items between these pointers.

In my engine, the above pseudocode looks like below (Free Pascal), it is an implementation using pointers and their arithmetic (after simplification, including shortening long variable names and without comments):

var
  UpdateNum: TGame_Float32 = 0.0;

var
  SamplesUpdate: TGame_List;
  SampleFirst:   PGame_UInt64;
  SampleCurrent: PGame_UInt64;
  SampleLast:    PGame_UInt64;

{..}

  if Game_ListReveal(@SamplesUpdate, @SampleFirst, @SampleLast) then
  begin
    SampleCurrent := SampleLast;

    while (SampleCurrent > SampleFirst) and (SampleLast^ - SampleCurrent^ < 1000) do
      SampleCurrent -= 1;

    if SampleLast^ - SampleCurrent^ > 0 then
      UpdateNum := 1000.0 / (SampleLast^ - SampleCurrent^) * (SampleLast - SampleCurrent);

    if SampleCurrent > SampleFirst then
      Game_ListDeleteRange(@SamplesUpdate, 0, SampleCurrent - SampleFirst);
  end;

The above implementation gives an effect almost exactly the same as FRAPS, with the difference that the calculated framerate is in floating-point form, which can be rounded as desired.

This is the method I usually use to compute framerate and maintain stable framerate, I keep track of timing of last frames and I subtract the current time and the time 60 frames ago. I mainly store fps as an integer with a denominator of 10 or 100 since I don’t need the floating point dependency in there, but it is possible to compute floating point version of fps.

const uint64_t frameratedeterminewindow = 60;
uint64_t frametiming[frameratedeterminewindow+1];
uint64_t frameint;
uint64_t timecounter;
uint64_t cputimersecond;

const int framerate = 60;

void initscreen(){
 cputimersecond=SDL_GetPerformanceFrequency();
 frameint=(cputimersecond+(framerate/2))/framerate;
 timecounter=SDL_GetPerformanceCounter();
 frametiming[frameratedeterminewindow]=timecounter-frameint;
 for(int i=frameratedeterminewindow; i>0; i--) {frametiming[i-1]=frametiming[i]-frameint;}
}

uint32_t fps;

void fpsupdate(){
 memmove(frametiming,frametiming+1,frameratedeterminewindow*sizeof*frametiming);
 frametiming[frameratedeterminewindow] = SDL_GetPerformanceCounter();
 uint64_t q=cputimersecond; q*=frameratedeterminewindow*100*2;
 fps = (frametiming2[frameratedeterminewindow]-frametiming2[0])?(q/(frametiming2[frameratedeterminewindow]-frametiming2[0])+1)/2:-1;
 //floating point version: (long double)cputimersecond*frameratedeterminewindow/(frametiming2[frameratedeterminewindow]-frametiming2[0])
}

void framesleep(){
 fpsupdate();
 uint64_t tmp=SDL_GetPerformanceCounter();
 if(tmp-timecounter>4*frameint) timecounter=tmp-4*frameint;
 while(tmp-timecounter<frameint) {SDL_Delay(1); tmp=SDL_GetPerformanceCounter();}
 timecounter+=frameint;
}