Custom structs for constant buffers

Editor
Feb 4, 2013 at 10:54 PM
Hello,

I'm fairly new to DirectX 11 (I come from a DX 9 background), so to my surprise, things have changed quite a bit. I ran across your engine while researching your book (Practical Render). I've poked through the code quite a bit of it, but one thing keeps bugging me. I notice that you don't create any custom structs for representing constant buffers in shader files. All of the DirectX 11 examples I've run across online that demonstrate using constant buffers create custom structs to represent them in code. Then call UpdateSubResource to send everything to the GPU. Is the way you're updating the parameters from the constant buffers a best practice in your engine? I'm in the process of updating my own game engine to support DirectX 11 and these constant buffers have me stumped on how to wrap them with platform independent classes.

Any insight would be much appreciated. Thanks!
Coordinator
Feb 5, 2013 at 1:36 AM
Hello,

Thank you for your interest in the engine and the book. The way that I handle the constant buffers is more for general purpose usage. The engine is intended to be able to easily incorporate new shader files with a minimum of effort, and thus I tend to use the elementary pieces in the building of the contents of a constant buffer.

In general, the parameter system is used to provide a store where vector and matrix based data is associated with a name. If an element in a constant buffer uses that name, then when it is time to update the buffer the value is retrieved and written into the appropriate location in the buffer. If you consider what the process would look like with a custom struct, everything would be the same except that you write the value into the struct by a member name instead of by a byte offset - so the CPU side data gathering is more or less equivalent, except that the custom data struct has to be declared at authoring time. The way that HG3 does this is more dynamic, and allows for runtime determination of the structure contents.

The choice between using UpdateSubresource and mapping the buffer is more or less up to the implementer. It doesn't take much effort to switch between one method or the other in your engine code (i.e. the system memory data is the same, the GPU resources are the same, only the API calls are different), so I would recommend making the option available to switch between the different techniques. In my own tests, I haven't found any statistically significant differences between the two... But of course this will probably depend on the target scene and driver.

As far as platform indepence, I haven't tried to map this construct onto another API. The engine was originally based on my D3D9 renderer, but that was heavily refactored during the switch to D3D11. However, the underlying hardware and drivers are still the same between APIs, so I wouldn't imagine that there is a big difference between what is done in OpenGL and D3D11. If you do come across some advice of ways to perform this abstraction then I would love to hear your comments!
Editor
Feb 5, 2013 at 2:29 AM
<div dir="ltr">I tested the different approaches (Map/Discard vs. UpdateSubresource) and I'd echo Jason Z's findings that [at least for my hardware and workload] there seemed to be little difference in performance. I ultimately went map/discard just because it seems to be &quot;best practice&quot; these days, e.g.: <div><a href="http://www.gamedev.net/topic/622685-constant-buffer-updatesubresource-vs-map/">http://www.gamedev.net/topic/622685-constant-buffer-updatesubresource-vs-map/</a><br> </div> <div><br> </div> <div style="">BTW, if you want to see an example of a straight-forward layer over both GL/DX11 take a look here:</div> <div style=""><a href="http://www.humus.name/index.php?page=3D">http://www.humus.name/index.php?page=3D</a></div> <div style=""><br> </div> <div style="">(Humus uses UpdateSubresource btw, in order to make the API consistent. I haven't looked at the GL-side at all, so can't comment there).</div> <div style=""><br> </div> <div style="">Jason</div> </div> <div><br> <br> <div>On Mon, Feb 4, 2013 at 8:36 PM, jzink <span dir="ltr">&lt;<a href="mailto:notifications@codeplex.com" target="_blank">notifications@codeplex.com</a>&gt;</span> wrote:<br> <blockquote style="margin:0 0 0 .8ex; border-left:1px #ccc solid; padding-left:1ex"> <div> <p>From: jzink</p> <div>Hello, Thank you for your interest in the engine and the book. The way that I handle the constant buffers is more for general purpose usage. The engine is intended to be able to easily incorporate new shader files with a minimum of effort, and thus I tend to use the elementary pieces in the building of the contents of a constant buffer. In general, the parameter system is used to provide a store where vector and matrix based data is associated with a name. If an element in a constant buffer uses that name, then when it is time to update the buffer the value is retrieved and written into the appropriate location in the buffer. If you consider what the process would look like with a custom struct, everything would be the same except that you write the value into the struct by a member name instead of by a byte offset - so the CPU side data gathering is more or less equivalent, except that the custom data struct has to be declared at authoring time. The way that HG3 does this is more dynamic, and allows for runtime determination of the structure contents. The choice between using UpdateSubresource and mapping the buffer is more or less up to the implementer. It doesn't take much effort to switch between one method or the other in your engine code (i.e. the system memory data is the same, the GPU resources are the same, only the API calls are different), so I would recommend making the option available to switch between the different techniques. In my own tests, I haven't found any statistically significant differences between the two... But of course this will probably depend on the target scene and driver. As far as platform indepence, I haven't tried to map this construct onto another API. The engine was originally based on my D3D9 renderer, but that was heavily refactored during the switch to D3D11. However, the underlying hardware and drivers are still the same between APIs, so I wouldn't imagine that there is a big difference between what is done in OpenGL and D3D11. If you do come across some advice of ways to perform this abstraction then I would love to hear your comments!</div> <div> <div> <p>Read the <a href="http://hieroglyph3.codeplex.com/discussions/431952#post996465" target="_blank"> full discussion online</a>.</p> <p>To add a post to this discussion, reply to this email (<a href="mailto:Hieroglyph3@discussions.codeplex.com?subject=[Hieroglyph3:431952]" target="_blank">Hieroglyph3@discussions.codeplex.com</a>)</p> <p>To start a new discussion for this project, email <a href="mailto:Hieroglyph3@discussions.codeplex.com" target="_blank"> Hieroglyph3@discussions.codeplex.com</a></p> <p>You are receiving this email because you subscribed to this discussion on CodePlex. You can <a href="https://hieroglyph3.codeplex.com/subscriptions/thread/project/edit" target="_blank"> unsubscribe or change your settings</a> on codePlex.com.</p> <p>Please note: Images and attachments will be removed from emails. Any posts to this discussion will also be available online at <a href="http://codeplex.com" target="_blank">codeplex.com</a></p> </div> </div> </div> </blockquote> </div> <br> </div>
Editor
Feb 5, 2013 at 2:54 PM
Thank you for your quick and detailed response Jason :)! That completely makes since. I just wasn't sure which way was better (i.e. UpdateSubresource or updating the params based on offset). In the examples I saw, it made since to define custom structs at compile time to represent the constant buffers, as the samples were simple. I really like the way you implemented it in your engine. Nice and clean!

