6

Every game tutorial and game framework (even the rather new XNA framework) start off with a never ending loop that has an equivalent of DoEvents() to prevent the OS from locking up.

Comming from a non-game based perspective I feel this kind of code smells rather funky.
Are there no better alternatives?

--EDIT--
A lot of answers say every program is basically a loop. True, but I feel the loop should be performed by your OS, not by you. Only the OS has all the information it needs to distribute its resources in an optimal way. Or am I missing an important point here?

Adrian McCarthy
  • 45,555
  • 16
  • 123
  • 175
Boris Callens
  • 90,659
  • 85
  • 207
  • 305

9 Answers9

5

Every Windows app has at its core a loop that looks something like this:

BOOL bRet;

while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0 ) 
{
   if (bRet == -1 )
   {
      // handle the error and possibly exit
   }
   else
   {
      TranslateMessage( &msg );
      DispatchMessage( &msg );
   }
}

It's the application's job--not the operating system's--to ensure that messages are dispatched.

As you probably know, early Windows games used an alternate method where, instead of calling the blocking GetMessage function, they'd call PeekMessage, and then call the game's main processing loop if there was no message to handle. Various forms of delays were used to try to get an adequate frame rate without taking 100% CPU. There just wasn't a good enough timer to give a smooth frame rate.

Today, it might not be necessary to explicitly write a loop that calls DoEvents. It might be possible now to get a smooth frame rate by using the built-in timer pool timers (exposed in .NET by System.Threading.Timer, and wrapped by System.Timers.Timer).

As I recall, there were also issues with getting mouse and keyboard events in a timely manner. We used direct keyboard and mouse access rather than depending on the message loop, because the message loop was often too slow and sometimes would cause us to lose information.

I've not written games in a number of years, and I don't know what .NET is like as a games platform. It's possible that input is still a problem--that the message queue simply isn't fast enough to give the lightning-quick response that game developers want. So they bypass the message queue for critical tasks.

Jim Mischel
  • 131,090
  • 20
  • 188
  • 351
  • I was actually thinking if it would make sence to make a timer that knows the frequency of the screen to create frames. In that way on unnecessary runs ever get made. Also the idea coined by @Bryan Parker to have the drawing in a separate thread to pull it loose from the rest(game logic/IO)... – Boris Callens Mar 25 '09 at 13:18
  • ..sounds like a nice idea. I don't know if it is pratical though. – Boris Callens Mar 25 '09 at 13:19
  • 1
    Most games have rendering in a separate thread now. And you want to be able to decouple your updates from vsync for performance testing (among other reasons) – BigSandwich Mar 25 '09 at 22:14
  • You don't want your game updates to be dependent on screen refresh rate - game logic should run on predictable wall-time, decoupled from screen painting. – snemarch Mar 26 '09 at 11:38
  • 1
    Yes, I agree, but most game engines today have the screen refresh rate and the game logic tied up, that's why you have to multiply your velocities by Timer.timerDelta or something like that. – Daniel Rodriguez Nov 27 '09 at 13:56
  • Thread pool timers are useless for things that need to synchronize with the UI. Why not the WinForms timer (which sends `WM_TIMER`) -- that integrates beautifully with the message loop. – Ben Voigt Dec 05 '13 at 20:51
2

Games (in most cases) are simulations. Traditionally this means you update the simulation, present its current state (eg. render graphics), and repeat. The natural representation for this is a loop.

The Win32 message pump on the other hand is a platform specific quirk, geared towards the event-driven applications that make up the majority of apps on Windows. It's by no means the standard for applications across all platforms so it's not surprising that not all software fits nicely into the model. So to amend a typical program to fit the Win32 model, you typically drain that queue in one go, once per iteration of the loop, using PeekMessage until it's empty. Or, you put that logic into a separate thread and use GetMessage as appropriate.

For most games with performance in mind there is no other practical way to do this. Firstly, if you tried to make the game event-driven instead of polled you'd need higher resolution timing than Windows can reliably give you if you want to keep the high performance that many games go for. Secondly, Windows is only one of the platforms that games are written for, and reworking the game to fit nicely into the Win32 model will just be an inconvenience for dedicated game platforms which expect the canonical game loop instead.

