Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unexpected sizes of arrays in a HLSL Constant Buffer

I have not yet used more complicated CBs like this here but, from what I understand, my C++ alignment and packing has to match what HLSL expects. So I'm trying to figure out the rules so I can predictably lay out the C++ struct to match what HLSL expects.

I was doing some tests in a Vertex Shader v5 to see packing produced in the output and used this structure in the vs.hlsl:

cbuffer conbuf {
    float m0;
    float m1;
    float4 m2;
    bool m3[1];
    bool m4[4];
    float4 m5;
    float m6;
    float4 m7;
    matrix m8;
    float m9;
    float m10;
    float4 m11[2];
    float m12[8];
    float m13;
};

which produced the following output (in the Header File Name VC++ Project HLSL Settings):

cbuffer conbuf {
    float m0; // Offset: 0 Size: 4
    float m1; // Offset: 4 Size: 4
    float4 m2; // Offset: 16 Size: 16
    bool m3; // Offset: 32 Size: 4
    bool m4[4]; // Offset: 48 Size: 52
    float4 m5; // Offset: 112 Size: 16
    float m6; // Offset: 128 Size: 4
    float4 m7; // Offset: 144 Size: 16
    float4x4 m8; // Offset: 160 Size: 64
    float m9; // Offset: 224 Size: 4
    float m10; // Offset: 228 Size: 4
    float4 m11[2]; // Offset: 240 Size: 32
    float m12[8]; // Offset: 272 Size: 116
    float m13; // Offset: 388 Size: 4
};

I pretty much figured out how offsets work (based on sizes) but I cannot understand the array sizes.

Some array sizes in here seem random. I can't figure out how the bool m4[4] array has size: 52. Same for float m12[8] which is size: 116. How does the HLSL compiler manage to produce these sizes?

Any help? I've already looked on MSDN packing page but they don't say much about arrays.

like image 599
CodeAngry Avatar asked Jun 18 '14 03:06

CodeAngry


1 Answers

I'll simplify your example a little bit, since you already get padding.

One important part for arrays, as per Packing rules (link you mentioned) is :

Arrays are not packed in HLSL by default. To avoid forcing the shader to take on ALU overhead for offset computations, every element in an array is stored in a four-component vector.

So let's take this simple cbuffer:

cbuffer cbPerObj : register( b0 )
{
     float Alpha[4];
};

As per the above rule (each float is stored in four vector), this would be (almost) equivalent to:

cbuffer cbPerObj : register( b0 )
{
     float4 Alpha[4];
};

Or (expanded)

 cbuffer cbPerObj : register( b0 )
{
     float Alpha1;
     float3 Dummy1;
     float Alpha2;
     float3 Dummy2;
     float Alpha3;
     float3 Dummy3;
     float Alpha4;
};

As you can notice, your last element is not padded, this is why you can notice in your case:

bool m4[4]; // Offset: 48 Size: 52
float4 m5; // Offset: 112 Size: 16

m4 is 16*4 = 64 (minus the last 3), 64-12 = 52

You can also notice that of course, 48 + 52 = 100 (so since m5 needs not to cross boundary, you can find the 12 lost bytes for the offset)

In the case you had,

bool m4[4]; // Offset: 48 Size: 52
float m5;

Offset for m5 would be 100, since it can fit the boundary.

Hope that makes sense.

like image 139
mrvux Avatar answered Oct 21 '22 14:10

mrvux