Locking Frame Rate without busy waiting

Started by
11 comments, last by Kitt3n 17 years, 8 months ago
I am currently writing a timer class and I am trying to decide on how to implement a function that will lock the fps to a certain number of frames per secon. So far the most precise method I found is to use the systems high performance timer inside a while loop. But I dont want to lock up the main thread doing nothing. I found a function called SetTimer which switchs out of the main thread and will send a message to a specified handler when the set time expires. Though according to what i've read it doesnt seem to have fine enough resolution/precision. Lastly a sleep() isn't guaranteed to have exact timing. So is the busy waiting the only way to precisely lock fps?
Advertisement
Well my background is in Industrial PLC stuff, but this is what im thinking.

#include <windows.h>#include <ctime>#include <cstdlib>#include <stdio.h>#include <iostream>#include <math.h>using namespace std;int main (){  LARGE_INTEGER LI_CurrentCount;  LARGE_INTEGER LI_CountFrequency;  double D_CurrentCount;  double D_CountFrequency;  double D_FrameStartTime;  double D_RealTime;  double D_TimePassed;  double D_PreviousRealTime;  double D_ScansPerSecond;  double D_TimeSinceStart;  double D_InverseFrequency;  double D_GoalFPS = 60;QueryPerformanceFrequency (&LI_CountFrequency);  D_CountFrequency      = LI_CountFrequency.QuadPart*(1.0f/D_GoalFPS);  D_InverseFrequency    = 1 /D_CountFrequency;  while(1)  {    QueryPerformanceCounter (&LI_CurrentCount);    D_CurrentCount      = LI_CurrentCount.QuadPart;    D_RealTime          = D_CurrentCount * D_InverseFrequency;    D_TimeSinceStart    = D_RealTime - D_FrameStartTime;    ///////////////////////////////////////////////////////////////    // Do Game Stuff Here - loops continually    ///////////////////////////////////////////////////////////////    if (D_TimeSinceStart>1)    {        ///////////////////////////////////////////////////////////////        // Render here - executes D_GoalFPS times per second        ///////////////////////////////////////////////////////////////        D_FrameStartTime = D_RealTime;    }  }}


extrapolate out as needed.

:) good luck


... and if the interveening game code is too long to loop back through the whole thing, you could place several jumps out in the various pieces so it would render from wherever it happened to be in the game code once the alloted time for that frame was up
Quando Omni Flunkus MoritatiWhen All Else Fails, Play Dead!
You are correct in that Sleep() is not an accurate way to lock the frame rate.

I am a bit confused as to why you don't want to lock with a while loop doing nothing though. If there is another application running at the same time as yours, Windows will automatically apportion time to each thread automatically.

I have heard that you can call Sleep(0) to volunteer up your time slice but as to whether there is any advantage to doing that, I'll leave that to someone who knows more about multi-threading.

From the OS's point of view, there is no problem with just spinning round in the message loop until a time threshold is reached. I can't find the link but there is code on MSDN that does just that.

A better solution though, although more complex to implement, is to make the app frame-rate independant by calculating the time since the last frame and using this value to adjust the speeds of movement and animation etc. This has the advantage that the app will run acceptabley on a machine that can't even manage the frame rate that you are trying to lock to.
NEVER lock your framerate that way, this is very bad practice (read: an abomination). Don't adapt the framerate to match your code, but adapt your code to match the current framerate. In other words, use adaptive time steps that take framerate into account.

If you only want to remove tearing artifacts, then use V-Sync. Although you still have to be framerate adaptive, since v-sync does not guarantee an explicit fixed rate either.

In order to measure frametime, you can use several different approaches. Query performance counter (or RDTSC) is the most precise way, but is going to fail horribly on multi-core systems (or on notebooks with variable speed CPUs). A high resolution system timer, or something like GetTickCount() would be more reasonable.

Quote:
I am a bit confused as to why you don't want to lock with a while loop doing nothing though. If there is another application running at the same time as yours, Windows will automatically apportion time to each thread automatically.

Not really. While Windows uses preemptive multitasking, and can force-distribute time slices, you will stumble over a different problem with 'pure' loops: You have to periodically call the message pump, otherwise Windows will label your process as "not responding", and ask the user for termination. An alternative is using Sleep(), which can be very useful if your window is minimized, for example. But none of these should ever be used to lock framerate.
Quote:Original post by Yann L
NEVER lock your framerate that way, this is very bad practice (read: an abomination). Don't adapt the framerate to match your code, but adapt your code to match the current framerate. In other words, use adaptive time steps that take framerate into account.