Finally, concerns about 'taking 100% CPU' are misplaced. Most modern games are designed to be used in fullscreen mode with exclusive hardware access, not to be just one of several other applications co-existing. Customers of such games actually demand that the game makes the most use of their hardware and this cannot be done if there are deliberate Sleep() calls or updates are dependent on external timers waking the application N times a second. Obviously there are exceptions for many games, eg. those that are designed to run primarily in a window, but it's important to note the distinction.

Kylotan
  • 18,290
  • 7
  • 46
  • 74
  • Taking 100% CPU is most certainly a valid concern and something to be avoided. Even when you are fullscreen foreground, you don't want to waste CPU cycles. It interferes with power management more and more with every passing year. When you wrote your answer, it would only have been a concern for laptops (which is still reason enough not to burn CPU). Today, it will kick a CPU core into Turbo Boost, decreasing power available to the integrated GPU. So your idea of hogging all the resources for your game actually diminishes its performance. -1 – Ben Voigt Dec 05 '13 at 20:50
  • @BenVoigt - That's a valid point but it depends on what platform you're targeting. AAA games that aren't targeted exclusively for mobile or laptop platforms will not be deliberately introducing sleep cycles for any reason and will scale up processing to fill available CPU resources. More pointedly, they won't be expected to produce good performance on integrated GPUs - they are second-class citizens as far as game software is concerned. But if you're aiming at mobile... that's a different story. – Kylotan Dec 10 '13 at 11:02
1

All programs that are running are never ending loops. Though in some cases you don't interact with the loop (like in web programming or other minor programs)

Your OS is a loop waiting for input commands. Same with a game. A web server is a loop waiting for requests.

A great explanation is from the user slim on one of my own questions.

Community
  • 1
  • 1
Ólafur Waage
  • 68,817
  • 22
  • 142
  • 198
  • You are basically right, although this is only true for GUI-based programs. for trivial and command line applications you can easily get away without a main loop. – Adrian Grigore Mar 25 '09 at 10:05
  • Read your post and indeed I figured that. But since the OS is already doing this loop, is it not a good idea to hook into that loop (use the OS's events) rathern then create our own loop in the OS loop? – Boris Callens Mar 25 '09 at 10:20
1

The better loop is an endless loop with blocking call to dispatch events/messages. If there's nothing to be done, you should indeed not sleep, but block while waiting for the OS. But if the OS indeed doesn't block when you poll for events, sleeping between polling is the only solution to prevent 100% CPU use.

MSalters
  • 173,980
  • 10
  • 155
  • 350
1

I don't see how it could be done much differently?

Your program execution happens inside a thread (or multiple threads if you're fancy). Having a gameloop (or a win32 GetMessage/DispatchMessage pump) is all about waiting for event to happen and then handle them. The alternative would be registering callbacks, then calling an HandleAllMyMessages(), which would internally do pretty much the same thing... and that wouldn't really buy you anything.

The OS isn't going to be able to magically split your program into multiple threads, since it has no knowledge of what you want to do within those threads. Spawning a new thread for every incoming message would be both performance and synchronization hell.

snemarch
  • 4,958
  • 26
  • 38
1

On Win32 I use a MultiMedia timer to call my gamelogic tick...

// request 1ms period for timer
g_mmTimer = timeBeginPeriod(1); 
if (g_mmTimer != TIMERR_NOERROR)
    return false;

//start game loop event timer/thread
g_mmTimer = timeSetEvent(10, 10, DoUpdate, 0, TIME_PERIODIC );

Optionally, you can do all the drawing in another thread so that the game logic is automatically independent of frame rate.

Bryan
  • 8,748
  • 7
  • 41
  • 62
0

MsgWaitForMultipleEvents combines GetMessage/PeekMessage functionality with a timeout and the ability to wait for kernel objects, such as a WaitableTimer.

I've used CreateWaitableTimer to set up my desired frame rate together with MsgWaitForMultipleEvents for message dispatching with great success for very smooth animation in the past, without busy-waiting. I wish other OSes had anything so nice.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
0

Message Queues are Infinite loops as well which makes all the programs as Infinite Loops.

Aamir
  • 14,882
  • 6
  • 45
  • 69
-3

if you not feel comfortable about it, package it with class and interface,

that is what we say "open and close principle": open the interface never changed, close the dirty part.

why we use loop? because it is the basic structure of our computer!

linjunhalida
  • 4,538
  • 6
  • 44
  • 64
  • I fear there is a miscommunication. I know how I could hide all the ugly bits, but the question is why should we implement a loop within the OS loop. – Boris Callens Mar 25 '09 at 10:22