A Simple GLFW FPS Counter

Update – September 2013: Need something that enforces a given frame rate rather than just reports the current frame rate? Try this: https://r3dux.org/2012/12/fpsmanager-a-c-helper-class-for-framerate-independent-movement/


I’m fed up of solving the same problem over and over again, so the next time I need some FPS measurements, I’m going to use this…

The Include Requirements

#include <iostream>
#include <string>
#include <sstream>
#include <GL/glfw.h>

The Function

double calcFPS(double theTimeInterval = 1.0, std::string theWindowTitle = "NONE")
{
	// Static values which only get initialised the first time the function runs
	static double t0Value       = glfwGetTime(); // Set the initial time to now
	static int    fpsFrameCount = 0;             // Set the initial FPS frame count to 0
	static double fps           = 0.0;           // Set the initial FPS value to 0.0
 
	// Get the current time in seconds since the program started (non-static, so executed every time)
	double currentTime = glfwGetTime();
 
	// Ensure the time interval between FPS checks is sane (low cap = 0.1s, high-cap = 10.0s)
	// Negative numbers are invalid, 10 fps checks per second at most, 1 every 10 secs at least.
	if (theTimeInterval < 0.1)
	{
		theTimeInterval = 0.1;
	}
	if (theTimeInterval > 10.0)
	{
		theTimeInterval = 10.0;
	}
 
	// Calculate and display the FPS every specified time interval
	if ((currentTime - t0Value) > theTimeInterval)
	{
		// Calculate the FPS as the number of frames divided by the interval in seconds
		fps = (double)fpsFrameCount / (currentTime - t0Value);
 
		// If the user specified a window title to append the FPS value to...
		if (theWindowTitle != "NONE")
		{
			// Convert the fps value into a string using an output stringstream
			std::ostringstream stream;
			stream << fps;
			std::string fpsString = stream.str();
 
			// Append the FPS value to the window title details
			theWindowTitle += " | FPS: " + fpsString;
 
			// Convert the new window title to a c_str and set it
			const char* pszConstString = theWindowTitle.c_str();
			glfwSetWindowTitle(pszConstString);
		}
		else // If the user didn't specify a window to append the FPS to then output the FPS to the console
		{
			std::cout << "FPS: " << fps << std::endl;
		}
 
		// Reset the FPS frame counter and set the initial time to be now
		fpsFrameCount = 0;
		t0Value = glfwGetTime();
	}
	else // FPS calculation time interval hasn't elapsed yet? Simply increment the FPS frame counter
	{
		fpsFrameCount++;
	}
 
	// Return the current FPS - doesn't have to be used if you don't want it!
	return fps;
}

Usage Examples

Call any of these in your main loop…

string windowTitle = "My Lovely App: "; // You might want to have string with window title hanging around...
 
cout << calcFPS() << endl;              // Print the FPS to the console once per second
cout << calcFPS(2.0) << endl;           // Print the FPS to the console every 2 seconds
calcFPS(1.0, windowTitle);              // Update the window title to include the FPS details once per second
calcFPS(2.0, windowTitle);              // Update the window title to include the FPS details every 2 seconds
calcFPS(3.0, "Current FPS: ");          // Update the window title to the string literal "Current FPS: " + the FPS details every 3 seconds

Suggestions?

I think that’s pretty usable and clean – if you’ve got any suggestions I’d really be interested in hearing them – I simply don’t want to re-implement a FPS counter in C++ ever again.

