Dynamic Buffer Templates in D3D11
D3D11 Vertex Buffer Index Buffer Templates
Last time, I described a few of the new 'immediate' style rendering classes that I have recently added to Hieroglyph 3. Since then, I have refactored some of the common code used in these classes into a handy set of template classes. I have found them very useful already, so I thought I would describe them here. To get started, we'll quickly discuss what design I started out with, and then discuss what I have now.
Hieroglyph 3 uses the concept of a PipelineExecutorDX11 as the interface used to configure the D3D11 rendering pipeline input (through the Input Assembler) and subsequently how to execute the pipeline (via an appropriate draw call). These operations are distinct from the rest of the pipeline configuration (which is handled through a material class) and the pipeline output configuration (which is handled through a 'view' class). Once this concept is encapsulated, it becomes really simple to develop new rendering techniques. For example, trying something out that uses instanced rendering, or an indirect rendering method is quite easy and there is almost no risk of breaking other functionality since you just have to swap out the pipeline executor instance.
By having an object perform the Input Assembler configuration for you, you are essentially letting it perform the mapping between an a renderable object's representation and putting into the pipeline. With this in mind, it probably makes sense to have the same object own whatever buffer resources are going to be used in the implementation of that draw call. Single or multiple vertex buffers, an index buffer, or whatever other type of buffer (like an arguments buffer for indirect rendering) you are going to use should all be owned by the class that is going to use them to configure the pipeline and ultimately do a draw call on them.
Where I was
With this in mind, I implemented three different pipeline executor classes that performed various types of draw calls for my immediate rendering functionality. One used a single vertex buffer, one used a vertex and index buffer, and one used two vertex buffers and an index buffer. While doing this, I found myself copying the code that was used to manage the contents of the buffers. This was a sure sign that I could refactor this code into a reusable component, especially considering that I would be creating other pipeline executors in the future.
When it comes to vertex buffers, you essentially have to create a buffer resource and specify if you want the buffer to be dynamic (i.e. CPU accessible) or not. Then you fill the buffer with data and provide a corresponding input layout and away you go. Since I wanted a simple interface to add vertices to my buffer, I provided a system memory copy of the buffer that can be manipulated by the user. That copy is later loaded into a mapped vertex buffer and used for rendering.
Where I am Now
To keep the interface easy to use, I wanted to be able to add one vertex at a time. However, I wanted this reusable component to be able to handle multiple types of vertices, especially the types that I don't even know about right now. So I came to the conclusion that this component would be a template class, allowing me to provide a simple addVertex( T vertex ) method. Also, dealing with the system memory copy of the classes was also quite easy. The template class covers the management of the buffer, and the template argument provides the custom vertex layout and size - all quite convenient.
The only issue that I encountered was that I couldn't automatically provide an input layout for the template class. In the end, I decided this was ok since whatever class is instantiating the template would have to know what type of vertex it was requesting anyway, so the owner of the template class would need to provide the input layout. In my design, this means that the pipeline executor instantiates the template and creates the corresponding input layout - effectively providing everything that it needs to get its job done.
Wrapping It Up
In the end, the addition of these template classes greatly reduced the amount of code for the pipeline executor classes, commonized their buffer management code (good for maintenance and debugging), and reduces the amount of time needed to create a new one. All of these are good things, so if they sound good to you too, go check out the Hieroglyph code base!
A Personal Note
I also received my Microsoft MVP Award for DirectX in 2012 on October 1st. I am very grateful to have the chance to continue on as an MVP awardee, so thanks to Microsoft for continuing their recognition program!