10
\$\begingroup\$

Alright, I'm having a hard time getting a bool packed and aligned into a hlsl constant buffer and I'm not sure why.

Here is the buffer in hlsl

cbuffer MaterialBuffer : register(b1) {
    float3 materialDiffuseAlbedo;
    float  materialSpecularExponent;
    float3 materialSpecularAlbedo;
    bool isTextured;
};

And here it is in c++

struct GeometryBufferPass_MaterialBuffer {
    XMFLOAT3 diffuse;
    float specularExponent;
    XMFLOAT3 specular;
    bool isTextured;
};

I've tried moving the bool and padding the struct in all kinds of ways with no luck. What is the correct way to do this?

\$\endgroup\$
6
  • \$\begingroup\$ What's the issue it's causing? \$\endgroup\$
    – House
    Jan 18, 2012 at 21:25
  • \$\begingroup\$ The bool is used to determine whether or not the shader needs to sample a texture. This way I can render textured and untextured objects with the same shader. The bool is simply used in a conditional statement. It isn't getting the correct data because it's treating all objects the same. This is incorrect because my sky sphere is the only thing that has a texture at the moment. \$\endgroup\$ Jan 18, 2012 at 21:30
  • \$\begingroup\$ The other values work but not the bool? Have you tried using one of the debuggers available for shaders to see what's getting put in to it? \$\endgroup\$
    – House
    Jan 18, 2012 at 21:36
  • 2
    \$\begingroup\$ try to store the bool value in a char. store as 1 for true and 0 for false. Just for test, and also, a bool is 1 byte in C++ anyway... \$\endgroup\$ Jan 18, 2012 at 21:43
  • 3
    \$\begingroup\$ The size of a bool is implementation dependent. On some platforms it's the same size as an int. stackoverflow.com/questions/5067492/… \$\endgroup\$
    – Tetrad
    Jan 18, 2012 at 22:02

3 Answers 3

9
\$\begingroup\$

For efficiency, constant buffers will be mapped such that values do not straddle GPU registers. Each register is four floats in size (16 bytes) so constant buffer structures must be a multiple thereof on the GPU. Your C++ structure should be padded accordingly if you want to use it as a convenience for mapping data (this, note, doesn't always scale well).

Your issue, then, is that an HLSL boolean is four bytes, but one byte on the CPU side (in your specific implementation). This causes your C++ structure to not align properly: the significant bit of a boolean value (the 0 or 1 that matters) is going to be stored in the least-signficant byte of the value, and since the sizes don't agree the location of that byte in memory will differ in the CPU and GPU versions of the structure.

Manually inserting the appropriate padding and ensuring proper 16-byte alignment, or just using an appropriately-sized type, like an integer, should fix the issue. This thread may also be of use to you as it contains a more in-depth discussion of roughly the same problem.

\$\endgroup\$
3
  • 1
    \$\begingroup\$ I don't follow: "isTextured fit into that same register since it would have to straddle into the next one. Thus it is bumped entirely in to the next register." The second register consists of specular in the first three components and isTextured in the last, so I don't see that anything needs to get bumped into the next register? The 1-byte vs 4-byte bool length obviously matters, but either one would fit after specular in the same register. \$\endgroup\$ Jan 18, 2012 at 22:45
  • \$\begingroup\$ You are correct; I confused myself w.r.t to the size of the registers versus the size of types and came up with an incorrect representation of the mapping. The only problem is the location of the relevant byte in memory. I've adjusted my answer accordingly. \$\endgroup\$
    – user1430
    Jan 18, 2012 at 22:51
  • \$\begingroup\$ Accepting your answer for thoroughness. Like you mentioned, it was a big/little endian issue and using an int solved it. \$\endgroup\$ Jan 19, 2012 at 1:29
4
\$\begingroup\$

Alright, did some reading and noticed that a hlsl bool is essentially a 32 bit integer. So I just used an int in the c++ struct to solve my problem.

struct GeometryBufferPass_MaterialBuffer {
    XMFLOAT3 diffuse;
    float specularExponent;
    XMFLOAT3 specular;
    int isTextured;
};
\$\endgroup\$
2
  • \$\begingroup\$ Why don't you keep the bool type but just use int in the compiler side? Just to keep the semantics. \$\endgroup\$ Jan 18, 2012 at 23:55
  • \$\begingroup\$ Yeah, that is what I'm doing above. Used an integer for the struct cpu side and a bool for the constant buffer gpu side. \$\endgroup\$ Jan 19, 2012 at 1:31
0
\$\begingroup\$

float, bool and int do not necessary line up for endian particularly for multiple items.

Switching to int or float works in some of the examples listed here as its aligned between XMFLOAT3 items so are referenced correctly. However if you need to declare an array or several items in the structure for int, float (none XM types) then you will likely find that GPU values don't match the values in set in the CPU structure.

I certainly did when adding an array of type int to be used for lighting type.

The easiest way I found is to stick to XM types that align by 16, which may require wasted elements / bytes but sorts the endian for you. E.G XMINT4 and just used the first element .x for your value, or if you have the need then use the other elements for another purpose but means poor naming (make sure to comment). Note: XMINT2 array will also be out of logical order

\$\endgroup\$

You must log in to answer this question.

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