Getting Rid of the Windows Message Pump
Glorious were the days when we programmed our games in a mostly linear fashion. We didnít have to worry about multitasking, DirectX or message pumps. We just programmed games.
It was a very big jump to port our creations to DirectX, but that was the way that the market was leading. Most importantly, you could achieve a performance that could only have been dreamed without the hardware acceleration and tuning that the DirectX team provided.
But there was this big problem: the Windows Message Pump. It is simply not adequate for game programming. In this article I propose to present an effective way to encapsulate the message pump, provide a linear programming model and, as a very desirable side effect, allow correct ALT-TAB application switching.
The Message Pump
The Windows operating systems demands that our application constantly check for inbound messages for all our members (windows, buttons, listboxes, or whatever). The Windows Messaging System is the heart of the cooperative model that the Microsoft team adopted. We canít just simply forget about the message pump, because it informs us of important things like ĎYouíve been deactivatedí, and ĎYouíve been reactivatedí, among many others. A typical Windows application has the following basic layout:
All the applicationís operativity is defined in the memberís methods, and in fact, itís a very comfortable programming model to make simple windows applications. But itís simply not very adequate for games.
The most used technique to start making games under Windows is to replace the GetMessage() function, which blocks the execution thread, with a couple of functions. PeekMessage() will check if any messages are waiting, and the PM_NOREMOVE parameter tells the function not to remove the message from the queue. If this function returns true, the GetMessage() function can then be used to retrieve the message. Otherwise, if no message is to be processed, we can call our UpdateWorld() function, that will be in charge of updating all the world variables and rendering the new scene. The following code shows this:
This basic Message Pump can be optimized by inserting a flag that indicates whether the game is active (i.e. not minimized), and, if we are not active, instead of updating the world, we call the WaitMessage() function, which will set the application in an idle state until a new message arrives.
Updating the World
Our next step is to code the UpdateWorld() function. This should be very simple; all your application variables, surfaces and interfaces have already been initialized, and now you just have to update and render them. Thatís an easy task.
Yes indeed, itís an easy task, but only if you plan that your game will have only one screen, no menus, no options, and nothing.
Letís suppos that we wanted to build a simple DirectX application that shows a splash screen, then goes into the game, and then shows the same splash screen again.
In the most traditional linear programming way, we would do something like this:
Now, with our UpdateWorld() function, we will have use a finite state machine instead:
And not only this, but we will also have to define our Splash() and PlayTheGame() functions to return a finish state when they have finished. Now suppose that the Splash() function performs a FadeIn() and a FadeOut(). Splash() would look like this:
Note that we must set the state to 0 when we finish the last step, because we must be able to invoke this function again later in the code. If we are resetting the state, then we must inform our invoker that we have finished, and that he can continue to his next state.
Well, what we have just done is just a very simple application. Now imagine thirty or forty of these functions, each one with a couple dozen states - as I had to do to port Malvinas 2032 from DOS4GW to DirectX - and you will be facing a very big monster. Try to debug, or even follow this code, and you will get really insane. As you can see, this finite state programming model is far more complicated that the simple one achieved by old linear programs.
To solve this, I developed a fairly simple multithreading model, that frees the game's programmer from the message pump and its undesirable finite state programming model.
Fortunately, Windows supports multithreading. This means that our application runs several simultaneous execution threads. The idea is very simple: put the message pump in one thread, put the game in another thread.
The message pump will remain in the initial thread (being the initial thread does not imply that it has more priviledges or importance). We can thus remove the UpdateWorld() function from the Message Pump and return it to its simplest form.
Now, we just need to add the code necessary to initiate the game thread to the DoInit() function.
And MainThread() is defined by:
Now, here we are. MainThread() will invoke our RunGame() function, and when it is finished, we just post a WM_CLOSE message to tell the other thread to finish execution.
Making a game truly multitasking under Windows is perhaps one of the most hazardous issues in game programming. A good application must be able to correctly switch to another application. Some games simply wonít allow the user to ALT-TAB at all. We will try to do it the right way.
As a first attempt, we can try to use the SuspendThread() and ResumeThread() methods in the WindowProc() function (the one that effectively handles the messages), but, although Iíve tried and tried, I've never been able to get them to work. Sometimes, the thread was successfully suspended, but it would never get back to life. If someone has been able able to get this approach to work, I'd love to know what you have done.
What Iíve done to solve this, with very positive results, is to implement waiting code in my FlipSurfaces() function (since it is constantly being called while the game is running). I declared a global bSuspended variable (globals have multithreaded scope) to inform the game thread that the game is in suspended state. Then Iíve inserted this code in FlipSurfaces():
Itís quite simple and easy to implement, and, most important, it always works. You just have to set the bSuspended global in the WindowProc() function every time that the application is activated or deactivated. Just remember to restore lost surfaces, and you are ready.
With this simple implementation, when the user ALT-TABs, the game will stop its execution, freeing all the processorís time to enable the user to do other tasks. If your game needs to not be suspended, just suspend the rendering pipeline, and keep on updating the world.
I hope that you liked this article, and that you find the linear programming model as enjoyable and rewarding as I do. You can contact me at firstname.lastname@example.org with any comments, criticism, queries or suggestions about this article. I am really curious if anyone here likes the finite state machine programming model using the message pump.
Javier F. Otaegui is the CTO and Lead Programmer of Sabarasa Entertainment, a game development company of Buenos Aires, Argentina. Sabarasa Entertainment has successfully finished and launched a RTS game called Malvinas 2032 in his country, and will start US and another countries commercialization soon. Current projects include a wide variety of things, among them 3d landscapes, online multiplaying and highly-addictive gameplay. Please check the company website for more information www.sabarasa.com
© 2000 by Javier F. Otaegui Ė email: email@example.com