% Uppercase register set names [pixel shader] cbuffer Constants : register(B1) { }; Texture2D tex : register(T1); float4 foo : register(C0); float4 main() : sv_target { return foo; } [test] uniform 0 float4 1.0 2.0 3.0 4.0 draw quad probe (0, 0) rgba (1.0, 2.0, 3.0, 4.0) [pixel shader fail(sm>=6)] // Annotations cbuffer cb : register(b1) < int i = 1; > { float4 m1; }; cbuffer cb < int i = 2; > { float4 m2; }; float4 main() : sv_target { return m1 + m2; } [pixel shader] // Test empty constant buffer. cbuffer Constants : register(b1) { }; float4 foo; float4 main() : sv_target { return foo; } [pixel shader fail(sm>=6)] // The 'single' modifier is not a keyword. It's meaningful only for fx_5_0. single cbuffer cb { float4 single; }; float4 main() : sv_target { return single; } [test] uniform 0 float4 1.0 2.0 3.0 4.0 draw quad probe (0, 0) rgba (1.0, 2.0, 3.0, 4.0) [pixel shader fail(sm>=6)] // 'single' modifier is usable when there is a variable with the same name cbuffer cb { float4 single; }; single cbuffer cb2 { float4 var; }; float4 main() : sv_target { return single; } [test] uniform 0 float4 1.0 2.0 3.0 4.0 draw quad probe (0, 0) rgba (1.0, 2.0, 3.0, 4.0) [pixel shader] typedef float4 single; single var; float4 main() : sv_target { return var; } [test] uniform 0 float4 1.0 2.0 3.0 4.0 draw quad probe (0, 0) rgba (1.0, 2.0, 3.0, 4.0) [pixel shader fail] typedef float4 single; single cbuffer cb { float4 var; }; float4 main() : sv_target { return var; } % SM1 buffer offset allocation follows different rules than SM4. % Those would have to be tested separately. [require] shader model >= 4.0 [pixel shader fail] cbuffer buffer { float4 a : packoffset(c1invalid_extra_chars); } float4 main() : sv_target { return 0; } % Respect register boundaries [pixel shader] cbuffer buffer { float2 a; float2 b; float2 c; float3 d; } float4 main() : sv_target { return float4(a.x, b.x, c.x, d.x); } [test] uniform 0 float4 0.0 1.0 2.0 3.0 uniform 4 float4 4.0 5.0 6.0 7.0 uniform 8 float4 8.0 9.0 10.0 11.0 uniform 12 float4 12.0 13.0 14.0 15.0 draw quad probe (0, 0) rgba (0.0, 2.0, 4.0, 8.0) [pixel shader] cbuffer buffer { float a; float b[2]; float c; } float4 main() : sv_target { return float4(a, b[0], b[1], c); } [test] uniform 0 float4 0.0 1.0 2.0 3.0 uniform 4 float4 4.0 5.0 6.0 7.0 uniform 8 float4 8.0 9.0 10.0 11.0 draw quad probe (0, 0) rgba (0.0, 4.0, 8.0, 9.0) [pixel shader] cbuffer buffer { float a; struct { float b; float c; } p; float d; } float4 main() : sv_target { return float4(a, p.b, p.c, d); } [test] uniform 0 float4 0.0 1.0 2.0 3.0 uniform 4 float4 4.0 5.0 6.0 7.0 uniform 8 float4 8.0 9.0 10.0 11.0 uniform 12 float4 12.0 13.0 14.0 15.0 draw quad probe (0, 0) rgba (0.0, 4.0, 5.0, 6.0) [pixel shader fail] // Elements cannot overlap if buffer is used. cbuffer buffer { float c : packoffset(c0); float4 a[2] : packoffset(c1); float4 b[2] : packoffset(c2); } float4 main() : sv_target { return float4(c, a[1].x, b[0].x, b[1].x); } [pixel shader fail(sm>=6)] // Elements can overlap if buffer is not used. cbuffer buffer { float4 a[2] : packoffset(c1); float4 b[2] : packoffset(c2); } float4 main() : sv_target { return 0; } [pixel shader] cbuffer buffer { float4 a : packoffset(c1); float4 b : packoffset(C2); } float4 main() : sv_target { return 100 * a + b; } [test] uniform 0 float4 1.0 2.0 3.0 4.0 uniform 4 float4 5.0 6.0 7.0 8.0 uniform 8 float4 9.0 10.0 11.0 12.0 draw quad probe (0, 0) rgba (509, 610, 711, 812) [pixel shader] struct apple { float2 a; float b; float4 c; }; cbuffer buffer { float4 foo : packoffset(c3); struct apple bar : packoffset(c1); } float4 main() : sv_target { return 1000 * foo + 100 * float4(bar.a, 0, 0) + 10 * float4(bar.b, 0, 0, 0) + bar.c; } [test] uniform 0 float4 0.0 1.0 2.0 3.0 uniform 4 float4 4.0 5.0 6.0 7.0 uniform 8 float4 8.0 9.0 10.0 11.0 uniform 12 float4 12.0 13.0 14.0 15.0 draw quad probe (0, 0) rgba (12468.0, 13509.0, 14010.0, 15011.0) [pixel shader] cbuffer buffer { float2 c : packoffset(c0.y); } float4 main() : sv_target { return float4(c, c); } [test] uniform 0 float4 1.0 2.0 3.0 4.0 draw quad probe (0, 0) rgba (2.0, 3.0, 2.0, 3.0) [pixel shader fail] // Elements must respect register boundaries. cbuffer buffer { float4 c : packoffset(c0.z); } float4 main() : sv_target { return 0; } [pixel shader fail] // Matrices must be aligned. cbuffer buffer { float1x1 m : packoffset(c0.y); } float4 main() : sv_target { return 0; } [pixel shader fail] // Arrays must be aligned. cbuffer buffer { float a[1] : packoffset(c0.y); } float4 main() : sv_target { return 0; } [pixel shader fail] // Structs must be aligned. struct apple { float p; }; cbuffer buffer { struct apple a : packoffset(c0.y); } float4 main() : sv_target { return 0; } [pixel shader fail] // Invalid offset on unused buffer. cbuffer buffer { float3 a : packoffset(c0.z); } float4 main() : sv_target { return 0; } [pixel shader fail] // Invalid offset on unused variable. cbuffer buffer { float a : packoffset(c0); float3 b : packoffset(c0.z); } float4 main() : sv_target { return a; } [pixel shader] cbuffer buffer { float4 a : packoffset(c1); float1 b : packoffset(c0); float1 c : packoffset(c0.y); } float4 main() : sv_target { return 100 * a + 10 * b + c; } [test] uniform 0 float 1.0 uniform 1 float 2.0 uniform 4 float4 5.0 6.0 7.0 8.0 draw quad probe (0, 0) rgba (512.0, 612.0, 712.0, 812.0) [pixel shader fail(sm<6)] // packoffset cannot be used unless all elements use it. cbuffer buffer { float4 a : packoffset(c0); float4 b; } float4 main() : sv_target { return a + b; } [pixel shader] cbuffer buffer { float2 c : packoffset(c0.b); } float4 main() : sv_target { return float4(c, c); } [test] uniform 0 float4 1.0 2.0 3.0 4.0 draw quad probe (0, 0) rgba (3.0, 4.0, 3.0, 4.0) [pixel shader fail] cbuffer buffer { float2 c : packoffset(c0.xy); } float4 main() : sv_target { return 0; } [pixel shader fail] cbuffer buffer { float4x4 mat : packoffset(c0._m00); } float4 main() : sv_target { return 0; } [pixel shader fail] cbuffer buffer { float4 a : packoffset(c0._m00); } float4 main() : sv_target { return 0; } [pixel shader fail] cbuffer buffer { float2 c : packoffset(c0.xz); } float4 main() : sv_target { return 0; } % packoffset cannot be used outside a constant buffer. [pixel shader fail] float4 a : packoffset(c0); float4 main() : sv_target { return 0; } [pixel shader fail] float4 foo(float4 a : packoffset(c0)) { return a; } float4 main() : sv_target { return 0; } [pixel shader fail] float4 foo(float4 a) : packoffset(c0) { return a; } float4 main() : sv_target { return 0; } [pixel shader fail] struct apple { float4 a : packoffset(c0); }; cbuffer buffer { struct apple a; } float4 main() : sv_target { return 0; } [pixel shader fail] cbuffer buffer { struct { float4 a : packoffset(c0); } s; } float4 main() : sv_target { return 0; } [srv 0] size (2d, 1, 1) 0.0 0.0 0.0 4.0 [sampler 0] filter linear linear linear address clamp clamp clamp [pixel shader] // Resources are allowed inside constant buffers but they behave as regular resources. cbuffer buffer { float4 a; Texture2D tex; sampler sam; float4 b; } float4 main() : sv_target { return a + b + tex.Sample(sam, float2(0, 0)); } [test] uniform 0 float4 1.0 0.0 0.0 0.0 uniform 4 float4 0.0 2.0 0.0 0.0 uniform 8 float4 0.0 0.0 3.0 0.0 draw quad probe (0, 0) rgba (1.0, 2.0, 0.0, 4.0) % packoffset() cannot be used to specify other types of registers [pixel shader fail] cbuffer buffer { sampler sam : packoffset(s0); } float4 main() : sv_target { return 0; } [pixel shader fail] cbuffer buffer { Texture2D tex : packoffset(t0); } float4 main() : sv_target { return 0; } [srv 0] size (2d, 1, 1) 1.0 1.0 1.0 1.0 [srv 1] size (2d, 1, 1) 2.0 2.0 2.0 2.0 [pixel shader] // packoffset() can be used in Textures, doesn't change the allocated t register. cbuffer buffer { Texture2D tex : packoffset(c1); } float4 main() : sv_target { return tex.Load(int3(0, 0, 0)); } [test] draw quad probe (0, 0) rgba (1.0, 1.0, 1.0, 1.0) % Samplers cannot have packoffset(), unless register() is also specified, or they are not used. % Note: In SM1 the rules are different: packoffset() is allowed for samplers, but they cannot be % used together with other numeric fields, which seems like a bug. [pixel shader fail(sm<6) todo] Texture2D tex; cbuffer buffer { sampler sam : packoffset(c1); } float4 main() : sv_target { return tex.Sample(sam, float2(0, 0)); } [pixel shader todo] Texture2D tex; cbuffer buffer { sampler sam : packoffset(c1) : register(s0); } float4 main() : sv_target { return tex.Sample(sam, float2(0, 0)); } [pixel shader] cbuffer buffer { sampler sam : packoffset(c1); } float4 main() : sv_target { return 0; } [pixel shader] cbuffer buffer { sampler sam : packoffset(c1); float4 a : packoffset(c0); } float4 main() : sv_target { return a; } % When packoffset is used in one field, resources are also expected to have a reservation. [pixel shader fail(sm<6)] cbuffer buffer { float4 foo : packoffset(c0); Texture2D tex; } float4 main() : sv_target { return 0; } [pixel shader fail(sm<6)] cbuffer buffer { float4 foo : packoffset(c0); sampler sam; } float4 main() : sv_target { return 0; } % register() can be used instead of packoffset(). [pixel shader] cbuffer buffer { float4 foo : packoffset(c0); Texture2D tex : register(T1); } float4 main() : sv_target { return 0; } [pixel shader] cbuffer buffer { float4 foo : packoffset(c0); sampler sam : register(s1); } float4 main() : sv_target { return 0; } % Using register() alone is considered manual packing for resources, so the other fields expect packoffset(). [pixel shader fail(sm<6)] cbuffer buffer { float4 foo; Texture2D tex : register(t1); } float4 main() : sv_target { return 0; } [pixel shader fail(sm<6)] cbuffer buffer { float4 foo; sampler sam : register(s1); } float4 main() : sv_target { return 0; } % register(c#) is ignored and does not count as manual packing. [pixel shader] cbuffer buffer { float4 foo : register(c1); Texture2D tex; } float4 main() : sv_target { return foo; } [test] uniform 0 float4 1.0 2.0 3.0 4.0 uniform 4 float4 0.0 0.0 0.0 0.0 draw quad probe (0, 0) rgba (1.0, 2.0, 3.0, 4.0) [pixel shader fail(sm<6)] cbuffer buffer { float4 foo : register(c1); Texture2D tex : register(t1); } float4 main() : sv_target { return foo; } % However, valid register reservations count as manual packing even if they % don't make sense for the given variable. % sm6 just rejects incorrect reservations. [pixel shader fail(sm>=6)] cbuffer buffer { float4 bar : register(t1); } float4 main() : sv_target { return 0; } [pixel shader fail(sm>=6)] cbuffer buffer { float4 foo : packoffset(c0); float4 bar : register(t1); Texture2D tex : register(s2); } float4 main() : sv_target { return foo + bar; } [test] uniform 0 float4 1.0 2.0 3.0 4.0 uniform 4 float4 0.1 0.2 0.3 0.4 draw quad probe (0, 0) rgba (1.1, 2.2, 3.3, 4.4) [require] shader model >= 5.0 [srv 0] size (2d, 1, 1) 0.0 0.0 0.0 0.5 [pixel shader] struct apple { float2 a; Texture2D tex; float b; }; cbuffer buffer { float4 foo : packoffset(c3); struct apple bar : packoffset(c1); struct apple baz : register(u3); } float4 main() : sv_target { return 10 * foo + float4(bar.a, 0, 0) + float4(0, 0, bar.b, 0) + bar.tex.Load(int3(0, 0, 0)); } [test] uniform 0 float4 0.0 1.0 2.0 3.0 uniform 4 float4 4.0 5.0 6.0 7.0 uniform 8 float4 8.0 9.0 10.0 11.0 uniform 12 float4 12.0 13.0 14.0 15.0 uniform 16 float4 16.0 17.0 18.0 19.0 draw quad probe (0, 0) rgba (124.0, 135.0, 146.0, 150.5) [require] shader model >= 5.1 [pixel shader fail] float4 ConstantBuffer; float4 main() : sv_target { return ConstantBuffer; } [pixel shader fail] struct s { float4 m; }; float4 cb; ConstantBuffer cb; float4 main() : sv_target { return 0; } [pixel shader fail(sm>=6)] struct s { float4 m; }; float4 main() : sv_target { // Local variable do not create resource definitions, // acting like a constant ConstantBuffer cb = (s)1; float4 var = 0.1f; var += cb.m; return var; } [test] draw quad probe (0, 0) rgba (1.1, 1.1, 1.1, 1.1) [pixel shader] struct s { float4 m; }; ConstantBuffer cb; float4 func(s arg) { return arg.m; } float4 main() : sv_target { return func(cb); } [test] uniform 0 float4 1.0 2.0 3.0 4.0 draw quad probe (0, 0) rgba (1.0, 2.0, 3.0, 4.0) [pixel shader fail todo] struct s { float4 m; }; float4 main() : sv_target { ConstantBuffer cb = (s)0; cb.m = 1.0f; return cb.m; } [pixel shader] struct s { float4 m; }; float4 var; ConstantBuffer cb[2][3] : register(b1); cbuffer cb { float4 m2; }; float4 main() : sv_target { return 0; } [pixel shader fail todo] struct s { float4 m; }; ConstantBuffer func(s arg) { return arg; } float4 main() : sv_target { return 0; } [require] shader model >= 2.0 shader model < 5.1 [pixel shader] float4 ConstantBuffer; float4 main() : sv_target { return ConstantBuffer; }