Game Loop – A little story
Posted by feal87 on May 29, 2009
When I look back at the almost three months spent on the game engine I’ve developed, I remind of the various problems and trials I had to resolve at each stage. One of the most interesting thing I think I’ve worked on was the designing of the game loop.
Well the design at first was “quick and dirty” and slowly evolved into something more advanced. Let’s go back and see the steps that have taken the game engine to what it is now and while at it, let’s look at the various type of game loops I’ve used. (I will NOT delve into the depth of programming, but leave it as a simple theorical reading)
When i first started tinkering with SlimDX, I started by using a slightly modified version of their Sample Framework. It is basically a simple framework that imitate the behaviour of XNA. Their game loop was a very simple single-threaded one.
The thing that i didn’t like of it was that it was based over the Application_Idle event to trigger the drawing of the frame, making the engine quite unreliable and generating useless trash work over the Game Clock. I quickly worked over it making it look like a classical game loop of old days…
The game loop repeated until the game ended its execution, updating the game logic and drawing frames as fast as it can. (if not blocked by the VSync)
While this kind of game loop was ok for demos and simple app, it was quite unefficient for games that needed correct timing and performance.
However I remained stuck to this kind of game loop for quite a while, until one day i received a mail from the Intel Visual Adrenaline newsletter containing a paper over multithreaded game engines.
I thought it would be nice to implement a multithreaded game engine, especially in these days of multicores CPUs. (While I’ve not followed any of the guidelines of the paper )
I started working on the Multithreaded game engine and after a day or two…
The new game loop contained several blocks. Let’s examine them all :
1 ) Just like the old game loop the first things it does is to process all the windows messages still in the queue.
2 ) Check if the engine is still active and if not, just sleep for 20 ms. This part of the engine was quite necessary because i felt that using CPU cycles while the application was minimized or paused was quite useless.
3 ) The third step is “Reset the devices if needed“. This is a needed feature if the user try to change resolution or the device is lost. (As we know Reset must occur in the same thread of the message pump and of the creation of the device)
4 ) Update the engine clock to have a correct timing inside the game.
5 ) The “Process all internal messages” phase is a particular one. I thought it would be needed to execute some operations serially so i gave the opportunity to queue inside a personal message pump some delegates to be executed at the next frame.
6 ) Finally we are at the most important step of the game loop, “Schedule all the tasks available for the engine“.
Before examining this step let’s see what is a task. A task in my engine, is a class that execute a particular objective (be it Draw the frame, update the game logic, calculate physics, etc…) at a certain rate calculated in ticks in a personal thread. For example we could have a Draw task that runs as fast as it can, and an Update task that runs maximum 60 times per second. The engine at this phase contained 3 basic always-active tasks that were the Draw Task, Update Task and the Audio Task.
As you can now imagine, this step schedule inside a particular Task manager class all the default tasks and all the tasks defined for the specific application. (for example an user can create a Physics task if needed for its app).
7 ) In the seven step we “wait for all the tasks to be completed“. (This is called lock-step mode in a game engine)
8 ) Synchronize data between tasks. In this step the Synchronization manager send notices of data changed to the various subscriber tasks.
9 ) Finally we go to the last step when we check if the game is ended or not.
As you can see, this kind of game loop was quite a step forward permitting control over timing and performance and on a side note allowed the game to scale over multiple processors easily.
One problem remained in my mind…what if the application needs more than one device? (like a level editor program with inside a character editor in another window)
There was two ways to resolve the issue :
1) Use multiple swapchains on the same device.
2) Reorganize the game loop to support multiple instances of the game engine.
I decided for the latter and started reorganizing the game loop to handle this new situation and I finally got to the actual form of the game loop :
I hope, this reading has been useful, I’ve still not decided what the next post will talk about, but I think it will hopefully be about code practices so look forward to it.
See you later.