Managing Rendering Parameters
All rendering frameworks need to define and manage rendering data that is used to configure the rendering pipeline. The simplest example of this is an object supplying its world transform before it is rendered so that it appears in the right place in a render target. Of course, there are many more complex types of rendering data such as resource objects (i.e. textures/buffers) matrix arrays, samplers and so on. This is a non-trivial task if you want to make your framework flexible enough to handle data driven rendering configurations, since the engine won't know in advance which specific rendering parameters are going to be needed or used.
Hieroglyph 3: The Current System
Hieroglyph 3 uses a ParameterManagerDX11 class to provide a way to communicate rendering parameters from many sources to the rendering system. The data can come from the application object itself, a 3D entity that is being drawn, a material, or a render view (a render view is just a scene level rendering operation in my engine). All of the data that is available from these sources is written into a parameter system instance, which then stores the data for use by the rendering system. When it is time to render, each shader that is being used will request the needed data from the parameter manager and use it accordingly. Resource objects will be bound to the appropriate pipeline locations, vector and matrix data will be written into constant buffers, and so on until all of the needed data is bound to the pipeline. The pipeline can then be executed (with an appropriate draw call) to render the current object. Multiple instances of the ParameterManagerDX11 class can be created and chained together to allow for a complex system of parameters that is used across multiple threads - meaning that any changes to the classes have to be carefully considered in the grand scheme of things...
During the development of some of the samples for our book, we found that there was lots of time being spent in the parameter manager classes. In fact, in some scenes there was up to 60% of the frame time was spent in these functions. This can be a bit misleading since samples typically don't do much else besides rendering, but it is still something to be improved upon. The existing implementation basically just uses a std::map<std::string, RenderParameterDX11*> to store all of the parameters, where the string is the name of the parameter. This is clearly less than efficient, since using a string as the key to a map will be doing lots of string comparisons.
In addition, the filling of constant buffers is performed every frame regardless of if any data has actually been updated or not. This is of course dependent on what type of rendering is being performed at any given moment, but could potentially reduce the number of interactions needed with the API - so I will be investigating my options for how to improve the situation and only update buffers when they need to be.
Because of these points, I had made some notes about what to change after the book was completed - that's what I will be writing about in this entry...
Hieroglyph 3: The New System
The first step to improving the parameter system was to change the engine over to using an interface instead of an implementation reference. This will make it much easier to try out new implementations without completely hacking up the whole rendering system. This was fairly painless since it didn't involve any real new code, just creating a stub interface and doing some search and replace.
The task at hand can be thought of as two different parts. First I want to reduce the cost of looking up and using a parameter. Most likely it will involve creating a small structure to use for referencing parameters similar to the D3DHANDLE methods used by the Effects framework to directly get to the needed data instead of having to do a string based map lookup for every parameter. This is actually a little lower on the priority list than the second part of the task at hand: reducing the number of times that parameters need to be looked up in the first place. As mentioned above, I am currently naively filling the constant buffers every frame, which I think is introducing lots of unneeded work. Potential solutions will be to use a time stamp on the parameter data to see when it was last updated, or possibly using an actual system memory copy of the contents of the buffer so that I can do CPU side checks of the data.
So over the next couple of days I will be trying out some of these options and seeing what works for each of the two problems. Hopefully I can come away with a leaner system that still accommodates multithreaded rendering...