0
\$\begingroup\$

I have a rather peculiar question remaining after solving most of my own question on StackOverflow. When creating the constant buffers used by my shaders, I've learned that the order of variables between the cbuffer and the struct representing it have to be an accurate match. I am still unsure as to why this is such an issue, and that's why I'm here.

When creating a cbuffer to hold object data, I create the cbuffer and struct like so:

cbuffer ObjectBuffer : register(b0) {
    float4x4 World;
    float4x4 WorldViewProjection;
    float4x4 WorldInverseTranspose;
}
public struct ObjectBuffer {
    public Matrix World;
    public Matrix WorldViewProjection;
    public Matrix WorldInverseTranspose;
}

This way works just fine, but if I reorder the cbuffer variables, or the struct variables and do not reflect the changes in the other, issues arise and I can't even create the buffer.

cbuffer ObjectBuffer : register(b0) {
    float4x4 WorldInverseTranspose;
    float4x4 WorldViewProjection;
    float4x4 World;
}
public struct ObjectBuffer {
    public Matrix World;
    public Matrix WorldViewProjection;
    public Matrix WorldInverseTranspose;
}

In this case, the types are exactly the same and given the fact that HLSL expects constant buffers to be in 16 byte chunks; why does the ordering matter so much?

Aren't float4x4 types the same as Matrix types where it's essentially an array of arrays?

[ 0, 0, 0, 0 ] = 16 bytes
[ 0, 0, 0, 0 ] = 16 bytes
[ 0, 0, 0, 0 ] = 16 bytes
[ 0, 0, 0, 0 ] = 16 bytes
[    TOTAL   ] = 64 bytes

Since a float is 4 bytes on its own, this would mean a float4 is 16 bytes, and thus a float4x4 is 64 bytes. So why does the order matter if the size remained the same?


Update

So I've re-visited the issue and turns out I was wrong about the issue given above. This does allow the objects to be compiled correctly but presents grotesque effects in the shaders since the variables are in the wrong order which is expected.

However, this issue came up because I misread which buffer was causing the issue initially. It was all related to my material buffer in which I have a bool and a float. When I swapped the two on either side without reflecting the changes in the other it failed to create. I presume this is due to the difference in expected types. The cbuffer is in bool-float order while the struct is the opposite. This I expect to cause an issue, but it would still be nice to know what's happening under the hood to cause it to break. Is it because bool is a single byte but perhaps allocated into a chunk of 4 bytes?

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Your edit to point out that you're seeing errors at creation is useful, but it would help to then see how you are creating the buffer and what the actual error you get is (e.g., from the debug layer of the runtime). The ordering issues are problems, but they shouldn't normally prevent creation of the buffer since the creation of the buffer doesn't depend on the format of the data inside in general (but might depending on what C# wrapper method you are using?) \$\endgroup\$
    – user1430
    Nov 19, 2018 at 15:39

1 Answer 1

1
\$\begingroup\$

DirectX doesn't see your struct. When you set the uniform buffer all it sees is a bunch of bytes that it needs to map to the cbuffer definition in the shader. This is also why you may need to add padding between subsequent fields when alignments between the host language (C/C++/C#/etc.) and hlsl don't match. In particular when dealing with multi-float variables.

In effect it does a reintepret_cast on your struct to the one declared in the shader.

If the layout does not match then you get surprising behavior.

\$\endgroup\$
1
  • \$\begingroup\$ Can you elaborate a little more on this? Is this occurring when it creates the actual buffer object? Why does this matter if the types are exactly the same with no padding needed? I would expect incorrect behavior during rendering but not prior to that. For example if I have four float types that do different things, and I reorder them, they are all still floats, but now the behavior of the HLSL will change, but it should still create the buffer just fine. \$\endgroup\$
    – user121635
    Nov 19, 2018 at 15:23

You must log in to answer this question.