The following HLSL compiles with both FXC and DXC, the only part that is
exclusive to DXC is the resource inside the Doggo struct.
Given this code, how you access each of the elements of the cbuffers and how are they shown in the shader reflection?
namespace NS {
cbuffer NSCB {
float f2;
}
}
How do you access f2?
A. NS::f2
B. f2
Answer!
In DXC, you access f2 as f2 because a bug in DXC causes names declared in
cbuffers to be declared at translation unit scope instead of the enclosing scope.
This also impacts the shader reflection where the name is f2.
In FXC, this works as you probably expect, the name is declared inside the NS
namespace as NS::f2 and the reflection also names the value NS::f2.
cbuffer CB2 {
namespace NS2 {
float f; // CB2 - NS2::f
}
}
How do you access f?
A. NS2::f
B. f
Answer!
In both compilers this is accessed as NS::f. This is because the bug in DXC
impacting cbuffer members hoists the namespace to translation unit scope,
but the enclosed decls are still in the namespace.
cbuffer CB2 {
namespace NS2 {
cbuffer NS2CB {
float f3; // DXC: NS2CB - f3 | FXC: NS2::NS2CB - NS2::f3
}
}
}
How do you access f3?
A. NS2::f3
B. f3
Answer!
You see where this is going right? In DXC, you access f3 as f3 because
cbuffer declarations get hoisted.
In FXC, this is NS2::f3 exactly as you would probably rationally expect
(assuming you have any rationality left).
For the next few questions consider this code:
cbuffer CB2 {
namespace NS2 {
cbuffer NS2CB {
struct Doggo {
static int D;
int d;
#ifdef __hlsl_dx_compiler // DXC allows resources in weird places...
Buffer<float> Buf; // not in cbuffer!
#endif
} Dog; // NS2::NS2CB - NS2::Dog
}
}
}
How do you access D?
A. NS2::Doggo::D
B. Doggo::D
Answer!
D is not in the cbuffer. In DXC you access D as Doggo::D, but in FXC it is
NS2::Doggo::D.
How you access d?
A. NS2::Dog.d
B. Dog.d
Answer!
You should be getting the hang of this by now, DXC drops the namespace to
Dog.d, while FXC expects NS2::Dog.d.
How do you access Buf?
A. NS2::Doggo::Dog.Buf
B. Dog.Buf
Answer!
This one only works in DXC because resources aren’t allowed in structs in FXC,
so this is just Dog.Buf.
Full Explanation!
Here’s a quick demonstration:
#ifdef __hlsl_dx_compiler
int Doggo::D = 0;
float main() : OUT {
return f + gf + f + NS2::f + f2 + f3 + (float)Dog.d + Dog.Buf[0];
}
#else
int NS2::Doggo::D = 0;
float main() : OUT {
return f + gf + NS::f2 + NS2::f + NS2::f3 + (float)NS2::Dog.d;
}
#endif
DXC on Compiler Explorer FXC on Shader Playground
Obviously this is a mixture of compiler bugs and weird usage. FXC does something
sane in all cases however cbuffer declarations can be pretty confusing. The
syntax used for cbuffer declarations looks an awful lot like a namespace
declaration but the name of a cbuffer is not a declared name in the
translation unit; it is only used to generate shader reflection.
Both FXC and DXC also allow you to put declarations inside cbuffer
declarations that have no meaning and can be really confusing. For example you
can put static global variable declarations, function declarations, even your
entry function in FXC can go in a cbuffer.
Due to the bugs in DXC’s implementation of cbuffers, and some user complaints
about confusion regarding declarations inside cbuffers that don’t belong the
HLSL 202x language version is adopting a slightly stricter rule around
cbuffer declarations. Specifically cbuffer and namespace declarations are
not allowed inside cbuffer declarations. This small change in HLSL 202x
simplifies the compiler implementation since we will not need to handle
potentially infinite nestings of cbuffer and namespace declarations.
We’re also considering additional changes in future versions of HLSL to further
restrict the valid set of declarations inside a cbuffer.