1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
|
// unbounded-arrays.hlsl
//TEST:COMPARE_HLSL:-profile cs_5_1 -entry main
//TEST:REFLECTION:-profile cs_5_1 -target hlsl -no-codegen -D__SLANG__
//
// This test is trying to make sure that we correctly compute
// reflection/layout information for shaders that make use
// of unbounded arrays of resources.
//
// We will begin by declaring various "simple" global arrays
// of resource/sampler types and try out variations on binding
// them to registers/spaces or not.
//
// We will want to confirm that Slang generates the bindings
// we expect, and we will do this by enforcing explicit
// bindings on all parameters in the HLSL baseline we'll
// compare against:
//
#ifdef __SLANG__
#define REGISTER(x, y) /* empty */
#else
#define REGISTER(x,y) : register(x,y)
#define aa aa_0
#define b0 b0_0
#define b1 b1_0
#define bb bb_0
#define c0 c0_0
#define cc cc_0
#define data data_0
#endif
// First, let's just declare a simple unbounded array of samplers.
// We expect this to be given its own register and space (for D3D12)
//
SamplerState aa[] REGISTER(s0, space2);
//
// Next, we will try to declare an array of resources with an explicit
// `register` binding. This should be set to start at that register in
// space zero (the default space), and should therefore "claim" all
// registers from that point on.
//
Texture2D bb[] : register(t2);
//
// If we have assigned register t2 and beyond in space zero to `bb`,
// then we should still be able to put other resources in there explicitly:
//
Texture2D b0 : register(t0);
Texture2D b1 : register(t1, space0);
//
// It should also be possible to give an unbounded array an explicit
// register and space, and again it should be poossible to fill
// in the space before the unbounded array:
//
TextureCube cc[] : register(t1, space1);
Texture2D c0 : register(t0, space1);
//
// As a final detail, we should allow the user to specify the space
// and no register, which should be interpreted as requesting *any*
// register in the given space.
//
// TODO: Implement support for this case.
//
// SamplerState dd[] : register(space5);
//
// With the simple cases out of the way, we will look at cases
// that involve structures and nested arrays.
//
// The first case we'll test is a structure type that contains
// two or more resources:
//
struct X
{
Texture3D t;
SamplerState s;
};
//
// The simple case should Just Work, even though the same
// syntax will fail when used with fxc (so we have to
// provide a hand-written expansion for the baseline).
//
#ifdef __SLANG__
X ee[];
#else
Texture3D ee_t_0[] REGISTER(t0, space3);
SamplerState ee_s_0[] REGISTER(s0, space4);
#endif
//
// TODO: we should probably test interactions with explicit
// bindings for a structrure.
//
// TODO: we should also test cases that mix resource and
// non-resource types, but we can't currently have an unbounded
// array of uniform data (in HLSL at least).
//
// TODO: should test arrays-of-arrays cases.
//
//
// We'll close things out with a dummy entry point just
// to allow this file to be compiled with fxc/dxc.
//
float4 use(Texture2D t, SamplerState s, float4 u)
{
return t.SampleLevel(s, u.xy + u.z, u.w);
}
float4 use(Texture3D t, SamplerState s, float4 u)
{
return t.SampleLevel(s, u.xyz, u.w);
}
float4 use(TextureCube t, SamplerState s, float4 u)
{
return t.SampleLevel(s, u.xyz, u.w);
}
RWStructuredBuffer<float4> data;
[numthreads(4,1,1)]
void main(uint3 tid : SV_DispatchThreadID)
{
int idx = int(tid.x);
float4 tmp = data[idx];
SamplerState s = aa[idx];
tmp = use(bb[idx], s, tmp);
tmp = use(b0, s, tmp);
tmp = use(b1, s, tmp);
tmp = use(cc[idx], s, tmp);
tmp = use(c0, s, tmp);
#ifdef __SLANG__
tmp = use(ee[idx].t, ee[idx].s, tmp);
#else
tmp = use(ee_t_0[idx], ee_s_0[idx], tmp);
#endif
data[idx] = tmp;
}
|