Some time ago after wasting another hour on a bug caused by different memory layout in my c++ struct and corresponding const buffer I figured it would be quite easy to write a simple validator using shader reflection and some basic static c++ reflection. I wrote it and it worked quite good so here is the source code in case anyone has a similar problem. The code is stripped from my framework. I’ve also made a quick sample project that shows how to print and compare struct and cbuffer layout. It’s very simple. This is how to get and print const bufffer info:
CShaderReflection reflection;
reflection.Initialize(shaderBlob); // param type is ID3DBlob*
// print it
reflection.PrintCBuffersInfo();
For const buffer declared like this:
struct Material
{
float4 Ambient;
float4 Specular;
float3 Reflect;
};
cbuffer testCBuffer
{
float4 ambient;
float intensity;
float intensity2;
float4 color;
matrix view;
float2 sth;
matrix world;
float3 eyePos;
float4 something;
float somethinggg;
Material material;
float3 something2;
};
It will print something like this:
cbuffers count: 1 cbuffer name: testCBuffer size: 304 vars count: 12 name size offset wasted space type name members elements columns rows ambient 16 0 0 float4 0 0 4 1 intensity 4 16 0 float 0 0 1 1 intensity2 4 20 8 float 0 0 1 1 color 16 32 0 float4 0 0 4 1 view 64 48 0 float4x4 0 0 4 4 sth 8 112 8 float2 0 0 2 1 world 64 128 0 float4x4 0 0 4 4 eyePos 12 192 4 float3 0 0 3 1 something 16 208 0 float4 0 0 4 1 somethinggg 4 224 12 float 0 0 1 1 material 44 240 4 Material 3 0 11 1 something2 12 288 4 float3 0 0 3 1
‘wasted space’ is number of bytes that are not used due to padding.
Next, for corresponding c++ struct declared like this:
struct Material
{
XMFLOAT4 Ambient;
XMFLOAT4 Specular;
XMFLOAT3 Reflect;
};
struct testCBuffer
{
XMFLOAT4 ambient;
float intensity;
float intensity2;
XMFLOAT2 padding;
XMFLOAT4 color;
XMMATRIX view;
XMFLOAT2 sth;
XMMATRIX world;
XMFLOAT3 eyePos;
XMFLOAT4 something;
float somethinggg;
Material material;
XMFLOAT3 something2;
RTTI(testCBuffer, MEMBER(ambient) MEMBER(intensity) MEMBER(intensity2) MEMBER(padding) MEMBER(color) MEMBER(view)
MEMBER(sth) MEMBER(world) MEMBER(eyePos) MEMBER(something)
MEMBER(somethinggg) MEMBER(material) MEMBER(something2) )
};
it will print something like this:
size: 288 ars count: 13 name size offset missing padding type name ambient 16 0 0 struct DirectX::XMFLOAT4 intensity 4 16 0 float intensity2 4 20 0 float padding 8 24 0 struct DirectX::XMFLOAT2 color 16 32 0 struct DirectX::XMFLOAT4 view 64 48 0 struct DirectX::XMMATRIX sth 8 112 8 struct DirectX::XMFLOAT2 world 64 128 0 struct DirectX::XMMATRIX eyePos 12 192 4 struct DirectX::XMFLOAT3 something 16 204 0 struct DirectX::XMFLOAT4 somethinggg 4 220 12 float material 44 224 4 struct Material something2 12 268 4 struct DirectX::XMFLOAT3
And finally the sample will print this:
hlsl cbuffer and c++ struct have different members count hlsl cbuffer and c++ struct have different sizes member: sth should be padded with 8 bytes member: eyePos should be padded with 4 bytes member: somethinggg should be padded with 12 bytes member: material should be padded with 4 bytes member: something2 should be padded with 4 bytes
Note that if you use #pragma pack(*) the “missing padding” column can be invalid.
I’ve included sample loop that shows how to iterate through struct and const buffer members and compare them. You can use the code however you like. I hope it will be useful for someone.
The sample is compiled using visual studio 2012. It uses Windows SDK and a little c++ 11.The reflection code is written by John Watte (http://www.enchantedage.com/cpp-reflection).