Best way to calculate framerate

Member
Posts: 185
Joined: 2005.02
Post: #1
My program is based in SDL with fixed-interval game update cycles (like this), and written in C. I was wondering how best to calculate the framerate.

The only way that I could think of would be to have an int FPS that was incremented after every call to my display function, then have an SDL timer that fired every second that printed the number and set it back to zero.

What is the best way to do it?
Quote this message in a reply
Sage
Posts: 1,199
Joined: 2004.10
Post: #2
This works pretty well for me, but I doubt it's the "best".

Code:
void Application::postDisplay( void )
{
    static unsigned int frameCounter = 1;
    static float lastCheckTime = 0;
    
    float elapsed = simtime::now() - lastCheckTime;
    if ( elapsed > 1.0 )
    {
        _currentFPS = (float) frameCounter / (float) elapsed;

        lastCheckTime = simtime::now();
        frameCounter = 0;
    }

    ++frameCounter;
}

It's called from the function Application::loop() which is called continuously from a timer.

Code:
void Application::loop( void )
{
    if ( !_ready ) return;
    
    /*
        Run our keys
    */
    if ( !_suppressKeys )
    {
        KeyVec::iterator it( _keys.begin() ), end( _keys.end() );
        for ( ; it != end; ++it ) it->match();
    }

    /*
        Update time and find out how much time
        has elapsed since last iteration of loop.
        
        Then add whatever was left over from last iteration.
    */

    _currentTime = CFAbsoluteTimeGetCurrent();
    
    float dt = _currentTime - _lastTime;
    _lastTime = _currentTime;

    float availableTime = dt + _timeRemainder;    
    if ( availableTime < simtime::interval() )
    {
        _timeRemainder += dt;
    }
    else
    {
        float steps = availableTime / simtime::interval();
        int numSteps = lrintf( steps );
        
        for ( int i=0; i < numSteps && !_performingPostRedisplay; i++ )
        {
            /*
                Update time, run timers, step.
            */
            
            simtime::update();
            processTimers();
            step();
            _hud->step();

            postStep();
        }

        /*
            Increment remainder
        */
        _timeRemainder = (steps - float( numSteps )) * simtime::interval();
    }

    /*
        We always draw, in the end.
    */

    display();
    _hud->display();    

    postDisplay();
}
Quote this message in a reply
Member
Posts: 185
Joined: 2005.02
Post: #3
hmm... I don't know C++, What does "::" mean?
Quote this message in a reply
Moderator
Posts: 522
Joined: 2002.04
Post: #4
http://www.unifycommunity.com/wiki/index...sPerSecond

Read the comments there. That's the best way I've used. (No complaints!)

-Jon
Quote this message in a reply
Moderator
Posts: 1,140
Joined: 2005.07
Post: #5
Um... that's JavaScript... Last I heard, SDL doesn't work too well with JavaScript. Wink

