• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2019 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <string>
16 
17 #include "gmock/gmock.h"
18 #include "test/opt/assembly_builder.h"
19 #include "test/opt/pass_fixture.h"
20 #include "test/opt/pass_utils.h"
21 
22 namespace spvtools {
23 namespace opt {
24 namespace {
25 
26 using DescriptorScalarReplacementTest = PassTest<::testing::Test>;
27 
GetStructureArrayTestSpirv()28 std::string GetStructureArrayTestSpirv() {
29   // The SPIR-V for the following high-level shader:
30   // Flattening structures and arrays should result in the following binding
31   // numbers. Only the ones that are actually used in the shader should be in
32   // the final SPIR-V.
33   //
34   // globalS[0][0].t[0]  0 (used)
35   // globalS[0][0].t[1]  1
36   // globalS[0][0].s[0]  2 (used)
37   // globalS[0][0].s[1]  3
38   // globalS[0][1].t[0]  4
39   // globalS[0][1].t[1]  5
40   // globalS[0][1].s[0]  6
41   // globalS[0][1].s[1]  7
42   // globalS[1][0].t[0]  8
43   // globalS[1][0].t[1]  9
44   // globalS[1][0].s[0]  10
45   // globalS[1][0].s[1]  11
46   // globalS[1][1].t[0]  12
47   // globalS[1][1].t[1]  13 (used)
48   // globalS[1][1].s[0]  14
49   // globalS[1][1].s[1]  15 (used)
50 
51   /*
52     struct S {
53       Texture2D t[2];
54       SamplerState s[2];
55     };
56 
57     S globalS[2][2];
58 
59     float4 main() : SV_Target {
60       return globalS[0][0].t[0].Sample(globalS[0][0].s[0], float2(0,0)) +
61              globalS[1][1].t[1].Sample(globalS[1][1].s[1], float2(0,0));
62     }
63   */
64 
65   return R"(
66                OpCapability Shader
67                OpMemoryModel Logical GLSL450
68                OpEntryPoint Fragment %main "main" %out_var_SV_Target
69                OpExecutionMode %main OriginUpperLeft
70                OpName %S "S"
71                OpMemberName %S 0 "t"
72                OpMemberName %S 1 "s"
73                OpName %type_2d_image "type.2d.image"
74                OpName %type_sampler "type.sampler"
75                OpName %globalS "globalS"
76                OpName %out_var_SV_Target "out.var.SV_Target"
77                OpName %main "main"
78                OpName %src_main "src.main"
79                OpName %bb_entry "bb.entry"
80                OpName %type_sampled_image "type.sampled.image"
81                OpDecorate %out_var_SV_Target Location 0
82                OpDecorate %globalS DescriptorSet 0
83                OpDecorate %globalS Binding 0
84         %int = OpTypeInt 32 1
85       %int_0 = OpConstant %int 0
86       %int_1 = OpConstant %int 1
87       %float = OpTypeFloat 32
88     %float_0 = OpConstant %float 0
89     %v2float = OpTypeVector %float 2
90          %10 = OpConstantComposite %v2float %float_0 %float_0
91        %uint = OpTypeInt 32 0
92      %uint_2 = OpConstant %uint 2
93 %type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
94 %_arr_type_2d_image_uint_2 = OpTypeArray %type_2d_image %uint_2
95 %type_sampler = OpTypeSampler
96 %_arr_type_sampler_uint_2 = OpTypeArray %type_sampler %uint_2
97           %S = OpTypeStruct %_arr_type_2d_image_uint_2 %_arr_type_sampler_uint_2
98 %_arr_S_uint_2 = OpTypeArray %S %uint_2
99 %_arr__arr_S_uint_2_uint_2 = OpTypeArray %_arr_S_uint_2 %uint_2
100 %_ptr_UniformConstant__arr__arr_S_uint_2_uint_2 = OpTypePointer UniformConstant %_arr__arr_S_uint_2_uint_2
101     %v4float = OpTypeVector %float 4
102 %_ptr_Output_v4float = OpTypePointer Output %v4float
103        %void = OpTypeVoid
104          %24 = OpTypeFunction %void
105          %28 = OpTypeFunction %v4float
106 %_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
107 %_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler
108 %type_sampled_image = OpTypeSampledImage %type_2d_image
109     %globalS = OpVariable %_ptr_UniformConstant__arr__arr_S_uint_2_uint_2 UniformConstant
110 %out_var_SV_Target = OpVariable %_ptr_Output_v4float Output
111        %main = OpFunction %void None %24
112          %25 = OpLabel
113          %26 = OpFunctionCall %v4float %src_main
114                OpStore %out_var_SV_Target %26
115                OpReturn
116                OpFunctionEnd
117    %src_main = OpFunction %v4float None %28
118    %bb_entry = OpLabel
119          %31 = OpAccessChain %_ptr_UniformConstant_type_2d_image %globalS %int_0 %int_0 %int_0 %int_0
120          %32 = OpLoad %type_2d_image %31
121          %34 = OpAccessChain %_ptr_UniformConstant_type_sampler %globalS %int_0 %int_0 %int_1 %int_0
122          %35 = OpLoad %type_sampler %34
123          %37 = OpSampledImage %type_sampled_image %32 %35
124          %38 = OpImageSampleImplicitLod %v4float %37 %10 None
125          %39 = OpAccessChain %_ptr_UniformConstant_type_2d_image %globalS %int_1 %int_1 %int_0 %int_1
126          %40 = OpLoad %type_2d_image %39
127          %41 = OpAccessChain %_ptr_UniformConstant_type_sampler %globalS %int_1 %int_1 %int_1 %int_1
128          %42 = OpLoad %type_sampler %41
129          %43 = OpSampledImage %type_sampled_image %40 %42
130          %44 = OpImageSampleImplicitLod %v4float %43 %10 None
131          %45 = OpFAdd %v4float %38 %44
132                OpReturnValue %45
133                OpFunctionEnd
134   )";
135 }
136 
TEST_F(DescriptorScalarReplacementTest,ExpandArrayOfTextures)137 TEST_F(DescriptorScalarReplacementTest, ExpandArrayOfTextures) {
138   const std::string text = R"(
139 ; CHECK: OpDecorate [[var1:%\w+]] DescriptorSet 0
140 ; CHECK: OpDecorate [[var1]] Binding 0
141 ; CHECK: OpDecorate [[var2:%\w+]] DescriptorSet 0
142 ; CHECK: OpDecorate [[var2]] Binding 1
143 ; CHECK: OpDecorate [[var3:%\w+]] DescriptorSet 0
144 ; CHECK: OpDecorate [[var3]] Binding 2
145 ; CHECK: OpDecorate [[var4:%\w+]] DescriptorSet 0
146 ; CHECK: OpDecorate [[var4]] Binding 3
147 ; CHECK: OpDecorate [[var5:%\w+]] DescriptorSet 0
148 ; CHECK: OpDecorate [[var5]] Binding 4
149 ; CHECK: [[image_type:%\w+]] = OpTypeImage
150 ; CHECK: [[ptr_type:%\w+]] = OpTypePointer UniformConstant [[image_type]]
151 ; CHECK: [[var1]] = OpVariable [[ptr_type]] UniformConstant
152 ; CHECK: [[var2]] = OpVariable [[ptr_type]] UniformConstant
153 ; CHECK: [[var3]] = OpVariable [[ptr_type]] UniformConstant
154 ; CHECK: [[var4]] = OpVariable [[ptr_type]] UniformConstant
155 ; CHECK: [[var5]] = OpVariable [[ptr_type]] UniformConstant
156 ; CHECK: OpLoad [[image_type]] [[var1]]
157 ; CHECK: OpLoad [[image_type]] [[var2]]
158 ; CHECK: OpLoad [[image_type]] [[var3]]
159 ; CHECK: OpLoad [[image_type]] [[var4]]
160 ; CHECK: OpLoad [[image_type]] [[var5]]
161                OpCapability Shader
162                OpMemoryModel Logical GLSL450
163                OpEntryPoint Fragment %main "main"
164                OpExecutionMode %main OriginUpperLeft
165                OpSource HLSL 600
166                OpDecorate %MyTextures DescriptorSet 0
167                OpDecorate %MyTextures Binding 0
168         %int = OpTypeInt 32 1
169       %int_0 = OpConstant %int 0
170       %int_1 = OpConstant %int 1
171       %int_2 = OpConstant %int 2
172       %int_3 = OpConstant %int 3
173       %int_4 = OpConstant %int 4
174        %uint = OpTypeInt 32 0
175      %uint_5 = OpConstant %uint 5
176       %float = OpTypeFloat 32
177 %type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
178 %_arr_type_2d_image_uint_5 = OpTypeArray %type_2d_image %uint_5
179 %_ptr_UniformConstant__arr_type_2d_image_uint_5 = OpTypePointer UniformConstant %_arr_type_2d_image_uint_5
180     %v2float = OpTypeVector %float 2
181        %void = OpTypeVoid
182          %26 = OpTypeFunction %void
183 %_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
184  %MyTextures = OpVariable %_ptr_UniformConstant__arr_type_2d_image_uint_5 UniformConstant
185        %main = OpFunction %void None %26
186          %28 = OpLabel
187          %29 = OpUndef %v2float
188          %30 = OpAccessChain %_ptr_UniformConstant_type_2d_image %MyTextures %int_0
189          %31 = OpLoad %type_2d_image %30
190          %35 = OpAccessChain %_ptr_UniformConstant_type_2d_image %MyTextures %int_1
191          %36 = OpLoad %type_2d_image %35
192          %40 = OpAccessChain %_ptr_UniformConstant_type_2d_image %MyTextures %int_2
193          %41 = OpLoad %type_2d_image %40
194          %45 = OpAccessChain %_ptr_UniformConstant_type_2d_image %MyTextures %int_3
195          %46 = OpLoad %type_2d_image %45
196          %50 = OpAccessChain %_ptr_UniformConstant_type_2d_image %MyTextures %int_4
197          %51 = OpLoad %type_2d_image %50
198                OpReturn
199                OpFunctionEnd
200 
201   )";
202 
203   SinglePassRunAndMatch<DescriptorScalarReplacement>(text, true);
204 }
205 
TEST_F(DescriptorScalarReplacementTest,ExpandArrayOfSamplers)206 TEST_F(DescriptorScalarReplacementTest, ExpandArrayOfSamplers) {
207   const std::string text = R"(
208 ; CHECK: OpDecorate [[var1:%\w+]] DescriptorSet 0
209 ; CHECK: OpDecorate [[var1]] Binding 1
210 ; CHECK: OpDecorate [[var2:%\w+]] DescriptorSet 0
211 ; CHECK: OpDecorate [[var2]] Binding 2
212 ; CHECK: OpDecorate [[var3:%\w+]] DescriptorSet 0
213 ; CHECK: OpDecorate [[var3]] Binding 3
214 ; CHECK: [[sampler_type:%\w+]] = OpTypeSampler
215 ; CHECK: [[ptr_type:%\w+]] = OpTypePointer UniformConstant [[sampler_type]]
216 ; CHECK: [[var1]] = OpVariable [[ptr_type]] UniformConstant
217 ; CHECK: [[var2]] = OpVariable [[ptr_type]] UniformConstant
218 ; CHECK: [[var3]] = OpVariable [[ptr_type]] UniformConstant
219 ; CHECK: OpLoad [[sampler_type]] [[var1]]
220 ; CHECK: OpLoad [[sampler_type]] [[var2]]
221 ; CHECK: OpLoad [[sampler_type]] [[var3]]
222                OpCapability Shader
223                OpMemoryModel Logical GLSL450
224                OpEntryPoint Fragment %main "main"
225                OpExecutionMode %main OriginUpperLeft
226                OpSource HLSL 600
227                OpDecorate %MySampler DescriptorSet 0
228                OpDecorate %MySampler Binding 1
229         %int = OpTypeInt 32 1
230       %int_0 = OpConstant %int 0
231       %int_1 = OpConstant %int 1
232       %int_2 = OpConstant %int 2
233        %uint = OpTypeInt 32 0
234      %uint_3 = OpConstant %uint 3
235 %type_sampler = OpTypeSampler
236 %_arr_type_sampler_uint_3 = OpTypeArray %type_sampler %uint_3
237 %_ptr_UniformConstant__arr_type_sampler_uint_3 = OpTypePointer UniformConstant %_arr_type_sampler_uint_3
238        %void = OpTypeVoid
239          %26 = OpTypeFunction %void
240 %_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler
241   %MySampler = OpVariable %_ptr_UniformConstant__arr_type_sampler_uint_3 UniformConstant
242        %main = OpFunction %void None %26
243          %28 = OpLabel
244          %31 = OpAccessChain %_ptr_UniformConstant_type_sampler %MySampler %int_0
245          %32 = OpLoad %type_sampler %31
246          %35 = OpAccessChain %_ptr_UniformConstant_type_sampler %MySampler %int_1
247          %36 = OpLoad %type_sampler %35
248          %40 = OpAccessChain %_ptr_UniformConstant_type_sampler %MySampler %int_2
249          %41 = OpLoad %type_sampler %40
250                OpReturn
251                OpFunctionEnd
252   )";
253 
254   SinglePassRunAndMatch<DescriptorScalarReplacement>(text, true);
255 }
256 
TEST_F(DescriptorScalarReplacementTest,ExpandArrayOfSSBOs)257 TEST_F(DescriptorScalarReplacementTest, ExpandArrayOfSSBOs) {
258   // Tests the expansion of an SSBO.  Also check that an access chain with more
259   // than 1 index is correctly handled.
260   const std::string text = R"(
261 ; CHECK: OpDecorate [[var1:%\w+]] DescriptorSet 0
262 ; CHECK: OpDecorate [[var1]] Binding 0
263 ; CHECK: OpDecorate [[var2:%\w+]] DescriptorSet 0
264 ; CHECK: OpDecorate [[var2]] Binding 1
265 ; CHECK: OpTypeStruct
266 ; CHECK: [[struct_type:%\w+]] = OpTypeStruct
267 ; CHECK: [[ptr_type:%\w+]] = OpTypePointer Uniform [[struct_type]]
268 ; CHECK: [[var1]] = OpVariable [[ptr_type]] Uniform
269 ; CHECK: [[var2]] = OpVariable [[ptr_type]] Uniform
270 ; CHECK: [[ac1:%\w+]] = OpAccessChain %_ptr_Uniform_v4float [[var1]] %uint_0 %uint_0 %uint_0
271 ; CHECK: OpLoad %v4float [[ac1]]
272 ; CHECK: [[ac2:%\w+]] = OpAccessChain %_ptr_Uniform_v4float [[var2]] %uint_0 %uint_0 %uint_0
273 ; CHECK: OpLoad %v4float [[ac2]]
274                OpCapability Shader
275                OpMemoryModel Logical GLSL450
276                OpEntryPoint Fragment %main "main"
277                OpExecutionMode %main OriginUpperLeft
278                OpSource HLSL 600
279                OpDecorate %buffers DescriptorSet 0
280                OpDecorate %buffers Binding 0
281                OpMemberDecorate %S 0 Offset 0
282                OpDecorate %_runtimearr_S ArrayStride 16
283                OpMemberDecorate %type_StructuredBuffer_S 0 Offset 0
284                OpMemberDecorate %type_StructuredBuffer_S 0 NonWritable
285                OpDecorate %type_StructuredBuffer_S BufferBlock
286        %uint = OpTypeInt 32 0
287      %uint_0 = OpConstant %uint 0
288       %uint_1 = OpConstant %uint 1
289      %uint_2 = OpConstant %uint 2
290       %float = OpTypeFloat 32
291     %v4float = OpTypeVector %float 4
292           %S = OpTypeStruct %v4float
293 %_runtimearr_S = OpTypeRuntimeArray %S
294 %type_StructuredBuffer_S = OpTypeStruct %_runtimearr_S
295 %_arr_type_StructuredBuffer_S_uint_2 = OpTypeArray %type_StructuredBuffer_S %uint_2
296 %_ptr_Uniform__arr_type_StructuredBuffer_S_uint_2 = OpTypePointer Uniform %_arr_type_StructuredBuffer_S_uint_2
297 %_ptr_Uniform_type_StructuredBuffer_S = OpTypePointer Uniform %type_StructuredBuffer_S
298        %void = OpTypeVoid
299          %19 = OpTypeFunction %void
300 %_ptr_Uniform_v4float = OpTypePointer Uniform %v4float
301     %buffers = OpVariable %_ptr_Uniform__arr_type_StructuredBuffer_S_uint_2 Uniform
302        %main = OpFunction %void None %19
303          %21 = OpLabel
304          %22 = OpAccessChain %_ptr_Uniform_v4float %buffers %uint_0 %uint_0 %uint_0 %uint_0
305          %23 = OpLoad %v4float %22
306          %24 = OpAccessChain %_ptr_Uniform_type_StructuredBuffer_S %buffers %uint_1
307          %25 = OpAccessChain %_ptr_Uniform_v4float %24 %uint_0 %uint_0 %uint_0
308          %26 = OpLoad %v4float %25
309                OpReturn
310                OpFunctionEnd
311   )";
312 
313   SinglePassRunAndMatch<DescriptorScalarReplacement>(text, true);
314 }
315 
TEST_F(DescriptorScalarReplacementTest,NameNewVariables)316 TEST_F(DescriptorScalarReplacementTest, NameNewVariables) {
317   // Checks that if the original variable has a name, then the new variables
318   // will have a name derived from that name.
319   const std::string text = R"(
320 ; CHECK: OpName [[var1:%\w+]] "SSBO[0]"
321 ; CHECK: OpName [[var2:%\w+]] "SSBO[1]"
322 ; CHECK: OpDecorate [[var1]] DescriptorSet 0
323 ; CHECK: OpDecorate [[var1]] Binding 0
324 ; CHECK: OpDecorate [[var2]] DescriptorSet 0
325 ; CHECK: OpDecorate [[var2]] Binding 1
326 ; CHECK: OpTypeStruct
327 ; CHECK: [[struct_type:%\w+]] = OpTypeStruct
328 ; CHECK: [[ptr_type:%\w+]] = OpTypePointer Uniform [[struct_type]]
329 ; CHECK: [[var1]] = OpVariable [[ptr_type]] Uniform
330 ; CHECK: [[var2]] = OpVariable [[ptr_type]] Uniform
331 ; CHECK: [[ac1:%\w+]] = OpAccessChain %_ptr_Uniform_v4float [[var1]] %uint_0 %uint_0 %uint_0
332 ; CHECK: OpLoad %v4float [[ac1]]
333 ; CHECK: [[ac2:%\w+]] = OpAccessChain %_ptr_Uniform_v4float [[var2]] %uint_0 %uint_0 %uint_0
334 ; CHECK: OpLoad %v4float [[ac2]]
335                OpCapability Shader
336                OpMemoryModel Logical GLSL450
337                OpEntryPoint Fragment %main "main"
338                OpExecutionMode %main OriginUpperLeft
339                OpSource HLSL 600
340                OpName %buffers "SSBO"
341                OpDecorate %buffers DescriptorSet 0
342                OpDecorate %buffers Binding 0
343                OpMemberDecorate %S 0 Offset 0
344                OpDecorate %_runtimearr_S ArrayStride 16
345                OpMemberDecorate %type_StructuredBuffer_S 0 Offset 0
346                OpMemberDecorate %type_StructuredBuffer_S 0 NonWritable
347                OpDecorate %type_StructuredBuffer_S BufferBlock
348        %uint = OpTypeInt 32 0
349      %uint_0 = OpConstant %uint 0
350       %uint_1 = OpConstant %uint 1
351      %uint_2 = OpConstant %uint 2
352       %float = OpTypeFloat 32
353     %v4float = OpTypeVector %float 4
354           %S = OpTypeStruct %v4float
355 %_runtimearr_S = OpTypeRuntimeArray %S
356 %type_StructuredBuffer_S = OpTypeStruct %_runtimearr_S
357 %_arr_type_StructuredBuffer_S_uint_2 = OpTypeArray %type_StructuredBuffer_S %uint_2
358 %_ptr_Uniform__arr_type_StructuredBuffer_S_uint_2 = OpTypePointer Uniform %_arr_type_StructuredBuffer_S_uint_2
359 %_ptr_Uniform_type_StructuredBuffer_S = OpTypePointer Uniform %type_StructuredBuffer_S
360        %void = OpTypeVoid
361          %19 = OpTypeFunction %void
362 %_ptr_Uniform_v4float = OpTypePointer Uniform %v4float
363     %buffers = OpVariable %_ptr_Uniform__arr_type_StructuredBuffer_S_uint_2 Uniform
364        %main = OpFunction %void None %19
365          %21 = OpLabel
366          %22 = OpAccessChain %_ptr_Uniform_v4float %buffers %uint_0 %uint_0 %uint_0 %uint_0
367          %23 = OpLoad %v4float %22
368          %24 = OpAccessChain %_ptr_Uniform_type_StructuredBuffer_S %buffers %uint_1
369          %25 = OpAccessChain %_ptr_Uniform_v4float %24 %uint_0 %uint_0 %uint_0
370          %26 = OpLoad %v4float %25
371                OpReturn
372                OpFunctionEnd
373   )";
374 
375   SinglePassRunAndMatch<DescriptorScalarReplacement>(text, true);
376 }
377 
TEST_F(DescriptorScalarReplacementTest,DontExpandCBuffers)378 TEST_F(DescriptorScalarReplacementTest, DontExpandCBuffers) {
379   // Checks that constant buffers are not expanded.
380   // Constant buffers are represented as global structures, but they should not
381   // be replaced with new variables for their elements.
382   /*
383     cbuffer MyCbuffer : register(b1) {
384       float2    a;
385       float2   b;
386     };
387     float main() : A {
388       return a.x + b.y;
389     }
390   */
391   const std::string text = R"(
392 ; CHECK: OpAccessChain %_ptr_Uniform_float %MyCbuffer %int_0 %int_0
393 ; CHECK: OpAccessChain %_ptr_Uniform_float %MyCbuffer %int_1 %int_1
394                OpCapability Shader
395                OpMemoryModel Logical GLSL450
396                OpEntryPoint Vertex %main "main" %out_var_A
397                OpSource HLSL 600
398                OpName %type_MyCbuffer "type.MyCbuffer"
399                OpMemberName %type_MyCbuffer 0 "a"
400                OpMemberName %type_MyCbuffer 1 "b"
401                OpName %MyCbuffer "MyCbuffer"
402                OpName %out_var_A "out.var.A"
403                OpName %main "main"
404                OpDecorate %out_var_A Location 0
405                OpDecorate %MyCbuffer DescriptorSet 0
406                OpDecorate %MyCbuffer Binding 1
407                OpMemberDecorate %type_MyCbuffer 0 Offset 0
408                OpMemberDecorate %type_MyCbuffer 1 Offset 8
409                OpDecorate %type_MyCbuffer Block
410         %int = OpTypeInt 32 1
411       %int_0 = OpConstant %int 0
412       %int_1 = OpConstant %int 1
413       %float = OpTypeFloat 32
414     %v2float = OpTypeVector %float 2
415 %type_MyCbuffer = OpTypeStruct %v2float %v2float
416 %_ptr_Uniform_type_MyCbuffer = OpTypePointer Uniform %type_MyCbuffer
417 %_ptr_Output_float = OpTypePointer Output %float
418        %void = OpTypeVoid
419          %13 = OpTypeFunction %void
420 %_ptr_Uniform_float = OpTypePointer Uniform %float
421   %MyCbuffer = OpVariable %_ptr_Uniform_type_MyCbuffer Uniform
422   %out_var_A = OpVariable %_ptr_Output_float Output
423        %main = OpFunction %void None %13
424          %15 = OpLabel
425          %16 = OpAccessChain %_ptr_Uniform_float %MyCbuffer %int_0 %int_0
426          %17 = OpLoad %float %16
427          %18 = OpAccessChain %_ptr_Uniform_float %MyCbuffer %int_1 %int_1
428          %19 = OpLoad %float %18
429          %20 = OpFAdd %float %17 %19
430                OpStore %out_var_A %20
431                OpReturn
432                OpFunctionEnd
433 )";
434 
435   SinglePassRunAndMatch<DescriptorScalarReplacement>(text, true);
436 }
437 
TEST_F(DescriptorScalarReplacementTest,DontExpandStructuredBuffers)438 TEST_F(DescriptorScalarReplacementTest, DontExpandStructuredBuffers) {
439   // Checks that structured buffers are not expanded.
440   // Structured buffers are represented as global structures, that have one
441   // member which is a runtime array.
442   /*
443     struct S {
444       float2   a;
445       float2   b;
446     };
447     RWStructuredBuffer<S> sb;
448     float main() : A {
449       return sb[0].a.x + sb[0].b.x;
450     }
451   */
452   const std::string text = R"(
453 ; CHECK: OpAccessChain %_ptr_Uniform_float %sb %int_0 %uint_0 %int_0 %int_0
454 ; CHECK: OpAccessChain %_ptr_Uniform_float %sb %int_0 %uint_0 %int_1 %int_0
455                OpCapability Shader
456                OpMemoryModel Logical GLSL450
457                OpEntryPoint Vertex %main "main" %out_var_A
458                OpName %type_RWStructuredBuffer_S "type.RWStructuredBuffer.S"
459                OpName %S "S"
460                OpMemberName %S 0 "a"
461                OpMemberName %S 1 "b"
462                OpName %sb "sb"
463                OpName %out_var_A "out.var.A"
464                OpName %main "main"
465                OpDecorate %out_var_A Location 0
466                OpDecorate %sb DescriptorSet 0
467                OpDecorate %sb Binding 0
468                OpMemberDecorate %S 0 Offset 0
469                OpMemberDecorate %S 1 Offset 8
470                OpDecorate %_runtimearr_S ArrayStride 16
471                OpMemberDecorate %type_RWStructuredBuffer_S 0 Offset 0
472                OpDecorate %type_RWStructuredBuffer_S BufferBlock
473         %int = OpTypeInt 32 1
474       %int_0 = OpConstant %int 0
475        %uint = OpTypeInt 32 0
476      %uint_0 = OpConstant %uint 0
477       %int_1 = OpConstant %int 1
478       %float = OpTypeFloat 32
479     %v2float = OpTypeVector %float 2
480           %S = OpTypeStruct %v2float %v2float
481 %_runtimearr_S = OpTypeRuntimeArray %S
482 %type_RWStructuredBuffer_S = OpTypeStruct %_runtimearr_S
483 %_ptr_Uniform_type_RWStructuredBuffer_S = OpTypePointer Uniform %type_RWStructuredBuffer_S
484 %_ptr_Output_float = OpTypePointer Output %float
485        %void = OpTypeVoid
486          %17 = OpTypeFunction %void
487 %_ptr_Uniform_float = OpTypePointer Uniform %float
488          %sb = OpVariable %_ptr_Uniform_type_RWStructuredBuffer_S Uniform
489   %out_var_A = OpVariable %_ptr_Output_float Output
490        %main = OpFunction %void None %17
491          %19 = OpLabel
492          %20 = OpAccessChain %_ptr_Uniform_float %sb %int_0 %uint_0 %int_0 %int_0
493          %21 = OpLoad %float %20
494          %22 = OpAccessChain %_ptr_Uniform_float %sb %int_0 %uint_0 %int_1 %int_0
495          %23 = OpLoad %float %22
496          %24 = OpFAdd %float %21 %23
497                OpStore %out_var_A %24
498                OpReturn
499                OpFunctionEnd
500 )";
501 
502   SinglePassRunAndMatch<DescriptorScalarReplacement>(text, true);
503 }
504 
TEST_F(DescriptorScalarReplacementTest,StructureArrayNames)505 TEST_F(DescriptorScalarReplacementTest, StructureArrayNames) {
506   // Checks that names are properly generated for multi-dimension arrays and
507   // structure members.
508   const std::string checks = R"(
509 ; CHECK: OpName %globalS_0__0__t_0_ "globalS[0][0].t[0]"
510 ; CHECK: OpName %globalS_0__0__s_0_ "globalS[0][0].s[0]"
511 ; CHECK: OpName %globalS_1__1__t_1_ "globalS[1][1].t[1]"
512 ; CHECK: OpName %globalS_1__1__s_1_ "globalS[1][1].s[1]"
513   )";
514 
515   const std::string text = checks + GetStructureArrayTestSpirv();
516   SinglePassRunAndMatch<DescriptorScalarReplacement>(text, true);
517 }
518 
TEST_F(DescriptorScalarReplacementTest,StructureArrayBindings)519 TEST_F(DescriptorScalarReplacementTest, StructureArrayBindings) {
520   // Checks that flattening structures and arrays results in correct binding
521   // numbers.
522   const std::string checks = R"(
523 ; CHECK: OpDecorate %globalS_0__0__t_0_ Binding 0
524 ; CHECK: OpDecorate %globalS_0__0__s_0_ Binding 2
525 ; CHECK: OpDecorate %globalS_1__1__t_1_ Binding 13
526 ; CHECK: OpDecorate %globalS_1__1__s_1_ Binding 15
527   )";
528 
529   const std::string text = checks + GetStructureArrayTestSpirv();
530   SinglePassRunAndMatch<DescriptorScalarReplacement>(text, true);
531 }
532 
TEST_F(DescriptorScalarReplacementTest,StructureArrayReplacements)533 TEST_F(DescriptorScalarReplacementTest, StructureArrayReplacements) {
534   // Checks that all access chains indexing into structures and/or arrays are
535   // replaced with direct access to replacement variables.
536   const std::string checks = R"(
537 ; CHECK-NOT: OpAccessChain
538 ; CHECK: OpLoad %type_2d_image %globalS_0__0__t_0_
539 ; CHECK: OpLoad %type_sampler %globalS_0__0__s_0_
540 ; CHECK: OpLoad %type_2d_image %globalS_1__1__t_1_
541 ; CHECK: OpLoad %type_sampler %globalS_1__1__s_1_
542   )";
543 
544   const std::string text = checks + GetStructureArrayTestSpirv();
545   SinglePassRunAndMatch<DescriptorScalarReplacement>(text, true);
546 }
547 
TEST_F(DescriptorScalarReplacementTest,ResourceStructAsFunctionParam)548 TEST_F(DescriptorScalarReplacementTest, ResourceStructAsFunctionParam) {
549   // Checks that a mix of OpAccessChain, OpLoad, and OpCompositeExtract patterns
550   // can be properly replaced with replacement variables.
551   // This pattern can be seen when a global structure of resources is passed to
552   // a function.
553 
554   /* High-level source:
555   // globalS[0].t[0]        binding: 0  (used)
556   // globalS[0].t[1]        binding: 1  (used)
557   // globalS[0].tt[0].s[0]  binding: 2
558   // globalS[0].tt[0].s[1]  binding: 3  (used)
559   // globalS[0].tt[0].s[2]  binding: 4
560   // globalS[0].tt[1].s[0]  binding: 5
561   // globalS[0].tt[1].s[1]  binding: 6
562   // globalS[0].tt[1].s[2]  binding: 7  (used)
563   // globalS[1].t[0]        binding: 8  (used)
564   // globalS[1].t[1]        binding: 9  (used)
565   // globalS[1].tt[0].s[0]  binding: 10
566   // globalS[1].tt[0].s[1]  binding: 11 (used)
567   // globalS[1].tt[0].s[2]  binding: 12
568   // globalS[1].tt[1].s[0]  binding: 13
569   // globalS[1].tt[1].s[1]  binding: 14
570   // globalS[1].tt[1].s[2]  binding: 15 (used)
571 
572   struct T {
573     SamplerState s[3];
574   };
575 
576   struct S {
577     Texture2D t[2];
578     T tt[2];
579   };
580 
581   float4 tex2D(S x, float2 v) {
582     return x.t[0].Sample(x.tt[0].s[1], v) + x.t[1].Sample(x.tt[1].s[2], v);
583   }
584 
585   S globalS[2];
586 
587   float4 main() : SV_Target {
588     return tex2D(globalS[0], float2(0,0)) + tex2D(globalS[1], float2(0,0)) ;
589   }
590   */
591   const std::string shader = R"(
592                OpCapability Shader
593                OpMemoryModel Logical GLSL450
594                OpEntryPoint Fragment %main "main" %out_var_SV_Target
595                OpExecutionMode %main OriginUpperLeft
596                OpName %S "S"
597                OpMemberName %S 0 "t"
598                OpMemberName %S 1 "tt"
599                OpName %type_2d_image "type.2d.image"
600                OpName %T "T"
601                OpMemberName %T 0 "s"
602                OpName %type_sampler "type.sampler"
603                OpName %globalS "globalS"
604                OpName %out_var_SV_Target "out.var.SV_Target"
605                OpName %main "main"
606                OpName %type_sampled_image "type.sampled.image"
607                OpDecorate %out_var_SV_Target Location 0
608                OpDecorate %globalS DescriptorSet 0
609                OpDecorate %globalS Binding 0
610         %int = OpTypeInt 32 1
611       %int_0 = OpConstant %int 0
612       %float = OpTypeFloat 32
613     %float_0 = OpConstant %float 0
614     %v2float = OpTypeVector %float 2
615          %14 = OpConstantComposite %v2float %float_0 %float_0
616       %int_1 = OpConstant %int 1
617        %uint = OpTypeInt 32 0
618      %uint_2 = OpConstant %uint 2
619 %type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
620 %_arr_type_2d_image_uint_2 = OpTypeArray %type_2d_image %uint_2
621      %uint_3 = OpConstant %uint 3
622 %type_sampler = OpTypeSampler
623 %_arr_type_sampler_uint_3 = OpTypeArray %type_sampler %uint_3
624           %T = OpTypeStruct %_arr_type_sampler_uint_3
625 %_arr_T_uint_2 = OpTypeArray %T %uint_2
626           %S = OpTypeStruct %_arr_type_2d_image_uint_2 %_arr_T_uint_2
627 %_arr_S_uint_2 = OpTypeArray %S %uint_2
628 %_ptr_UniformConstant__arr_S_uint_2 = OpTypePointer UniformConstant %_arr_S_uint_2
629     %v4float = OpTypeVector %float 4
630 %_ptr_Output_v4float = OpTypePointer Output %v4float
631        %void = OpTypeVoid
632          %27 = OpTypeFunction %void
633 %_ptr_UniformConstant_S = OpTypePointer UniformConstant %S
634 %type_sampled_image = OpTypeSampledImage %type_2d_image
635     %globalS = OpVariable %_ptr_UniformConstant__arr_S_uint_2 UniformConstant
636 %out_var_SV_Target = OpVariable %_ptr_Output_v4float Output
637        %main = OpFunction %void None %27
638          %29 = OpLabel
639          %30 = OpAccessChain %_ptr_UniformConstant_S %globalS %int_0
640          %31 = OpLoad %S %30
641          %32 = OpCompositeExtract %_arr_type_2d_image_uint_2 %31 0
642          %33 = OpCompositeExtract %type_2d_image %32 0
643          %34 = OpCompositeExtract %type_2d_image %32 1
644          %35 = OpCompositeExtract %_arr_T_uint_2 %31 1
645          %36 = OpCompositeExtract %T %35 0
646          %37 = OpCompositeExtract %_arr_type_sampler_uint_3 %36 0
647          %38 = OpCompositeExtract %type_sampler %37 1
648          %39 = OpCompositeExtract %T %35 1
649          %40 = OpCompositeExtract %_arr_type_sampler_uint_3 %39 0
650          %41 = OpCompositeExtract %type_sampler %40 2
651          %42 = OpSampledImage %type_sampled_image %33 %38
652          %43 = OpImageSampleImplicitLod %v4float %42 %14 None
653          %44 = OpSampledImage %type_sampled_image %34 %41
654          %45 = OpImageSampleImplicitLod %v4float %44 %14 None
655          %46 = OpFAdd %v4float %43 %45
656          %47 = OpAccessChain %_ptr_UniformConstant_S %globalS %int_1
657          %48 = OpLoad %S %47
658          %49 = OpCompositeExtract %_arr_type_2d_image_uint_2 %48 0
659          %50 = OpCompositeExtract %type_2d_image %49 0
660          %51 = OpCompositeExtract %type_2d_image %49 1
661          %52 = OpCompositeExtract %_arr_T_uint_2 %48 1
662          %53 = OpCompositeExtract %T %52 0
663          %54 = OpCompositeExtract %_arr_type_sampler_uint_3 %53 0
664          %55 = OpCompositeExtract %type_sampler %54 1
665          %56 = OpCompositeExtract %T %52 1
666          %57 = OpCompositeExtract %_arr_type_sampler_uint_3 %56 0
667          %58 = OpCompositeExtract %type_sampler %57 2
668          %59 = OpSampledImage %type_sampled_image %50 %55
669          %60 = OpImageSampleImplicitLod %v4float %59 %14 None
670          %61 = OpSampledImage %type_sampled_image %51 %58
671          %62 = OpImageSampleImplicitLod %v4float %61 %14 None
672          %63 = OpFAdd %v4float %60 %62
673          %64 = OpFAdd %v4float %46 %63
674                OpStore %out_var_SV_Target %64
675                OpReturn
676                OpFunctionEnd
677 )";
678 
679   const std::string checks = R"(
680 ; CHECK: OpName %globalS_0__t_0_ "globalS[0].t[0]"
681 ; CHECK: OpName %globalS_0__t_1_ "globalS[0].t[1]"
682 ; CHECK: OpName %globalS_1__t_0_ "globalS[1].t[0]"
683 ; CHECK: OpName %globalS_1__t_1_ "globalS[1].t[1]"
684 ; CHECK: OpName %globalS_0__tt_0__s_1_ "globalS[0].tt[0].s[1]"
685 ; CHECK: OpName %globalS_0__tt_1__s_2_ "globalS[0].tt[1].s[2]"
686 ; CHECK: OpName %globalS_1__tt_0__s_1_ "globalS[1].tt[0].s[1]"
687 ; CHECK: OpName %globalS_1__tt_1__s_2_ "globalS[1].tt[1].s[2]"
688 ; CHECK: OpDecorate %globalS_0__t_0_ Binding 0
689 ; CHECK: OpDecorate %globalS_0__t_1_ Binding 1
690 ; CHECK: OpDecorate %globalS_1__t_0_ Binding 8
691 ; CHECK: OpDecorate %globalS_1__t_1_ Binding 9
692 ; CHECK: OpDecorate %globalS_0__tt_0__s_1_ Binding 3
693 ; CHECK: OpDecorate %globalS_0__tt_1__s_2_ Binding 7
694 ; CHECK: OpDecorate %globalS_1__tt_0__s_1_ Binding 11
695 ; CHECK: OpDecorate %globalS_1__tt_1__s_2_ Binding 15
696 
697 ; CHECK: %globalS_0__t_0_ = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
698 ; CHECK: %globalS_0__t_1_ = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
699 ; CHECK: %globalS_1__t_0_ = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
700 ; CHECK: %globalS_1__t_1_ = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
701 ; CHECK: %globalS_0__tt_0__s_1_ = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
702 ; CHECK: %globalS_0__tt_1__s_2_ = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
703 ; CHECK: %globalS_1__tt_0__s_1_ = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
704 ; CHECK: %globalS_1__tt_1__s_2_ = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
705 
706 ; CHECK:     [[img_1:%\w+]] = OpLoad %type_2d_image %globalS_0__t_0_
707 ; CHECK:     [[img_2:%\w+]] = OpLoad %type_2d_image %globalS_0__t_1_
708 ; CHECK: [[sampler_1:%\w+]] = OpLoad %type_sampler %globalS_0__tt_0__s_1_
709 ; CHECK: [[sampler_2:%\w+]] = OpLoad %type_sampler %globalS_0__tt_1__s_2_
710 
711 ; CHECK: [[sampled_img_1:%\w+]] = OpSampledImage %type_sampled_image [[img_1]] [[sampler_1]]
712 ; CHECK:      [[sample_1:%\w+]] = OpImageSampleImplicitLod %v4float [[sampled_img_1]]
713 ; CHECK: [[sampled_img_2:%\w+]] = OpSampledImage %type_sampled_image [[img_2]] [[sampler_2]]
714 ; CHECK:      [[sample_2:%\w+]] = OpImageSampleImplicitLod %v4float [[sampled_img_2]]
715 ; CHECK:                          OpFAdd %v4float [[sample_1]] [[sample_2]]
716 
717 ; CHECK:     [[img_3:%\w+]] = OpLoad %type_2d_image %globalS_1__t_0_
718 ; CHECK:     [[img_4:%\w+]] = OpLoad %type_2d_image %globalS_1__t_1_
719 ; CHECK: [[sampler_3:%\w+]] = OpLoad %type_sampler %globalS_1__tt_0__s_1_
720 ; CHECK: [[sampler_4:%\w+]] = OpLoad %type_sampler %globalS_1__tt_1__s_2_
721 
722 ; CHECK: [[sampled_img_3:%\w+]] = OpSampledImage %type_sampled_image [[img_3]] [[sampler_3]]
723 ; CHECK:      [[sample_3:%\w+]] = OpImageSampleImplicitLod %v4float [[sampled_img_3]]
724 ; CHECK: [[sampled_img_4:%\w+]] = OpSampledImage %type_sampled_image [[img_4]] [[sampler_4]]
725 ; CHECK:      [[sample_4:%\w+]] = OpImageSampleImplicitLod %v4float [[sampled_img_4]]
726 ; CHECK:                          OpFAdd %v4float [[sample_3]] [[sample_4]]
727 )";
728 
729   SinglePassRunAndMatch<DescriptorScalarReplacement>(checks + shader, true);
730 }
731 
TEST_F(DescriptorScalarReplacementTest,BindingForResourceArrayOfStructs)732 TEST_F(DescriptorScalarReplacementTest, BindingForResourceArrayOfStructs) {
733   // Check that correct binding numbers are given to an array of descriptors
734   // to structs.
735 
736   const std::string shader = R"(
737 ; CHECK: OpDecorate {{%\w+}} Binding 0
738 ; CHECK: OpDecorate {{%\w+}} Binding 1
739                OpCapability Shader
740           %1 = OpExtInstImport "GLSL.std.450"
741                OpMemoryModel Logical GLSL450
742                OpEntryPoint Fragment %2 "psmain"
743                OpExecutionMode %2 OriginUpperLeft
744                OpDecorate %5 DescriptorSet 0
745                OpDecorate %5 Binding 0
746                OpMemberDecorate %_struct_4 0 Offset 0
747                OpMemberDecorate %_struct_4 1 Offset 4
748                OpDecorate %_struct_4 Block
749       %float = OpTypeFloat 32
750         %int = OpTypeInt 32 1
751       %int_0 = OpConstant %int 0
752       %int_1 = OpConstant %int 1
753        %uint = OpTypeInt 32 0
754      %uint_2 = OpConstant %uint 2
755   %_struct_4 = OpTypeStruct %float %int
756 %_arr__struct_4_uint_2 = OpTypeArray %_struct_4 %uint_2
757 %_ptr_Uniform__arr__struct_4_uint_2 = OpTypePointer Uniform %_arr__struct_4_uint_2
758        %void = OpTypeVoid
759          %25 = OpTypeFunction %void
760 %_ptr_Uniform_int = OpTypePointer Uniform %int
761           %5 = OpVariable %_ptr_Uniform__arr__struct_4_uint_2 Uniform
762           %2 = OpFunction %void None %25
763          %29 = OpLabel
764          %40 = OpAccessChain %_ptr_Uniform_int %5 %int_0 %int_1
765          %41 = OpAccessChain %_ptr_Uniform_int %5 %int_1 %int_1
766                OpReturn
767                OpFunctionEnd
768 )";
769 
770   SinglePassRunAndMatch<DescriptorScalarReplacement>(shader, true);
771 }
772 
773 }  // namespace
774 }  // namespace opt
775 }  // namespace spvtools
776