Sign in to follow this  
fpsgamer

Locking Frame Rate without busy waiting

Recommended Posts

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?

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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?

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
Guest 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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Well, uhm, no. Microsoft did in fact release a fix (and so did AMD), but it is far from perfect. A lot of people still report sporadic weird behaviour and timing anomalies when using QPC on AMD multicore system, especially when cool'n'quiet is enabled. The only thing that seems to work 100% is manually setting the affinity of the process to a single core. Which is a bad hack with bad side effects. Reportedly Windows Vista resolved the issue.

Oh, and it's QueryPerformanceCounter...

Share this post


Link to post
Share on other sites
Actually, Microsoft's recommendation isn't to set the whole process to a single processor. It is to create a seperate timing thread and lock that thread to a single processor. That way you are at least getting timings from a single processor, eliminating the posibilty of getting a drastically different tick value if the thread were to jump from one processor to another.

Share this post


Link to post
Share on other sites

>It was fixed? I just googled this topic but didnt see any mention of
>resolution, but I'll take your word for it.
Yeah, it was apparenty fixed in windows xp (and what about windows 2000)?
And judging by the amount of questions concerning timers on the gamedev.net
forums, the big majority of the people doesn't have this fix installed
(and if even the tech-guys here don't have it, probably the end-users
don't have it either)


>Actually, Microsoft's recommendation isn't to set the whole process to
>a single processor.
I don't think having a thread where the timer is running is a good solution either. Either your thread will not be called enough (who will guarantee
you that this thread is called at least once per frame - and what about
intraframetimes, you sometimes need these)...
Or maybe the thread is locked to one cpu which happens to be also
compressing a zipfile or sth cpu-intensive, without having the option
to run on the other idling-cpu
Or this thread might even suck away cpu time because it's
running on a higher priority (to get enough updates).

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this