If you're using C, I suggest using gettimeofday. Here's a little snippet of code that should work.
Code:
double lastTime, currentTime, fps;
struct timeval time;
gettimeofday(&time, NULL);
lastTime = time.tv_sec + time.tv_usec*1e-6;
...
//in render loop
gettimeofday(&time, NULL);
currentTime = time.tv_sec + time.tv_usec*1e-6;
fps = 1/(currentTime - lastTime);
lastTime = currentTime;
lastTime and currentTime must be doubles (floats don't have enough precision), but fps could potentially be a float if you want.

BTW, to answer your question about C++, :: is scope resolution, whether with a class or namespace.
Quote this message in a reply
Member
Posts: 185
Joined: 2005.02
Post: #6
Ok what's wrong with my port of that code to non-Unity-dependent C?
It keeps telling me the frame rate is either 1, less than 1, or infinity.
here's my port:
Code:
#define updateInterval 0.5
float accum = 0.0;
int frames = 0;
float timeLeft = updateInterval;
double FPS_lastFrameTime;
    
void FPSupdate (void)
{
    timeLeft -= (currentTime - FPS_lastFrameTime);
    accum += 1 / (currentTime - FPS_lastFrameTime);
    ++frames;
    FPS_lastFrameTime = currentTime;
    if(timeLeft <= 0.0)
    {
        printf("%f\n", (accum / frames));
        timeLeft = updateInterval;
        accum = 0.0;
        frames = 0;
    }
    return;
}

currentTime and FPS_lastFrameTime are in milliseconds, and FPSupdate is called at the end of my display function.
Quote this message in a reply
Moderator
Posts: 1,140
Joined: 2005.07
Post: #7
First off, if your times are in milliseconds, you should be subtracting timeLeft by (currentTime - FPS_lastFrameTime)/1000.0f, and you should be adding accum by 1000.0f/(currentTime - FPS_lastFrameTime). If your program is going over 1000 fps, you will get an infinite framerate at times, as well, since the difference is 0. (very possible if it's a simple program) Also, the problem with doing everything in milliseconds is it's usually too rough of an interval. (for example, if it takes 9 ms for a render, it's going at 111.111 fps, while if it takes 10 ms it's going at 100 fps) The advantage of using my method is it theoretically has 1000 times the precision since it has a resolution down to 1 microsecond, and everything is already done in seconds.
Quote this message in a reply
Moderator
Posts: 771
Joined: 2003.04
Post: #8
Here is what I use:
Code:
void Game::CalculateFPS()
{
    static float lastTime = 0.0f;
    float currentTime = SDL_GetTicks();
    _fps = _fps*0.9+(100.0f/(currentTime - lastTime));
    lastTime = currentTime;
}

In plain C, it would look something like this:
Code:
float getFPS()
{
    static float fps = 0.0f;
    static float lastTime = 0.0f;
    float currentTime = SDL_GetTicks();
    fps = fps*0.9+(100.0f/(currentTime - lastTime));
    lastTime = currentTime;
    return fps;
}
Quote this message in a reply
Moderator
Posts: 522
Joined: 2002.04
Post: #9
akb825 Wrote:Um... that's JavaScript... Last I heard, SDL doesn't work too well with JavaScript. Wink
Yeah, real clever. The algorithm is the important part, thus I pointed towards the comments in the code, which explain the algorithm.

/kick akb825

-Jon
Quote this message in a reply
Moderator
Posts: 3,577
Joined: 2003.06
Post: #10
Here's yet another way to do it in C which will update a string every half second for convenience:

Code:
#define UPDATE_INTERVAL  0.5

char    fpsString[32];

void RenderFrame(void)
{
    double           currTime, deltaFPSTime, frameRate;
    static long      frameCount = 0;
    static double    lastFPSUpdate = 0.0;

    currTime = CurrentTime();
    frameCount++;
    deltaFPSTime = currTime - lastFPSUpdate;
    if (deltaFPSTime >= UPDATE_INTERVAL)
    {
        frameRate = (double)frameCount / deltaFPSTime;
        sprintf(fpsString, "FPS: %0.1f", frameRate);
        lastFPSUpdate = currTime;
        frameCount = 0;
    }

    // draw stuff
}

double CurrentTime(void)
{
    struct timeval time;

    gettimeofday(&time, NULL);
    return (double)time.tv_sec + (0.000001 * (double)time.tv_usec);
}
Quote this message in a reply
Member
Posts: 469
Joined: 2002.10
Post: #11
Not that I've done this before, but I just thought of a way to get live FPS with good accuracy...

Set up a sliding window calculation:
1) Set up a Queue of timestamps.
2) Every frame put a timestamp in the Queue.

To calculate your sliding window FPS:
Lets say your window is 3 seconds.
1) poll the Queue and discard timestamps until you reach a timestamp that is within the last 3 seconds.
2) divide the Queue count by 3.0
3) there's no step 3!

This method for calculating FPS will give you a live average of the last 3 seconds with higher accuracy than any other method. If you set your window to 1 second, you get an exact number of frames for the last second. The nice thing about the live average is that you can see your FPS for EVERY SINGLE FRAME. if you were so inclined, you could graph the result and get a non-chunky live view of your throughput.