I'll be sure to get back to you if I run across a good way to abstract constant buffers for DirectX 11 and OpenGL. Thanks again!
Editor
Feb 5, 2013 at 2:59 PM
Thank you too for your response jmkinzer! I appreciate you taking time to test the performance of mapping vs. updatesubresource. I'll be sure to check out the links you mentioned above. Thanks again!
Editor
Mar 7, 2013 at 10:34 PM
Hey Jason,

So a quick update on what I decided to do about abstracting constant buffers for API independence. Not sure if you know Mr. David Eberly there at Microsoft, but I've been chatting with him over the last 6 months about engine design. He uses a great approach to abstracting certain parts of his engine (WildMagic). I've actually adopted this same method myself. I have a ConstantBuffer class that's platform independent. This is what users of my engine will interface with. I also have DX11 and GL dependent versions of a constant buffer that wraps the specifics of each API. Both versions of the class consume a ConstantBuffer independent class and uses the data with-in it to create the appropriate buffer and handle parameter updates (Map/Unmap). I've actually done this same technique with most of the classes that users will interface with (e.g. Viewport, IndexBuffer, VertexBuffer ect.). This approach has worked great in my engine and helps abstract the API dependent details that users don't need to worry about. The only think they need to be concerned about is whether they want a DX11 or GL game and what platform to build for. The rest is handled by my engine.
Editor
Mar 7, 2013 at 10:48 PM
Wow, don't know why it posted 3 times. Please feel free to remove the last two :).
Coordinator
Mar 8, 2013 at 1:16 AM
Hello again,

I actually do know Dave - his books were my original inspiration for Hieroglyph (long ago...) and I have worked with him in regards to my own book projects. We have chatted a few times over the years, and I closely follow his engine development. He has a ton of experience, and I value his opinions greatly.

Your system sounds like a good way to mask out the API calls for each type of renderer, which is great. I have actually been thinking about how to handle some fallback rendering scenarios, and this type of a technique seems like it would help to minimize the effort to hide the implementation from the user. To be perfectly honest, this is already somewhat removed from the user due to my material classes handling the pipeline configurations, but your way is a much finer resolution (which is probably better...).

If I may ask, which version of GL are you working with? Have you found many areas where there isn't too much similarity? I would be interested to hear your thoughts on what is the same and what needs some work to get around.

Thanks for the update!
Editor
Mar 8, 2013 at 3:40 PM
That's great you know Dave! I have enjoyed his books over the years and regularly check-in on his engine. He definitely knows his stuff.

Yeah the abstraction has really worked well with my engine. I thought about going the route you did when I originally started and abstracting everything out into a pipeline configuration, but I like how functional OpenGL and DirectX is in terms of just calling a method to set state (e.g. IASetVertexBuffers, glBindBuffer, RSSetViewports, glViewport ect).

I'm currently using OpenGL 4.3 for my engine. I'm still not as comfortable in OpenGL as DirectX, but I'm learning fast :). So far, I have found that the two APIs are not that much different from each other. Going back to what I mentioned above about OpenGL and DirectX being functional, I have found that for every method I use in DirectX (e.g. IASetVertexBuffer, RSSetViewports ect) there is a equivalent method in OpenGL.

As far as what needs some work to get around, OpenGL doesn't have as nice of a reflection API as DirectX does for shaders. There is the glGetActiveUniform method, which can be used to query variable info from a shader program. What really got me hung up though, was the whole constant buffers thing from my original post. I couldn't quite figure out how to abstract a buffer, so that OpenGL and DirectX can handle it gracefully. Thanks to your technique though and how I've been abstracting out my classes, everything works perfectly :).

Are you thinking about supporting OpenGL in Hieroglyph?