I do still strongly disagree with "render as fast as you can" in most cases, especially over the limit of your updates.
I don’t know what you mean by the limit of your updates. It may help if we all use a consistent set of terms: Render Update, Logical Update, and Game Loop. I assume you mean a logical update.
#1: I disagree with “strongly disagreeing to it”. I agree with “giving the player options”, and what you suggest is “taking away options”. In fact, the game engineers typically try to max out the CPU’s performance, using all available resources for whatever they can. And if you are playing a game, the performance of the rest of the applications on you machine don’t really matter unless the game is minimized, in which case yes, I do wait 10 milliseconds between game loops to give back the CPU power. As far as real-time caps go there are 2 things to consider:
-> #a: Too much power can overheat the system and fry parts. So the motivation for a cap is not related to refresh rates or starving other applications etc., it is about not frying the system.
-> #b: Therefor any cap at all should be based on getting the maximum performance out of the CPU without physically killing it. Which is extremely rare these days, and there are often system-wide settings the user can enable to prevent this.
Do not force a cap on the user unless it is in the multiple hundreds of FPS’s such that no human eye can detect the difference. There are plenty of things people can do themselves, if and only if necessary, without you forcing it on them.
Specifically, do you know of another way to sleep your threads on windows that allows you the time granularity needed for games (serious question, I'd love to know because I'd rather not have to waste any extra CPU time with a lower timer resolution myself).
Your question is deceptively broad, so there are many things to say in reply.
The first thing that needs to be made very clear is that there are 2 types of waiting:
#1: Waiting for a given amount of time. This is called “sleeping”.
#2: Waiting for an event to happen. This is called…“waiting”.
These are 2 distinct states for a thread—waiting actually
does put the thread in the most efficient state for CPU usage, sleeping still uses cycles based on the granularity of the timing system. Additionally, waiting has the (almost) exact “granularity” of for whatever event it is waiting, while sleeping has only the granularity of the system timer.
In short, you always want to wait when possible, not sleep.
With that made clear, and then to restate your question as, “Is there a better way to
sleep for a more accurate time?”, the answer is No. Which makes it easy to draw the wrong conclusion—it would be easy to misunderstand and decide, “Then I guess that’s that—increase the timer resolution and sleep.” Do draw the correct conclusion, we need to keep deducing.
Sleeping does not offer a reliable granularity of down-time. Thus it should never be used in a system that needs fine granularity.
The game loop is one of the more core features of the engine/game, and anything you do there has a cascade effect down to all of the other parts of the engine. The only systems that should ever sleep (and yes, it does have its place) are systems in which granularity is not so important. Background threads loading resources. The sound thread (as a wake-up call when it has not been awakened by the game thread for too long and sound buffers need to be updated). Etc. These things aren’t thrown off by the timer granularity, and as a general rule of thumb: “If you need to call timeSetPeriod(), you are doing it wrong.”
The game loop should require much finer granularity and reliability, this waiting is the correct solution.
As wintertime mentioned, one related function is WaitForSingleObject().
However, for what object would you wait? You don’t have access to the object that triggers an event on every v-sync, but that would obviously be your choice of object for waiting.
Luckily, it just so happens that with v-sync enabled it will automatically wait on that object
for you.
I already said this in my previous post, but iOS has CADisplayLink, consoles have v-sync events you can register, and Windows has simply an internal even/trigger that you can’t access directly, but will be don for you if you simply enable v-sinc.
The reason I did not just tell the original poster to Sleep( 1 ) or such is because I am sure the original poster knows that he wants to give resources back to the CPU (though even that is debatable), but he likely does not understand “sleeping” and “waiting”, and has mistakenly made the assumption he needs to sleep to accomplish this.
As far as the main game loop is concerned, the correct way to give resources back is to wait for v-sync.
sadly WaitForSingleObject appears to also be dependent on the timer resolution specified by timeBeginPeriod().
No, only the time-out period is.
L. Spiro