Sponsored Feature: Designing the Framework of a Parallel Game Engine
February 25, 2009 Page 5 of 6
The object component, also called a system object, is an object within the scene and is typically associated with what is visible to the user on-screen.
The universal object uses the system object as an extension of its functionality, described in "Universal Scene and Objects", to allow the properties this object provides to be exposed via the universal object. For example, a universal object could extend geometry, graphics, and physics to create a beam of wood on-screen. The geometry system would hold the position, orientation, and scale information of the object. The graphics system would display the beam on-screen using the given mesh, and the physics system would apply rigid body collision to the beam so that it would correctly interact with other objects and with gravity.
In certain situations a system object may be interested in the changes of a different universal object, or one of the universal object's extensions. In this case a link can be established so that the system object can observe the other object.
The task component, referred to as a system task, operates on the scene. When the task receives a command to update from the task manager, the task performs the system's functionality on the objects within the scene.
The task can also choose to subdivide its execution into subtasks and schedule the subtasks with the task manager for even more threaded execution. Doing this allows the engine to scale more readily to a configuration with multiple processors. This technique is known as data decomposition.
During the task's update of the scene, any modifications done to its objects are posted to the state manager.
Tying It All Together
The engine execution can be broken up into several stages: initialization, scene loading, and game loop.
Engine execution begins by initializing the managers and the framework.
1. The framework calls the scene loader to load in the scene.
2. The loader determines what systems the scene is using, and then calls the platform manager to load those modules.
3. The platform manager loads the modules, passes in the manager interfaces, and then calls into them to create a new system.
4. The system module returns a pointer to the instantiated system that implements the system interface.
5. The system module registers any services it provides with the service manager.
Figure 10. The engine manager and system initializations.
Control returns to the loader, which loads the scene.
1. The loader creates a universal scene and calls each system interface to instantiate system scenes, extending the functionality of the universal scene.
2. The universal scene checks each system scene for any shared data changes they could make and for shared data changes they want to receive.
3. The universal scene then registers the matching system scenes with the state manager so they will be notified of the changes.
4. The loader creates a universal object for each object in the scene and determines which systems will be extending the universal object. The universal object follows a similar system object registration pattern with the state manager, as occurs for the universal scene.
5. The loader instantiates system objects via the system scene interfaces it previously received and extends the universal objects with the system objects.
6. The scheduler then queries the system scene interfaces for their primary tasks because the scheduler is responsible for issuing the primary tasks to the task manager during execution.
Figure 11. The universal scene and object initializations.
The main game loop begins processing.
1. The platform manager is called to process all window messages and other platform-specific items that are needed for operation on the current platform.
2. Execution is then transferred to the scheduler, which waits for the clock time to expire before proceeding.
3. The scheduler, for free step mode, checks which of the system tasks completed execution in the previous clock. All tasks that have finished (that is, they are ready to execute) get issued to the task manager.
4. The scheduler now determines which tasks will complete on the current clock and waits for completion of those tasks.
5. For lock step mode, the scheduler issues all tasks and waits for them to complete for each clock step.
Execution is transferred to the task manager.
1. The task manager queues all submitted tasks and starts processing each one as threads become available. (Task processing is specific to each system. Systems can operate using only one task or they can issue more tasks which get queued in the task manager, thus potentially getting executed in parallel).
2. As tasks execute they operate on the entire scene or on specific objects and modify their internal data structures.
3. Any data that is considered shared, such as position and orientation, need to be propagated to the other systems. To do this, the system task has the system scene or system object (whichever was changed) inform their observer of the change. In this case the observer is actually the change controller located in the state manager.
4. The change controller queues the change information to be processed later, but change types that the observer is not interested in are simply ignored.
5. If the task needs any services it goes through the service manager to call into the provided service. The service manager can also be used to change the property of a different system that isn't exposed via the messaging mechanism (that is, the user input system changes the screen resolution of the graphics system).
6. Tasks can also call into the environment manager to read environment variables, and change the runtime state (pause execution, go to next scene, and so on).
Figure 12. The task manager and tasks.
Once all tasks targeted for the current clock have completed execution, the main loop calls the state manager to distribute the changes.
1. The state manager calls each of its change controllers to distribute the changes they have queued. This is done by going through each subject's changes and seeing which observer was listening to that subject.
2. The change controller then calls the observer informing it of the change (a pointer to the subject's interface is also passed to the observer). For free step mode the observer gets the changed data from the change controller, but for lock step mode the observer queries the subject for the data.
3. The observers that are interested in the changes done by a system object will typically be other system objects that are attached to the same universal object. This makes it possible for the change distribution to be broken up into tasks for execution in parallel. To limit synchronization, any universal objects' extensions that are linked should be grouped together in a task.
Runtime Check and Exit
The final step of the main loop is to check the runtime's state, which might be run, pause, or next scene. If the runtime state is set to run, it will repeat the entire game loop. If the runtime is set to exit, it exits the game loop, frees up resources, and exits the application.
Page 5 of 6