Shape Rendering Sample
The Shape Rendering Sample demonstrates a method for adding debug shape rendering functionality to a game.Sample Overview
Games frequently need to add visual debugging information to help identify bugs or to verify that certain parts of the code are working correctly. This visual debugging is used to show bounding volumes, as well as other primitive shapes such as lines and triangles. The Shape Rendering Sample provides a DebugShapeRenderer that allows games to easily render debugging shapes.
How the Sample Works
Using DebugShapeRenderer is a simple process. To begin, the game calls the Initialize method, passing the GraphicsDevice from the game so that the renderer can create the objects necessary to perform the drawing. Next, the game code uses various Add* methods to add shapes—with or without a lifetime—to the renderer. Shapes that are not given a lifetime are rendered for just one frame. Shapes added with a lifetime are maintained for a period of time, and are drawn each frame until the shape's lifetime is met. Finally, the game calls the Draw method to handle rendering all shapes in the renderer.
Adding Shapes
The DebugShapeRenderer is not an immediate mode drawing system. While this may seem less intuitive, it adds flexibility when writing game code because you can have any part of your game call an Add* method to add a shape to the renderer, even if the code is executing in the Update method. This is useful because it does not force you to add Draw methods to all of your objects or to expose the internals of your objects. You can simply call Add* from your object whenever you want; the object appears the next time you call Draw on the renderer.
The renderer also tracks a lifetime for each shape added. While many scenarios are fine passing 0 or using the overload that does not take a lifetime, there are times when you have a single event but want to show debug output. One example is firing raycast bullets in a first-person shooter. If you drew the bullet for just the frame it existed in, you would never see the line. With the lifetime, you can add the line to the renderer for multiple seconds, giving you time to look around and investigate if the line looks as you would expect.
Batched Rendering
The renderer avoids garbage and provides an efficient rendering scheme for debug shape rendering. The debugger manages a list of private DebugShape objects that get cached and recycled to avoid unnecessary allocations.
The renderer also works to combine all shape rendering into as few draw calls as possible. The renderer adheres to the Reach profile primitive count limit of 65,535 primitives per draw call. Games that do not need more than that many lines at once see only one draw call for all shapes in the renderer. The renderer only reallocates the vertex array when the number of vertices required exceeds the array. In this situation, the renderer doubles the new length, giving it headroom to avoid allocating the arrays as often.
Extending the Sample
The DebugShapeRenderer class currently only implements Add methods for lines, triangles, bounding boxes, bounding frustums, and bounding spheres. Because of the way the class works, it should be relatively straightforward to extend the class to use Add methods for other shape types.