The Passage
Programming Title

December 22, 2011
Michael Schoell

One of the most recent upgrades to DarkForge has been a massive overhaul of the scene graph management system. It was always a clunky system for the user to setup objects for rendering with a long list of steps that needed to be made with the hopes that you did not accidentally mess up another object. Before I begin, I'll define a few terms.

  • Render Object - Every time the user wants to render another object, even if it's visually the same as another, you create a render object. Simply it stores the unique position of the object, along with all the other objects that dictate what is being rendered and how.
  • Texture - As you can imagine, this object is the instance of a texture in the scene. Each texture that is loaded up is an instance.
  • Render Context - This object holds the shaders and various other video card related data to render the render object.
  • Mesh - Whatever mesh you want to render. It holds bounding volume, the collision mesh, and the rendered mesh data. Later it will contain more advanced data to facilitate level of detail.
  • Scene - The scene is a collection of all the objects to be rendered from a specific camera view. One main scene is used for the world, for all the visual objects that the player will see. Most of the time if you want a different view, change the camera, not the scene. Other scenes would be used for holding meshes to receive shadows, there the scene is rendered from a different perspective and some objects may not be desired to have shadows for performance.

Now, before this overhaul, creating a mesh would require the user to create a texture for the mesh being used. Then set in that mesh the render context. If you wanted a different context for an object using the same mesh, you needed a new instance of that mesh. The complexity of where to set objects and when you can and cannot, made the system clunky. It was easy to change more objects than you desired to a new render context or texture.

One reason for this was that the objects that contained the textures, render contexts, meshes, and render objects, were themselves the objects that formed the scene graph. They would link themselves to their parents and they could only have one parent at a time. If you changed their parent, all children would receive this change. This would have also led to problems of multiple scenes and thus, multiple scene graphs.

My systems always have one major criteria for pursuit: speed. The rendering pipeline thrives on efficiency. I have found that even with the multithreading, which has allowed me to triple the number of objects being rendered at similar frames per second, DarkForge is still CPU bound. On a six core processor, I have found that three instances of the same project (rendering 10,000 objects, 100 lights) did not make any of their frames per second suffer. Since each instance isolates itself to separate cores, neither is slowing the others down on the CPU, however the single GPU would be receiving commands from all three. That would amount to 30,000 objects and 300 lights at 80 FPS.

This has made me conclude that the GPU is still idle too often. There is little I can do to speed this process up, DirectX suffers from it's own performance issues and the rest would lie in hard to find code optimizations. The device thread, dedicated to only processing GPU commands, should already be well design for cache hits.

None of this is that important however, it's beyond the scope of priorities currently. I only brought it up because I do not want to slow the pipeline down by adding a system that must use up a lot of cycles. With these issues in mind, I restructured the code.

Render objects now are told the render context, texture, and mesh used for rendering. Changing the data for one will have no effect on another. These objects also no longer contain scene graph information and are parented in no way. The scene graph comes in when the object is added to a scene. Using the relationship between child and parent, I create a hash and look up the object in a hash table.If it finds a current instance of the object, it will simply use it as a parent. If not found, it creates one and adds it to the hash table.

With this system, any time data changes in the render object (such as what texture to use), the scene will just re-hash the object to find the new parent. Objects will share parents only if it is the correct object. Speed-wise, the only new overhead is when objects are added, modified, and removed. That should not happen every frame, and even if some objects are changed that often, it should not be a major performance hit.

DarkForge advances to match my needs. If this system proves to be poor later, it will be remade to match needs. When needs change, the system will change to try and match new and old needs effectively. It must perform well under a variety of conditions with little user hassle.

Here is a small list of future tasks I am working towards with DarkForge:

  • Shadow Mapping
  • Normal Mapping
  • Distant Light Culling
  • Depth of Field
  • Level of Detail

These are some of the major focuses, though I have some personal game projects that do help direct features for DarkForge, such as the above scene graph changes.

Site Development and Design by <CS>

Graphic Design by Nathan Schoell