If you only want to remove tearing artifacts, then use V-Sync. Although you still have to be framerate adaptive, since v-sync does not guarantee an explicit fixed rate either.

In order to measure frametime, you can use several different approaches. Query performance counter (or RDTSC) is the most precise way, but is going to fail horribly on multi-core systems (or on notebooks with variable speed CPUs). A high resolution system timer, or something like GetTickCount() would be more reasonable.

Quote:
I am a bit confused as to why you don't want to lock with a while loop doing nothing though. If there is another application running at the same time as yours, Windows will automatically apportion time to each thread automatically.

Not really. You have to periodically call the message pump, otherwise Windows will label your process as "not responding", and ask the user for termination. An alternative is using Sleep(), which can be very useful if your window is minimized, for example. But none of these should ever be used to lock framerate.



Thanks for the tip. I didn't actually plan on locking the frame rate. I just get semi-obsessed with writing every function I can think of when I write classes (bad habit, I know).

The class currently uses a system where it checks if the "Query performance counter" is available on that system, if it isn't it just uses "GetTickCount()" to keep track of time.

You mentioned that the "Query performance counter" doesnt work properly on multicore systems. For example if the system is multicore will Windows list it as an unavailable device or will it just let me use it?
Quote:Original post by EasilyConfused
From the OS's point of view, there is no problem with just spinning round in the message loop until a time threshold is reached. I can't find the link but there is code on MSDN that does just that.

A better solution though, although more complex to implement, is to make the app frame-rate independant by calculating the time since the last frame and using this value to adjust the speeds of movement and animation etc. This has the advantage that the app will run acceptabley on a machine that can't even manage the frame rate that you are trying to lock to.


Sorry. Didn't mean a while loop exactly.
Can't you just get the graphics subsystem to wait for vsync on buffer swap?

For instance, using the openGL extension WGL_EXT_swap_control (On Windows) and GLX_SGI_swap_control on X ? (I believe Apple have a similar one on the Mac)

This is clearly the right thing to do.

Some graphics vendors have another method of setting this up which is not (directly) controlled by the application, for instance a control panel or environment variable.

I expect Direct3d has something similar which you can enable which will cause it to wait for vsync when you swap buffers.

Whether it uses busy-waiting or not is implementation dependent.

Mark
Quote:Original post by fpsgamer
You mentioned that the "Query performance counter" doesnt work properly on multicore systems. For example if the system is multicore will Windows list it as an unavailable device or will it just let me use it?

It will just let you use it. There are mainly two problems with rdtsc based functions (QPC is partially based on rdtsc, but is not guaranteed to use it either - although in practice it will use it most of the time): F

First, multicore. Each core has its own time stamp counter. If both are not synchronised properly, and your process is switched from one core to the next by the OS task scheduler, then you'll suddendly read two completely unrelated counter values. AFAIK, Intel CPUs use synchronized time stamp counters, so you won't run into the problem with them. AMD multi core CPUs however, are asynchronous last time I checked. It may or may not be fixed in the future, but I wouldn't rely on it. There are some obscure hacks (SetAffinityMask) which have other negative side effects, and there is a fix available from AMD (although it doesn't seem to work all of the time).

The second problem comes from CPUs that dynamically change their frequency during operation. SpeedStep, for example. This feature is often used in notebooks to save energy when the CPU is not heavily used. In practice, this means that your "precise" timer will fluctuate in random ways, making it completely unusable.

GetTickCounter is the best choice. Not as precise as QPC, but much more reliable.
There is nothing wrong with multicore systems and QueryPerformanceTimer. There WAS a problem but it was fixed with update from Microsoft (if we are talking about that OS) and AMD/Intel. So don't worry about QPT on Dual Core systems.
Also - don't lock your frame rate, that's pointless (talking about PC games). Just make your movement and animation frame rate independand.
Quote:Original post by Anonymous Poster
There is nothing wrong with multicore systems and QueryPerformanceTimer. There WAS a problem but it was fixed with update from Microsoft (if we are talking about that OS) and AMD/Intel. So don't worry about QPT on Dual Core systems.
Also - don't lock your frame rate, that's pointless (talking about PC games). Just make your movement and animation frame rate independand.


It was fixed? I just googled this topic but didnt see any mention of resolution, but I'll take your word for it. Thanks guys. I learned some neat stuff.

This topic is closed to new replies.

Advertisement