1
\$\begingroup\$

My problem is a bit complicated. When doing ray tracing rendering, all data in the scene needs to be prepared. In addition to texture resources, each object to be rendered will also have its own material data. Because the shader codes are different, the data structures of these material data are also different. For example:

sturct S1 { float3 p1, uint p2, float p3 };
struct S2 { float2 p1, float2 p2, uint2 p3};

Assuming that all objects in the scene use the same material, then their material data structure are the same. So I can create an SRV for each material data (one material data corresponds to an ID3D12Resource), and then bind it through the Descriptor Table to the pipeline and write a declaration in HLSL:

StructuredBuffer<S1> _MaterialDatasBuffer : register(t0);

Then use the ID obtained through InstanceID() to index this S1 array buffer. It's very simple.

But the problem is that it's impossible for all objects in the scene to use the same material, and the data structures corresponding to different materials are different. So it seems that I can't use a single StructuredBuffer to bind all data. But I don’t want to write a StructuredBuffer for every type of material, because I want my solution to handle arbitrary scenarios. Suppose there are 1,000 objects in the scene and 100 type of materials are used, I don’t want to create 100 descriptor table in C++ and I also don't want to write 100 StructuredBuffers of different types in HLSL. So how should I do?

Actually I have solved this problem in Vulkan. In Vulkan, each of my material data corresponds to a VkBuffer. Then I stored the VkBuffer addresses of all the material data in the scene into an array, and bound this address array to the pipeline. Then index the address array according to the current object ID in GLSL, and after obtaining the address, use buffer_reference to obtain the data at the address. In this way, no matter how many material data types there are, you only need to declare the material data type corresponding to the current shader in glsl:

layout(buffer_reference, scalar) buffer MaterialBuffer { S1 material; };

But it seems that in D3D12 it is not possible to obtain a buffer on the GPU memory through the GPU address like Vulkan. So how can I bind a variable amount of material data which have different data structure to the pipeline?

\$\endgroup\$

1 Answer 1

0
\$\begingroup\$

One option would be to use offsets into a single large ByteAddressBuffer, and then load the data from there. That way you can store an offset into the buffer with each object, instead of a buffer address, and the shader code can interpret that in whatever way it needs to for the specific material type.

The shader code might look something like this:

ByteAddressBuffer g_parameterBuffer;

void SomeFunction(int parameterBufferOffset)
{
    float4 param1 = asfloat(g_parameterBuffer.Load4(parameterBufferOffset));
    uint param2 = g_parameterBuffer.Load(parameterBufferOffset + 16);
    // etc.
}

You might also find http://www.joshbarczak.com/blog/?p=1260 useful for understanding more about the various types of buffers available in DirectX.

\$\endgroup\$
2
  • \$\begingroup\$ Does this have any different performance characteristics compared to using a structured buffer? Those may be useful to call out here for users weighing this alternative. \$\endgroup\$
    – DMGregory
    Nov 6, 2023 at 15:59
  • \$\begingroup\$ If you can use a structured buffer I'd recommend doing so, because it's simpler to use - you get to use names for things instead of byte offsets, and maintenance (e.g. adding / removing data) is much simpler. The main advantage of the ByteAddressBuffer is flexibility of data layout - you can put multiple different data structures inside a single buffer. \$\endgroup\$
    – Adam
    Nov 6, 2023 at 16:39

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .