Forcing Frame-Rate to the Game
In this post, I'm gonna show you how to force the game frame-rate.
Basically, we need that game runs in 30 or 60 frames per seconds.
This post will be very simple.
The first step is to create utility functions that compute the wall clock and time elapsed.
We'll use the QueryPerformanceCounter
to get the wall clock and the
QueryPerformanceFrequency
for mapping to seconds.
The next code retrieves the wall clock.
inline static LARGE_INTEGER win32GetWallClock() { LARGE_INTEGER counter; QueryPerformanceCounter(&counter); return counter; }
And the next code retrieves the seconds elapsed from A to B timestamp.
inline static f64 win32GetSecondsElapsed(LARGE_INTEGER start, LARGE_INTEGER end) { return (f64)(end.QuadPart - start.QuadPart) / (f64) perfFrequency; }
Ok, the next step is to define the target seconds per frame. This value is based on monitor refresh frequency.
We'll assume that the target seconds per frame is half of monitor frequency.
int monitorRefreshHz = 60; // get your monitor hz here! int gameUpdateHz = monitorRefreshHz / 2; float targetSecPerFrame = 1.0f / gameUpdateHz;
Now, before the flip (buffer swap), let's force a frame-rate.
Get the seconds elapsed between the work time and last frame counter.
LARGE_INTEGER workCounter = win32GetWallClock(); f64 workSecondsElapsed = win32GetSecondsElapsed(lastCounter, workCounter); f64 secondsElapsedForFrame = workSecondsElapsed;
Now, if the time elapsed is less than target, we must wait until hit the target.
This 'wait' can be achieve by the Sleep
function, that turns the
current thread idle.
But, we must check if this machine can sleep, otherwise, we'll consume
the CPU time only using the while
loop.
UINT desireScheduleMS = 1; bool isSleep = timeBeginPeriod(desireScheduleMS) == TIMERR_NOERROR;
The above code makes the operation system to be more granularity, I mean, it allows the 1 milliseconds for schedule the thread.
if (secondsElapsedForFrame < targetSecPerFrame) { if (isSleep) { int fixedLatencyMS = 2; DWORD sleepMS = (1000 * (targetSecPerFrame - secondsElapsedForFrame)) - fixedLatencyMS; if (sleepMS > 0) { Sleep(sleepMS); } } }
The fixedLatencyMS
variable ensure that we don't sleep the whole time.
For me, works fine.
Now, after the Sleep
, if there is still time, let's consume it with
while loop.
while(secondsElapsedForFrame < targetSecPerFrame) { LARGE_INTEGER nextCounter = win32GetWallClock(); secondsElapsedForFrame = win32GetSecondsElapsed(lastCounter, nextCounter); }
We'll just get out of the loop if we can achieve the frame rate.
LARGE_INTEGER endCounter = win32GetWallClock(); f64 secondsPerFrame = win32GetSecondsElapsed(lastCounter, endCounter); lastCounter = endCounter;
At the end of frame, the lastCounter
receive the current wall clock
endCounter
.
The program must fixed at 33.33ms or 16.66ms.
That's it!
This is a frame rate that you need to the game.