I'm currently using QueryperformanceFrequency/Counter to time my frames.
However, after a frame has been processed and I need to wait x amount of time to produce 60 FPS how do I put the application into wait mode that doesn't use 100% CPU time?
Sleep is too inaccurate. Sleep(1) through to Sleep(10) causes the program to sleep for 10ms and Sleep(11) through to Sleep(20) causes the program to sleep for 20ms and so forth.
This means that Sleep rounds up the 1/1000ms to 10/1000ms which is too inaccurate.
WedNESday wrote:
I'm currently using QueryperformanceFrequency/Counter to time my frames.
However, after a frame has been processed and I need to wait x amount of time to produce 60 FPS how do I put the application into wait mode that doesn't use 100% CPU time?
Sleep is too inaccurate. Sleep(1) through to Sleep(10) causes the program to sleep for 10ms and Sleep(11) through to Sleep(20) causes the program to sleep for 20ms and so forth.
This means that Sleep rounds up the 1/1000ms to 10/1000ms which is too inaccurate.
I had similar issues with Query* API. Then I switched to audio-sample based timing. I'm not trying to say my implementation is perfect...but Sleep, as you point out, is not reliable.
cpow wrote:
I had similar issues with Query* API. Then I switched to audio-sample based timing. I'm not trying to say my implementation is perfect...but Sleep, as you point out, is not reliable.
So how do you manage it?
In fullscreen mode you can achieve a vsynch through your graphics API. Some API will let you wait for vsynch in windowed mode (even though the actual presentation of the video isn't synched).
It's hard to get a precise framerate, but you can use QueryPerformanceCounter to time your frames, and using Sleep when you need to delay the next frame is normally an acceptable solution. Don't expect a consistent timing from Sleep, but obviously you can determine your exact timing with QueryPerformanceCounter and have the program react accordingly. Just accumulate the time elapsed and execute a frame every time it goes over 16.6ms.
The resolution of Sleep varies depending on a lot of factors; it's not always as bad as 10ms. Also try Sleep(0), as it works a little different than Sleep(1). Increasing the priority of your program's thread can help with this problem. Calling Sleep(0) is healthy for a program to do, as it can reduce power usage and/or allow background programs to run, instead of a busy loop which will hog the CPU.
WedNESday wrote:
cpow wrote:
I had similar issues with Query* API. Then I switched to audio-sample based timing. I'm not trying to say my implementation is perfect...but Sleep, as you point out, is not reliable.
So how do you manage it?
I use SDL. The SDL callback is timed based on the number of samples you tell it you'll queue up whenever asked. Then when asked, I provide them. My emulation core hangs on a semaphore when it's generated enough samples. The SDL callback kicks that semaphore. I think the key is to not tie the audio callback rate to video update...don't, in other words, lock yourself into the thought that you *need* to set up SDL to ask you for 735 samples at 44100Hz just so you know that *exactly* 60 frames will go by in a second. I have my SDL callback set at 1024 samples. My emulator core -- though I'm still playing around with this -- generates 2048 samples before hanging. So on a powerful enough* system my emulator will always be one frame ahead.
*My emulator core is trying to be ridiculously accurate and generate a ridiculous amount of debug information for the IDE...so it's a bit of a CPU hog.
What I'm suggesting is something like this:
Code:
Time accum = 0;
Time last = GetTime();
Time FRAME_TIME = 1000 * ms / 60;
while ( quit != TRUE )
{
// execute one frame, remove one frame of time from the accumulated time
DoFrame();
accum -= FRAME_TIME;
// while it is not time for the next frame, allow other programs to execute
while (accum + GetTime() - last < 0)
{
Sleep(0);
}
// account for time spent sleeping and proceed to the next frame
Time next = GetTime();
accum += next - last;
last = next;
}
By keeping a running total of the time spent, you can make sure that even though your frames don't run precisely at 60hz, on average they will. If you try to slow down individual frames without accumulating earlier time, you end up just running at a consistently slower rate.
You may wish to put a clamp on how big the accumulated time value can get. If a frame ends up taking 5 seconds, you don't necessarily want to run at max speed until it catches up. Probably a couple of frames is a good limit for accum. Similarly you may want to reset the accumulator after loading from disk, or other things you know will introduce a long pause.
If you wish, you can change Sleep(0) in there to a Sleep using the expected number of milliseconds you need to wait. As you noticed, it will wait longer than you specified, but the accumulated time will account for that.
In an instance where no other programs were hogging the CPU wouldn't your example still result in 100% CPU usage?
Sleep(0) doesn't necessarily return right away, even if you think no other programs are running, but as I suggested at the bottom it might be better to Sleep with the predicted needed number of milliseconds. In practice the difference between using Sleep(0) in a wait loop and just doing nothing can be huge in terms of CPU resource consumed.
Cpow's solution is also good, waiting on a semaphore for a hardware driven timed signal. If you're synching to audio that requires a working audio signal. APIs for vsynch usually work similarly. There are other kinds of timers you can use to drive your loop. The main thing for maintaining a consistent framerate is to keep track of how many frames you should have executed based on that timer, catch up when behind (if desired), and especially wait up when ahead (preferrably giving CPU back to the system with Sleep or a semaphore or something similar).
I agree that using Sleep(0) is better than nothing at all but doesn't solve my problem. How do PC games achieve 60FPS without 100% CPU usage?
I was trying to figure out how to get 60FPS, Vsync, and not 100% CPU usage, when I noticed that XNA does that stuff automatically, and it all just works. So I guess we have to find out how XNA did it.
Edit: checked out XNA, but the actual code responsible for performing the vblank wait is not .NET code. The last .NET code used is "GraphicsDevice.Present()". Then it calls some native code after that.
Dwedit wrote:
I was trying to figure out how to get 60FPS, Vsync, and not 100% CPU usage, when I noticed that XNA does that stuff automatically, and it all just works. So I guess we have to find out how XNA did it.
Getting VSYNC windowed was gonna be my next question.
Edit: But how can XNA help us with our C++?
If you want to give up some CPU time, I already mentioned multiple ways to do this. Sleep, semaphores, timer callbacks, vsynch. Probably XNA does it via vsynch.
The example code I provided was an example if you want to do it with Sleep.
Regarding
Sleep(0):
MSDN wrote:
If you specify 0 milliseconds, the thread will relinquish the remainder of its time slice but remain ready. Note that a ready thread is not guaranteed to run immediately. Consequently, the thread may not run until some time after the sleep interval elapses.
As I said though, Sleep with the number of milliseconds you'd like to wait might be a little better. In practice, I don't think you'll find much of a difference; either way is fine for giving the CPU breathing room.
The default behaviour in Direct3D is to sleep your thread until vsynch when you call Present. This is customizable; you probably want to use D3DPRESENT_INTERVAL_ONE.
See:
CreateDevice,
D3DPRESENT, and
Present.
This works in windowed mode as well. Note that in windowed mode, it won't prevent screen tearing, as the vsync isn't actually observed for flipping the screen, but it will still wait on Present. (Note also the timing will be a little bit variable; on average it will be called at the specified rate, but individual frames may run short or long.)
Note that the actual rate it locks to is not guaranteed. You can query the framerate you're actually going to get, I believe. Most systems will support 60hz, but you may want to tweak things if you don't get it.
I forget if there's an equivalent way to do this in OpenGL, but there likely is.
So, since XNA is calling Present(), that's the one to use for vblank waiting then.
Then I started doing web searches about present(). I was reading a blog post on virtualdub.org, where the author talks about input events (keyboard and mouse) being horribly delayed by 5 seconds. XNA only calls Present() in an Application.Idle event handler, so this is after the windows message queue is empty, and it avoids that problem.
Code:
onepercent = cpuspeed / 100;
frame = cpuspeed / 60;
runframe();
frame -= frametime;
if (frame > onepercent)
Sleep(10);
This is what I am trying to achieve. This way I believe that my CPU can be put to sleep for at least 60% of its life to save energy.
I've been calling QueryPerformanceFrequency to get my CPU speed in Hz but apparantly what it actually returns is something else?
QueryPerformanceFrequency gets you a value that lets you convert QueryPerformanceCounter values to seconds.
Dwedit, if you have a problem with input not getting through, there are many solutions, and they don't necessarily need to be part of your video or audio synchronization solution, though they can be related to it if you want. If you're comfortable with multithreading, you should take advantage of it (though proper multithreading is a whole area of its own). For example, if you're about to load from disk and basically hang the game for a few seconds, instead of just doing it in your regular loop you can put your main thread into a loop that updates necessary things like the message pump, and spin off all the loading stuff in its own thread.
I'm not having any problems, I'm just saying that the author of a blog post had problems.
But I was tracking down how XNA worked, as well as reading his post about how he was having problems. I could clearly see how XNA was resolving them.
WedNESday wrote:
Sleep(1) through to Sleep(10) causes the program to sleep for 10ms
You can get 1ms resolution by calling
timeBeginPeriod.
James wrote:
WedNESday wrote:
Sleep(1) through to Sleep(10) causes the program to sleep for 10ms
You can get 1ms resolution by calling
timeBeginPeriod.
James if I ever see you in the street, I'm gonna make sweet love to you.

note to self: stay out of England

rainwarrior wrote:
You're a funny little cunt rainwarrior I'll give you that... but I've got no time for comedians...
WedNESday wrote:
rainwarrior wrote:
You're a funny little cunt rainwarrior I'll give you that... but I've got no time for comedians...
Has to be the first time somebody has been called a cunt here. Do you think that's appropriate?
He's sort of quoting Brick Top, from Snatch, which the "don't go to England" clip is from.
http://www.youtube.com/watch?v=XcqXpDwi5jQ#t=0m47s
rainwarrior wrote:
He's sort of quoting Brick Top, from Snatch, which the "don't go to England" clip is from.
Ah, makes sense then. I saw that movie 12 years ago.

thefox wrote:
Ah, makes sense then. I saw that movie 12 years ago.

Do you know what nemesis means?
WedNESday wrote:
thefox wrote:
Ah, makes sense then. I saw that movie 12 years ago.

Do you know what nemesis means?
You lost me.
Go watch the movie again

.
WedNESday wrote:
Go watch the movie again

.
I guess I should have googled that quote too.