Recently I was working a bit more on my Direct3d11 shadow experiments code. I had to modify constant buffer layouts many times and that meant I had to make many matching updates in C++ and HLSL. I was thinking about a simple solution to only define one side and get the other one automatically generated. Without the redundancy I can also prevent some bugs. I though of many solutions and decided on using macros and some C++ code that generates the HLSL code though some form of reflection (getting type and name). This is how I define my constant buffer on C++ side:
START_STRUCT(SViewConstants) ENTRY(XMMATRIX, c_WorldViewProj) ENTRY(XMMATRIX, c_InvViewProj) ENTRY(XMMATRIX, c_World2Shadow) ENTRY(XMFLOAT3, c_EyePos) ENTRY(float, c_ShadowBias) ENTRY(XMFLOAT3, c_SunDirection) // normalized ENTRY(float, c_FracTime) ENTRY(XMFLOAT4, c_BackbufferExtent) // w, h, 1/w, 1/h ENTRY(uint32, c_iBackbufferExtentX) ENTRY(uint32, c_iBackbufferExtentY) ENTRY(uint32, c_iFrameId) ENTRY(uint32, c_iDummy9123) ENTRY(XMFLOAT4, c_ShadowmapExtent) // w, h, 1/w, 1/h END_STRUCT()
This is the code needed for the reflection (WordPress ate all HTML looking parts in the templates so I had to post it as image and word document as it doesn’t support txt and cpp either):
reflection (as word document)
And this is how I generate the HLSL code:
struct SStructReflection : public IStructReflection
{
FILE* out;
SStructReflection(FILE* InOut) : out(InOut) {}
virtual void Start(const char* StructName)
{
fprintf(out, "\r\ncbuffer %s\r\n{\r\n", StructName);
}
virtual void Add(const char* Type, const char* Name)
{
fprintf(out, "\t%s %s;\r\n", Type, Name);
}
virtual void End()
{
fprintf(out, "};\r\n");
}
};
void GenerateAutoCommon()
{
FILE* out = 0;
if(_wfopen_s(&out, L"Shaders\\AutoCommon.hlsl", L"wb") == 0)
{
SStructReflection Refl(out);
SViewConstants::Reflection(Refl);
// ... other constant buffers
fclose(out);
}
}
This could be improved further e.g.
- Checking for proper alignment (simply count the size and asset)
- Make reflection defines independent from HLSL (simply return the type as string)
- Avoid the virtual (create a data structure that can be processed later on)
- Hide dummy functions from auto completion (could read/parse C++ as text and avoid macros)
- …
However in it’s current form it fulfills its purpose and it’s much easier to get the idea (look at the defines, they create functions that produce a chain calling the reflection interface methods).