8 thoughts on “A Simple GLFW FPS Counter”

  1. I might be wrong but I think your counter misses a frame, fpsFrameCount should be reseted to 1 and not 0, otherwise you miss counting one frame when is time to return a new FPS value. I tried running your counter with no low time interval cap and it kept returning 0 fps

    1. Hmm, I can see what you’re getting at – and you’re right, resetting the fpsFrameCount to 1 does seem to provide FPS values, while leaving it at 0 returns a FPS value of zero.

      So picking it apart… doing away with the interval entirely should mean that the (now non-existent) interval elapses every time, so I’d expect the value fpsFrameCount to still be 0 because it never goes to the else condition to indicate “the interval hasn’t elapsed yet, so add 1 to the frame count”. When we divide 0 by the frame interval, we get 0.

      When we reset to a value of 1, still with no interval, we’re going to be re-calculating the FPS value with 1 frame divided by the duration of that frame – so we get the correct FPS value.

      But, my thinking when writing it was “Count how many frames have been drawn in a given interval” – once that interval has expired, we’ve drawn 0 frames since the interval expired – so I still like resetting to 0 as that feels like the factually correct thing to do. But something has to change, and I think that something is to move the frame count adder to the top of the function and let it always run (i.e. if the calcFPS() function runs, the fpsFrameCount gets incremented).

      How about this:

      1. double calcFPS(double timeInterval = 1.0, std::string windowTitle = "NONE")
      2. {
      3.     // Static values which only get initialised the first time the function runs
      4.     static double startTime  =  glfwGetTime(); // Set the initial time to now
      5.     static double fps        =  0.0;           // Set the initial FPS value to 0.0
      6.  
      7.     // Set the initial frame count to -1.0 (it gets set to 0.0 on the next line). Because
      8.     // we don't have a start time we simply cannot get an accurate FPS value on our very
      9.     // first read if the time interval is zero, so we'll settle for an FPS value of zero instead.
      10.     static double frameCount =  -1.0;
      11.  
      12.     // Here again? Increment the frame count
      13.     frameCount++;
      14.  
      15.     // Ensure the time interval between FPS checks is sane (low cap = 0.0 i.e. every frame, high cap = 10.0s)
      16.     if (timeInterval < 0.0)
      17.     {
      18.         timeInterval = 0.0;
      19.     }
      20.     else if (timeInterval > 10.0)
      21.     {
      22.         timeInterval = 10.0;
      23.     }
      24.  
      25.     // Get the duration in seconds since the last FPS reporting interval elapsed
      26.     // as the current time minus the interval start time
      27.     double duration = glfwGetTime() - startTime;
      28.  
      29.     // If the time interval has elapsed...
      30.     if (duration > timeInterval)
      31.     {
      32.         // Calculate the FPS as the number of frames divided by the duration in seconds
      33.         fps = frameCount / duration;
      34.  
      35.         // If the user specified a window title to append the FPS value to...
      36.         if (windowTitle != "NONE")
      37.         {
      38.             // Convert the fps value into a string using an output stringstream
      39.             std::ostringstream stream;
      40.             stream << fps;
      41.             std::string fpsString = stream.str();
      42.  
      43.             // Append the FPS value to the window title details
      44.             windowTitle += " | FPS: " + fpsString;
      45.  
      46.             // Convert the new window title to a c_str and set it
      47.             const char* pszConstString = windowTitle.c_str();
      48.             glfwSetWindowTitle(pszConstString);
      49.         }
      50.         else // If the user didn't specify a window to append the FPS to then output the FPS to the console
      51.         {
      52.             std::cout << "FPS: " << fps << std::endl;
      53.         }
      54.  
      55.         // Reset the frame count to zero and set the initial time to be now
      56.         frameCount        = 0.0;
      57.         startTime = glfwGetTime();
      58.     }
      59.  
      60.     // Return the current FPS - doesn't have to be used if you don't want it!
      61.     return fps;
      62. }

      Change summary:
      – Because we’re adding to the frame count early, I’ve also set the initial frameCount value to be -1.0 so that it bumps to 1.0 on first run and the very first FPS reading is zero. We can’t get an accurate value on a first run with a zero interval because we don’t have a start time to measure the frame duration, so a very first FPS value of 0 is preferable than some large value about the duration between consequtive calls to glfwGetTime(),
      – The frame count gets reset to zero at interval change (as before – but this is correct!),
      – It works properly now with a zero interval (which I’ve made allowable),
      – I’ve renamed the variables to make things more sensible, and finally
      – It should run a little bit faster as I’ve removed the cast to double on frameCount and got rid of a divide by re-using a duration value.

      Cheers, and thanks for your feedback!

  2. I just started OpenGL and came across your post. Thank you !
    Also, I have my own GLFW window because I need to define specific height, width.

    Here’s my own window :
    GLFWwindow *window = glfwCreateWindow( WIDTH, HEIGHT, “Exercise 2” , nullptr, nullptr );

    And I can’t seem to call the function using input of my original GLFW window for the 2nd parameter. Any suggestion ?

    1. Hi, I took a look at then docs and tried to change the function parameter to accept my GLFW window. So I tried using : GLFWwindow* window;

      double calcFPS(GLFWwindow *window, double timeInterval = 1.0)
      ….
      if (window)
      {

      But now, since the parameter isn’t string. I can’t append the fps as in → window += ” | FPS: ” + fpsString;
      I know this is silly, like I don’t even completely understand pointer, yet dare to go for OpenGL. I’m sorry. but I can’t really get it work

      1. Pretty close! You need to change the calcFPS function signature to this:

        double calcFPS(GLFWwindow *window, std::string windowTitle = "NONE", double timeInterval = 1.0)

        Then, inside the function actually set the title using the window object, like this:

        glfwSetWindowTitle(window, windowTitle);

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.