Vsync and frame timing

Hi, I'm using vsync for frame flipping, but I'm a bit confused on how to control the frame flipping. Right now i time a frame and try to guess what frequency vsync is running at (See code below). I've read games will render a heavy scene (lots of particle effects & sprites/models) at startup and use that dt value for the game.

The questions I had were:

1. Is there a way to get the rates that vysnc will occur at? I found 24fps was a common frame rate by timing. Can vysnc only be the monitor refresh rate equally divided by a whole number. ie. for 60fps the rates would be 60, 30, 15, 10?

2. If the monitor refresh rate is 120fps but i wanted to cap the game at 60fps would i use setSwapInterval for this?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
           float idealFrameTime = 1.0f / mode.refresh_rate; //use monitor refresh rate 
            ...
            ...
            dt = timeInFrameSeconds / 1000;
            float frameRates[] = {idealFrameTime*1.0f, idealFrameTime*2.0f, idealFrameTime*4.0f};
            float smallestDiff = 0;
            bool set = false;
            float newRate = idealFrameTime;
            for(int i = 0; i < arrayCount(frameRates); ++i) {
                float val = frameRates[i];
                float diff = dt - val;
                if(diff < 0.0f) {
                    diff *= -1;
                }
                if(diff < smallestDiff || !set) {
                    set = true;
                    smallestDiff = diff;
                    newRate = val;
                }
            }
            dt = newRate;
            assert(dt <= frameRates[2]);


Thanks for you help.

Edited by Oliver Marsh on Reason: Initial post
1. Yes, that's exactly right - it must be divider of monitor vertical refresh rate.

2. Yes, you set swap interval to be 2. Because 120/60 = 2.

There is one extra thing you can look into - EXT_swap_control_tear extension which allows to wait for vsync if your frame time is smaller, or tear if you go over this. This avoids extra latency in case some frames spike due to unforeseen reasons (OS doing weird stuff).

Not sure why you say 24fps is common frame rate for timing. For games common frame rate is 60. Or 30. Unless you use gsync/freesync monitor.
Thanks for clearing that up mmozeiko. I think it must of just been the timer broken for the 24fps. Thanks for the EXT_swap_control_tear suggestion, sounds very handy.

1. 24fps is the frame rates of movies (in theater). It's enough for movie because the camera "captures" motion blur which tends to smooth frame transition. 24fps in games is really bad. I (to avoid generalizing) can see the difference between 30fps and 60fps pretty clearly, and I can feel the difference in input latency between 60 and 120 (or vsync off). Depending on the game/application it can make a (big) difference. 20 is also a multiple of 60 so your list should be 60, 30, 20, 15, 10.

To get the frame time you could just measure it:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
u64 performanceFrequency = 0;
u64 lastPerformanceCounter = 0;
QueryPerformanceFrequency( ( LARGE_INTEGER* ) &performanceFrequency );
    
while ( running ) {
        
    r32 deltaTime = 0.0f;
    u64 performanceCounter = 0;
    QueryPerformanceCounter( ( LARGE_INTEGER* ) &performanceCounter );
    perf_getPeformanceCounter(  );
        
    if ( lastPerformanceCounter == 0 ) {
        deltaTime = 1.0f / 60.0f;
    } else {
        deltaTime = (r32) ( (r64)( performanceCounter - lastPerformanceCounter ) / (r64) performanceFrequency );
    }

    lastPerformanceCounter = performanceCounter;

    // ...
}


It's probably better to avoid using FPS as a measure, and avoid conversion from fps to ms and back. It ok to show fps to the user but it isn't useful in the code.
Yes, I should try and get the program to consistently run at 60fps. The 24fps was off a timer taking a running mean as well so i think that was the main problem. Yes, i should make sure everything is seconds per frame not fames per second to avoid any error.

Edited by Oliver Marsh on