Implementation details:
Fear not, I've also thought of a novel way to implement this sliding window. Use a ring buffer (will eliminate allocations) of doubles (ints might be slightly faster) for your Queue. This will be very very fast and keep FPS calculation to a minimum. Also, doing the poll dump is trivial and fast. Just traverse and compare. If you are calculating FPS every frame with a 1 second window, you'll on average do just 1 inequality check and 2 adds.

When you reach a Queue overflow, simply over-write the head of the Queue and move the head down the line. When you do this, you get a capped FPS (I'd say 1024 (you can wrap it with a bit manipulation) is a nice sized ring buffer to allocate) calculation.

When you think about it, saving memory to calculate FPS is really not beneficial at all. You're trying to calculate how fast you are going! Why slow down (even infinitesimally) for some useless approximate calculation? Nowadays 4KB for a sliding window is practically free. And the calculation... well, this implementation is about as cheap as you can get. (and it's live!)

[edit:] I just thought of something else: Since your Queue is sorted (in theory) you can traverse it like a binary tree. If you are using the ring buffer implementation with 1024 spaces, you'll never have to compare more than 11 entries!

[edit again:] Perhaps the best way to do the poll dump is to do an exponential search up, then an exponential search down. Since the index you're looking for is *most likely* near the head of the queue, you'll probably find it faster searching up than down.

---Kelvin--
15.4" MacBook Pro revA
1.83GHz/2GB/250GB
Quote this message in a reply
Member
Posts: 469
Joined: 2002.10
Post: #12
TomorrowPlusX Wrote:This works pretty well for me, but I doubt it's the "best".

Code:
void Application::postDisplay( void )
{
    static unsigned int frameCounter = 1;
    static float lastCheckTime = 0;
    
    float elapsed = simtime::now() - lastCheckTime;
    if ( elapsed > 1.0 )
    {
        _currentFPS = (float) frameCounter / (float) elapsed;

You've got a divide by zero error waiting to happen on fast machines.

Note: this goes for all the above implementations in this thread. (except mine of course)

---Kelvin--
15.4" MacBook Pro revA
1.83GHz/2GB/250GB
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #13
It's not an error to do a floating-point divide by zero (you'll get infinity or -infinity, but that's not a major issue)
Quote this message in a reply
Member
Posts: 30
Joined: 2006.05
Post: #14
Here's how I do it using SDL (I think this is all of it... didn't check carefully...):

Keep a variable that is updated at the end of each frame called "startFrame" (at the end of each game loop, this code should be run:
Code:
startFrame = SDL_GetTicks();
Then at the beginning of each game loop, get the current frame rate like so:
Code:
// the framerate:
int framerate = (1000/(SDL_GetTicks() - startFrame));

If I missed something, please forgive me - I'm somewhat busy at the moment and didn't have much time to write this.
Quote this message in a reply
Moderator
Posts: 771
Joined: 2003.04
Post: #15
kelvin Wrote:Not that I've done this before, but I just thought of a way to get live FPS with good accuracy...

Set up a sliding window calculation:
1) Set up a Queue of timestamps.
2) Every frame put a timestamp in the Queue.

To calculate your sliding window FPS:
Lets say your window is 3 seconds.
1) poll the Queue and discard timestamps until you reach a timestamp that is within the last 3 seconds.
2) divide the Queue count by 3.0
3) there's no step 3!

This method for calculating FPS will give you a live average of the last 3 seconds with higher accuracy than any other method. If you set your window to 1 second, you get an exact number of frames for the last second. The nice thing about the live average is that you can see your FPS for EVERY SINGLE FRAME. if you were so inclined, you could graph the result and get a non-chunky live view of your throughput.

Or... you could just use exponential smoothing (what I posted earlier) to get very similar results. Wink Actually, not only is exponential smoothing a lot simpler to implement, it also suffers less lag so maybe not just "similar" but "better" results:

http://www.robertwcolby.com/EMAreport.html
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  Framerate control + basic physics math MacGoober 4 3,360 Jan 10, 2007 11:40 AM
Last Post: Frank C.
  Calculate mouse position in opengl MACnus 1 3,677 Nov 8, 2006 01:02 PM
Last Post: akb825
  NSTimer / animation framerate question MonitorFlickers 5 5,162 Dec 4, 2005 01:10 PM
Last Post: MonitorFlickers