Advertisement

Data Driven Material Managment

Started by June 03, 2019 06:59 AM
2 comments, last by Jman2 5 years, 8 months ago

Hello,

I posted about a similar topic around one year ago but have since rebuilt my engine in dx12 (although its agnostic so can work with any API), the idea for managing shader/materials would eventually be to have a custom language that generates a material file for me but for now this is the process.

Write Vertex and pixel shader

Create XML material definition: States, Parameters, vs and ps code.

Engine iterates through Shader folder and creates all shaders/states up front from the material definition file.

So the issue is the C++ side particularly with constant buffers, a lot of hobby engines that sue OpenGL go down the route of having a single cpp called Material with various get and setters for parameters based on there name, Unity actually does this as well: https://docs.unity3d.com/ScriptReference/Material.html However with Constant buffers the previous solution I had was to generate a byte blob with the required size for all the required parameters, I would then offset and set the bytes myself. This means creating a map between the string parameter name (hashed of course) too the byte offset for the start of that particular parameter. The above would work as you can just offset to a specific byte for say a float and set the 4 bytes, or a vector and set the 12 bytes and so on. My issue is the use of a map, its additional space and potentially time to grab the offset in order to get/set a parameter.

So, the question is, does anybody have any insight into how something like unity manages to have a single class that is data driven to set constant buffer data when we can have any variety of parameters defined in xml etc. other then the potential method i have suggested.

Thanks :)

3 hours ago, Jman2 said:

I would then offset and set the bytes myself. This means creating a map between the string parameter name (hashed of course) too the byte offset for the start of that particular parameter. The above would work as you can just offset to a specific byte for say a float and set the 4 bytes, or a vector and set the 12 bytes and so on. My issue is the use of a map, its additional space and potentially time to grab the offset in order to get/set a parameter.

That's pretty much exactly what I do - a list of named parameters with an offset (and also a type, array size, number of components, etc).

Instead of doing something like:


SetParam("foo", 42.0f);

You can do the string lookup once ahead of time:


foo_handle = FindParam("foo");
...
SetParam(foo_handle, 42.0f);

(foo_handle can be as simple as an integer index into the array, or some kind of fancy pointer).

Also, if your materials are defined ahead of time, you can move a lot of this work into your tools. For artist-defined materials (as opposed to gameplay-generated ones), I do the entire cbuffer generation step offline, as part of our data-compiler. At runtime, we just load the fully formed cbuffer into memory and send it to the GPU. 

Lastly, the above is only used for half our cbuffers... The other type of cbuffer uses a fixed structure layout that's hard-coded to be the same in both the shader and in the engine code. The engine can then write data directly into a native struct, and then memcpy it into the cbuffer. This is used for things like cameras or per-object transforms, etc... The data driven system is used for materials, FX, gameplay, etc...

Advertisement

Great point, i didnt think about doing it at tool time and loading it in one go.

ShaderHandle also is a good ideam, so my material definition file will just contain a key item pair parameter name hashed and index into data. Then just push the pair into a map during initialization and let the user grab the handle and cache it for when every they want to adjust a parameter?

Thanks.

This topic is closed to new replies.

Advertisement