[pixel shader fail(sm<6)] // Overlapping register(cX) reservations are not allowed except on SM6, where they are aliased. // On SM1 this gives hr 0x88760b59. float a : register(c0); float b : register(c0); float4 main() : sv_target { return a + b; } [pixel shader] // It is not required to provide a register(cX) for all elements in the $Globals buffer. float4 a; // will get register(c1) float4 b : register(c0); float4 main() : sv_target { return float4(a.xw, b.yz); } [test] uniform 0 float4 0.1 0.2 0.3 0.4 uniform 4 float4 1.1 1.2 1.3 1.4 todo(glsl) draw quad probe (0, 0) rgba (1.1, 1.4, 0.2, 0.3) [pixel shader] float4 a[3]; // will get register(c3) float4 b[2] : register(c1); float4 main() : sv_target { return float4(a[1].xy, b[0].zw); } [test] uniform 0 float4 0.1 0.2 0.3 0.4 uniform 4 float4 1.1 1.2 1.3 1.4 uniform 8 float4 2.1 2.2 2.3 2.4 uniform 12 float4 3.1 3.2 3.3 3.4 uniform 16 float4 4.1 4.2 4.3 4.4 todo(glsl) draw quad probe (0, 0) rgba (4.1, 4.2, 1.3, 1.4) [require] shader model < 4.0 [pixel shader] float a : register(c2); float b; // will get register c0 in SM1 float4 main() : sv_target { return float4(a, b, 0.0, 0.0); } [test] uniform 0 float4 0.1 0.2 0.3 0.4 uniform 4 float4 1.1 1.2 1.3 1.4 uniform 8 float4 2.1 2.2 2.3 2.4 uniform 12 float4 3.1 3.2 3.3 3.4 draw quad probe (0, 0) rgba (2.1, 0.1, 0.0, 0.0) [require] shader model >= 4.0 [pixel shader] float a : register(c2); float b; // will get offset equivalent to c2.y in SM4 and SM6 float4 main() : sv_target { return float4(a, b, 0.0, 0.0); } [test] uniform 0 float4 0.1 0.2 0.3 0.4 uniform 4 float4 1.1 1.2 1.3 1.4 uniform 8 float4 2.1 2.2 2.3 2.4 uniform 12 float4 3.1 3.2 3.3 3.4 todo(glsl) draw quad probe (0, 0) rgba (2.1, 2.2, 0.0, 0.0) [require] shader model >= 6.0 [pixel shader] // Variables with overlapping register(cX) reservations are aliased in SM6. float2 a : register(c2); float3 b : register(c2); float4 main() : sv_target { return float4(a, b.yz); } [test] uniform 0 float4 0.1 0.2 0.3 0.4 uniform 4 float4 1.1 1.2 1.3 1.4 uniform 8 float4 2.1 2.2 2.3 2.4 draw quad probe (0, 0) rgba (2.1, 2.2, 2.2, 2.3) % Results differ between SM1 and SM4 because in the latter variables can share the same register, % using different writemasks. [require] shader model < 4.0 [pixel shader] struct { float2 a; float b; } apple : register(c2); float4 main() : sv_target { return float4(apple.a, apple.b, 0); } [test] uniform 0 float4 0.1 0.2 0.3 0.4 uniform 4 float4 1.1 1.2 1.3 1.4 uniform 8 float4 2.1 2.2 2.3 2.4 uniform 12 float4 3.1 3.2 3.3 3.4 draw quad probe (0, 0) rgba (2.1, 2.2, 3.1, 0.0) [require] shader model >= 4.0 [pixel shader] struct { float2 a; float b; } apple : register(c2); float4 main() : sv_target { return float4(apple.a, apple.b, 0); } [test] uniform 0 float4 0.1 0.2 0.3 0.4 uniform 4 float4 1.1 1.2 1.3 1.4 uniform 8 float4 2.1 2.2 2.3 2.4 uniform 12 float4 3.1 3.2 3.3 3.4 todo(glsl) draw quad probe (0, 0) rgba (2.1, 2.2, 2.3, 0.0) [pixel shader] // On SM4, register(cX) has no effect unless in the $Globals buffer. cbuffer extra { float a : register(c1); }; float4 main() : sv_target { return a; } [test] uniform 0 float 100 uniform 4 float 101 todo(glsl) draw quad probe (0, 0) rgba (100, 100, 100, 100) [pixel shader fail(sm>=6)] // On SM4 register(cX) has no effect unless in the $Globals buffer. float4 main(uniform float a : register(c1)) : sv_target { return a; } [test] uniform 0 float 100 uniform 4 float 101 todo(glsl) draw quad probe (0, 0) rgba (100, 100, 100, 100) [pixel shader todo] cbuffer c { float a : packoffset(c1); float b : packoffset(c2) : register(c1); // ^ register(c1) ignored for cbuffer that is not $Globals. } float4 main() : sv_target { return float4(a, b, 0, 0); } [test] uniform 0 float 200 uniform 4 float 201 uniform 8 float 202 todo(sm<6) draw quad todo(sm<6) probe (0,0) rgba (201.0, 202.0, 0.0, 0.0) [pixel shader fail(sm<4)] int k : register(i0); // register(cX) is also required. float4 main() : sv_target { return k; } [require] % All shader models. % In SM1, most variables are needed in the "c" register group, for float operations. % If a variable is needed in the "c" register group, register() reservations in other groups can be % provided only if a register(cX) reservation is also provided. [pixel shader fail(sm<4) todo(sm<4)] int k : register(i0); // ^^ register(cX) is also required in SM1. float4 main() : sv_target { return k; } [pixel shader todo] int k : register(i0) : register(c1); // Shader compiles because a "c" register reservation is provided for "k". float4 main() : sv_target { return k; } [require] shader model >= 3.0 % model 2.0 doesn't support unrollable loops. [pixel shader todo(sm<4)] int k : register(i0); // ^^ register(cX) is not required since "k" is just needed in the "i" register group. float4 main() : sv_target { float f = 0; for (int i = 0; i < k; ++i) f += i; return f; } [pixel shader todo] int k : register(c0) : register(b0); // ^^ unlike the "c" register group, a reservation is not required for the "i" group, even though "k" is needed on it. float4 main() : sv_target { float f = 0; for (int i = 0; i < k; ++i) f += i; return f; } % Expressions as offsets. [require] % All shader models. [pixel shader] // no offset at all, implicitly c0. float a : register (c); float4 main() : sv_target { return a; } [test] uniform 0 float 1.0 todo(glsl) draw quad probe (0, 0) rgba (1.0, 1.0, 1.0, 1.0) [pixel shader] // Numeric expr, no offset in the identifier. DXC ignores this. float a : register (c[1 + 1 * 2 * 0]); float4 main() : sv_target { return a; } [test] uniform 0 float 1.0 uniform 4 float 2.0 todo(glsl) draw quad if(sm<6) probe (0, 0) rgba (2.0, 2.0, 2.0, 2.0) if(sm>=6) probe (0, 0) rgba(1.0, 1.0, 1.0, 1.0) [pixel shader] // Numeric expr. DXC also ignores this. float a : register (c0[1 + 1 * 2 * 0]); float4 main() : sv_target { return a; } [test] uniform 0 float 0.0 uniform 4 float 1.0 todo(glsl) draw quad if(sm<6) probe (0, 0) rgba (1.0, 1.0, 1.0, 1.0) if(sm>=6) probe (0, 0) rgba (0.0, 0.0, 0.0, 0.0) [pixel shader] // Float offsets truncate. float a : register (c0[0.6 + 0.6]); float4 main() : sv_target { return a; } [test] uniform 0 float 0.0 uniform 4 float 1.0 todo(glsl) draw quad if(sm<6) probe (0, 0) rgba (1.0, 1.0, 1.0, 1.0) if(sm>=6) probe (0, 0) rgba (0.0, 0.0, 0.0, 0.0) [pixel shader] // Booleans are interpreted as integers in the usual way. float a : register (c0[true + false * true]); float4 main() : sv_target { return a; } [test] uniform 0 float 0.0 uniform 4 float 1.0 todo(glsl) draw quad if(sm<6) probe (0, 0) rgba (1.0, 1.0, 1.0, 1.0) if(sm>=6) probe (0, 0) rgba (0.0, 0.0, 0.0, 0.0) [pixel shader fail(sm>=6)] // Negative offsets. DXC fails to compile this. float a : register (c2[-1]); float4 main() : sv_target { return a; } [test] uniform 4 float 1.0 todo(glsl) draw quad probe (0, 0) rgba (1.0, 1.0, 1.0, 1.0) % Weird FXC behavior. % SM4 accepts anything for 'b' reservations and discards it silently for global numeric variables. [pixel shader fail(sm<4 | sm>=6)] float a : register(banana); float4 main() : sv_target { return a; } [test] uniform 0 float 1.0 todo(glsl) draw quad probe (0, 0) rgba(1.0, 1.0, 1.0, 1.0) % Testing other reservation types. This is a parse failure, i.e "X3530: register sa not valid" [pixel shader fail] sampler s : register(samply); float4 main() : sv_target { return tex2D(s, float2(0, 0)); } % This seems to parse fine, but fails with a different error message: "X3530: sampler requires an 's' or 't' register". % Resource types probably have extra validation. [pixel shader fail] sampler s : register(banana); float4 main() : sv_target { return tex2D(s, float2(0, 0)); } % Trailing characters after the reservation index are okay in SM < 6, except for 'b' reservations [pixel shader fail(sm>=6)] float a : register(c1manymanyletters); float4 main() : sv_target { return a; } [test] uniform 0 float 0.0 uniform 4 float 2.0 todo(glsl) draw quad probe (0, 0) rgba(2.0, 2.0, 2.0, 2.0) [require] shader model >= 4.0 [pixel shader fail] cbuffer buf : register(b0manymanyletters) { float a; } float4 main() : sv_target { return a; } % SM4 fails, but not during parsing: it tries to reserve slot 4294967295 (i.e UINT_MAX) for the constant buffer. % It'll attempt to reserve the same slot even if an offset is passed in brackets, which suggests it uses -1 % as a "parsing failure" flag. % DXC fails during parsing. [pixel shader fail] cbuffer buf : register(banana) { float a; } float4 main() : sv_target { return a; } % Some versions of fxc's SM5.1 completely ignore bracket exprs for samplers and textures. % This was fixed (?) in later versions. [sampler 0] filter linear linear linear address clamp clamp clamp [sampler 1] filter point point point address clamp clamp clamp [srv 0] size (2d, 1, 2) 0.0 0.0 0.0 0.0 1.0 1.0 1.0 1.0 [srv 1] size (2d, 1, 2) 1.0 1.0 1.0 1.0 2.0 2.0 2.0 2.0 [require] shader model >= 4.0 [pixel shader] Texture2D tex : register(t[1]); sampler sam : register(s[1]); float4 main() : sv_target { return tex.Sample(sam, float2(0, 0.5)); } [test] todo(glsl) draw quad if(sm<6) probe (0, 0) rgba(2.0, 2.0, 2.0, 2.0) if(sm>=6) probe (0, 0) rgba(0.5, 0.5, 0.5, 0.5) % cbuffer reservation limits. [pixel shader] cbuffer buf0 : register(b0) { float a; }; cbuffer buf1 : register(b1) { float b; }; cbuffer buf2 : register(b2) { float c; }; cbuffer buf3 : register(b3) { float d; }; cbuffer buf4 : register(b4) { float e; }; cbuffer buf5 : register(b5) { float f; }; cbuffer buf6 : register(b6) { float g; }; cbuffer buf7 : register(b7) { float h; }; cbuffer buf8 : register(b8) { float i; }; cbuffer buf9 : register(b9) { float j; }; cbuffer buf10 : register(b10) { float k; }; cbuffer buf11 : register(b11) { float l; }; cbuffer buf12 : register(b12) { float m; }; cbuffer buf13 : register(b13) { float n; }; float4 main() : sv_target { return a * b * c * d * e * f * g * h * i * j * k * l * m * n; } [pixel shader fail(sm>=6)] // $Globals and $Params count towards the 14 cbuffer limit. float glob; cbuffer buf0 : register(b0) { float a; }; cbuffer buf1 : register(b1) { float b; }; cbuffer buf2 : register(b2) { float c; }; cbuffer buf3 : register(b3) { float d; }; cbuffer buf4 : register(b4) { float e; }; cbuffer buf5 : register(b5) { float f; }; cbuffer buf6 : register(b6) { float g; }; cbuffer buf7 : register(b7) { float h; }; cbuffer buf8 : register(b8) { float i; }; cbuffer buf9 : register(b9) { float j; }; cbuffer buf10 : register(b10) { float k; }; cbuffer buf11 : register(b11) { float l; }; float4 main(uniform float param) : sv_target { return glob * param * a * b * c * d * e * f * g * h * i * j * k * l; } [pixel shader fail] float glob; cbuffer buf0 : register(b0) { float a; }; cbuffer buf1 : register(b1) { float b; }; cbuffer buf2 : register(b2) { float c; }; cbuffer buf3 : register(b3) { float d; }; cbuffer buf4 : register(b4) { float e; }; cbuffer buf5 : register(b5) { float f; }; cbuffer buf6 : register(b6) { float g; }; cbuffer buf7 : register(b7) { float h; }; cbuffer buf8 : register(b8) { float i; }; cbuffer buf9 : register(b9) { float j; }; cbuffer buf10 : register(b10) { float k; }; cbuffer buf11 : register(b11) { float l; }; cbuffer buf12 : register(b12) { float m; }; float4 main(uniform float param) : sv_target { return glob * param * a * b * c * d * e * f * g * h * i * j * k * l * m; } [pixel shader fail] // Unused cbuffers still reserve their slot, and count towards the limit. float glob; cbuffer buf0 : register(b0) { float a; }; cbuffer buf1 : register(b1) { float b; }; cbuffer buf2 : register(b2) { float c; }; cbuffer buf3 : register(b3) { float d; }; cbuffer buf4 : register(b4) { float e; }; cbuffer buf5 : register(b5) { float f; }; cbuffer buf6 : register(b6) { float g; }; cbuffer buf7 : register(b7) { float h; }; cbuffer buf8 : register(b8) { float i; }; cbuffer buf9 : register(b9) { float j; }; cbuffer buf10 : register(b10) { float k; }; cbuffer buf11 : register(b11) { float l; }; cbuffer buf12 : register(b12) { float m; }; float4 main(uniform float param) : sv_target { return glob * param * a * b * c * d * e * f * g * h * i * j * k * l; } [require] shader model >= 5.1 [pixel shader fail(sm>=6)] // 5.1 and up have unlimited CBV slots. float glob; cbuffer buf0 : register(b0) { float a; }; cbuffer buf1 : register(b1) { float b; }; cbuffer buf2 : register(b2) { float c; }; cbuffer buf3 : register(b3) { float d; }; cbuffer buf4 : register(b4) { float e; }; cbuffer buf5 : register(b5) { float f; }; cbuffer buf6 : register(b6) { float g; }; cbuffer buf7 : register(b7) { float h; }; cbuffer buf8 : register(b8) { float i; }; cbuffer buf9 : register(b9) { float j; }; cbuffer buf10 : register(b10) { float k; }; cbuffer buf11 : register(b11) { float l; }; cbuffer buf12 : register(b12) { float m; }; float4 main(uniform float param) : sv_target { return glob * param * a * b * c * d * e * f * g * h * i * j * k * l * m; } [pixel shader] // SM6 doesn't support uniform parameters (why??) float glob; cbuffer buf0 : register(b0) { float a; }; cbuffer buf1 : register(b1) { float b; }; cbuffer buf2 : register(b2) { float c; }; cbuffer buf3 : register(b3) { float d; }; cbuffer buf4 : register(b4) { float e; }; cbuffer buf5 : register(b5) { float f; }; cbuffer buf6 : register(b6) { float g; }; cbuffer buf7 : register(b7) { float h; }; cbuffer buf8 : register(b8) { float i; }; cbuffer buf9 : register(b9) { float j; }; cbuffer buf10 : register(b10) { float k; }; cbuffer buf11 : register(b11) { float l; }; cbuffer buf12 : register(b12) { float m; }; cbuffer buf13 : register(b13) { float n; }; float4 main() : sv_target { return glob * a * b * c * d * e * f * g * h * i * j * k * l * m * n; }