• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 The Tint Authors.
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 <ostream>
16 
17 #include "gmock/gmock.h"
18 #include "src/reader/spirv/function.h"
19 #include "src/reader/spirv/parser_impl_test_helper.h"
20 #include "src/reader/spirv/spirv_tools_helpers_test.h"
21 
22 namespace tint {
23 namespace reader {
24 namespace spirv {
25 namespace {
26 
27 using ::testing::Eq;
28 using ::testing::HasSubstr;
29 using ::testing::Not;
30 using ::testing::StartsWith;
31 
32 using SpvParserHandleTest = SpvParserTest;
33 
Preamble()34 std::string Preamble() {
35   return R"(
36     OpCapability Shader
37     OpCapability Sampled1D
38     OpCapability Image1D
39     OpCapability StorageImageExtendedFormats
40     OpCapability ImageQuery
41     OpMemoryModel Logical Simple
42   )";
43 }
44 
FragMain()45 std::string FragMain() {
46   return R"(
47     OpEntryPoint Fragment %main "main" ; assume no IO
48     OpExecutionMode %main OriginUpperLeft
49   )";
50 }
51 
MainBody()52 std::string MainBody() {
53   return R"(
54     %main = OpFunction %void None %voidfn
55     %main_entry = OpLabel
56     OpReturn
57     OpFunctionEnd
58   )";
59 }
60 
CommonBasicTypes()61 std::string CommonBasicTypes() {
62   return R"(
63     %void = OpTypeVoid
64     %voidfn = OpTypeFunction %void
65 
66     %float = OpTypeFloat 32
67     %uint = OpTypeInt 32 0
68     %int = OpTypeInt 32 1
69 
70     %int_0 = OpConstant %int 0
71     %int_1 = OpConstant %int 1
72     %int_2 = OpConstant %int 2
73     %int_3 = OpConstant %int 3
74     %int_4 = OpConstant %int 4
75     %uint_0 = OpConstant %uint 0
76     %uint_1 = OpConstant %uint 1
77     %uint_2 = OpConstant %uint 2
78     %uint_3 = OpConstant %uint 3
79     %uint_4 = OpConstant %uint 4
80     %uint_100 = OpConstant %uint 100
81 
82     %v2int = OpTypeVector %int 2
83     %v3int = OpTypeVector %int 3
84     %v4int = OpTypeVector %int 4
85     %v2uint = OpTypeVector %uint 2
86     %v3uint = OpTypeVector %uint 3
87     %v4uint = OpTypeVector %uint 4
88     %v2float = OpTypeVector %float 2
89     %v3float = OpTypeVector %float 3
90     %v4float = OpTypeVector %float 4
91 
92     %float_null = OpConstantNull %float
93     %float_0 = OpConstant %float 0
94     %float_1 = OpConstant %float 1
95     %float_2 = OpConstant %float 2
96     %float_3 = OpConstant %float 3
97     %float_4 = OpConstant %float 4
98     %float_7 = OpConstant %float 7
99     %v2float_null = OpConstantNull %v2float
100     %v3float_null = OpConstantNull %v3float
101     %v4float_null = OpConstantNull %v4float
102 
103     %the_vi12 = OpConstantComposite %v2int %int_1 %int_2
104     %the_vi123 = OpConstantComposite %v3int %int_1 %int_2 %int_3
105     %the_vi1234 = OpConstantComposite %v4int %int_1 %int_2 %int_3 %int_4
106 
107     %the_vu12 = OpConstantComposite %v2uint %uint_1 %uint_2
108     %the_vu123 = OpConstantComposite %v3uint %uint_1 %uint_2 %uint_3
109     %the_vu1234 = OpConstantComposite %v4uint %uint_1 %uint_2 %uint_3 %uint_4
110 
111     %the_vf12 = OpConstantComposite %v2float %float_1 %float_2
112     %the_vf21 = OpConstantComposite %v2float %float_2 %float_1
113     %the_vf123 = OpConstantComposite %v3float %float_1 %float_2 %float_3
114     %the_vf1234 = OpConstantComposite %v4float %float_1 %float_2 %float_3 %float_4
115 
116 
117     %depth = OpConstant %float 0.2
118   )";
119 }
120 
CommonImageTypes()121 std::string CommonImageTypes() {
122   return R"(
123 
124 ; Define types for all sampler and texture types that can map to WGSL,
125 ; modulo texel formats for storage textures. For now, we limit
126 ; ourselves to 2-channel 32-bit texel formats.
127 
128 ; Because the SPIR-V reader also already generalizes so it can work with
129 ; combined image-samplers, we also test that too.
130 
131     %sampler = OpTypeSampler
132 
133     ; sampled images
134     %f_texture_1d          = OpTypeImage %float 1D   0 0 0 1 Unknown
135     %f_texture_2d          = OpTypeImage %float 2D   0 0 0 1 Unknown
136     %f_texture_2d_ms       = OpTypeImage %float 2D   0 0 1 1 Unknown
137     %f_texture_2d_array    = OpTypeImage %float 2D   0 1 0 1 Unknown
138     %f_texture_2d_ms_array = OpTypeImage %float 2D   0 1 1 1 Unknown ; not in WebGPU
139     %f_texture_3d          = OpTypeImage %float 3D   0 0 0 1 Unknown
140     %f_texture_cube        = OpTypeImage %float Cube 0 0 0 1 Unknown
141     %f_texture_cube_array  = OpTypeImage %float Cube 0 1 0 1 Unknown
142 
143     ; storage images
144     %f_storage_1d         = OpTypeImage %float 1D   0 0 0 2 Rg32f
145     %f_storage_2d         = OpTypeImage %float 2D   0 0 0 2 Rg32f
146     %f_storage_2d_array   = OpTypeImage %float 2D   0 1 0 2 Rg32f
147     %f_storage_3d         = OpTypeImage %float 3D   0 0 0 2 Rg32f
148 
149     ; Now all the same, but for unsigned integer sampled type.
150 
151     %u_texture_1d          = OpTypeImage %uint  1D   0 0 0 1 Unknown
152     %u_texture_2d          = OpTypeImage %uint  2D   0 0 0 1 Unknown
153     %u_texture_2d_ms       = OpTypeImage %uint  2D   0 0 1 1 Unknown
154     %u_texture_2d_array    = OpTypeImage %uint  2D   0 1 0 1 Unknown
155     %u_texture_2d_ms_array = OpTypeImage %uint  2D   0 1 1 1 Unknown ; not in WebGPU
156     %u_texture_3d          = OpTypeImage %uint  3D   0 0 0 1 Unknown
157     %u_texture_cube        = OpTypeImage %uint  Cube 0 0 0 1 Unknown
158     %u_texture_cube_array  = OpTypeImage %uint  Cube 0 1 0 1 Unknown
159 
160     %u_storage_1d         = OpTypeImage %uint  1D   0 0 0 2 Rg32ui
161     %u_storage_2d         = OpTypeImage %uint  2D   0 0 0 2 Rg32ui
162     %u_storage_2d_array   = OpTypeImage %uint  2D   0 1 0 2 Rg32ui
163     %u_storage_3d         = OpTypeImage %uint  3D   0 0 0 2 Rg32ui
164 
165     ; Now all the same, but for signed integer sampled type.
166 
167     %i_texture_1d          = OpTypeImage %int  1D   0 0 0 1 Unknown
168     %i_texture_2d          = OpTypeImage %int  2D   0 0 0 1 Unknown
169     %i_texture_2d_ms       = OpTypeImage %int  2D   0 0 1 1 Unknown
170     %i_texture_2d_array    = OpTypeImage %int  2D   0 1 0 1 Unknown
171     %i_texture_2d_ms_array = OpTypeImage %int  2D   0 1 1 1 Unknown ; not in WebGPU
172     %i_texture_3d          = OpTypeImage %int  3D   0 0 0 1 Unknown
173     %i_texture_cube        = OpTypeImage %int  Cube 0 0 0 1 Unknown
174     %i_texture_cube_array  = OpTypeImage %int  Cube 0 1 0 1 Unknown
175 
176     %i_storage_1d         = OpTypeImage %int  1D   0 0 0 2 Rg32i
177     %i_storage_2d         = OpTypeImage %int  2D   0 0 0 2 Rg32i
178     %i_storage_2d_array   = OpTypeImage %int  2D   0 1 0 2 Rg32i
179     %i_storage_3d         = OpTypeImage %int  3D   0 0 0 2 Rg32i
180 
181     ;; Now pointers to each of the above, so we can declare variables for them.
182 
183     %ptr_sampler = OpTypePointer UniformConstant %sampler
184 
185     %ptr_f_texture_1d          = OpTypePointer UniformConstant %f_texture_1d
186     %ptr_f_texture_2d          = OpTypePointer UniformConstant %f_texture_2d
187     %ptr_f_texture_2d_ms       = OpTypePointer UniformConstant %f_texture_2d_ms
188     %ptr_f_texture_2d_array    = OpTypePointer UniformConstant %f_texture_2d_array
189     %ptr_f_texture_2d_ms_array = OpTypePointer UniformConstant %f_texture_2d_ms_array
190     %ptr_f_texture_3d          = OpTypePointer UniformConstant %f_texture_3d
191     %ptr_f_texture_cube        = OpTypePointer UniformConstant %f_texture_cube
192     %ptr_f_texture_cube_array  = OpTypePointer UniformConstant %f_texture_cube_array
193 
194     ; storage images
195     %ptr_f_storage_1d         = OpTypePointer UniformConstant %f_storage_1d
196     %ptr_f_storage_2d         = OpTypePointer UniformConstant %f_storage_2d
197     %ptr_f_storage_2d_array   = OpTypePointer UniformConstant %f_storage_2d_array
198     %ptr_f_storage_3d         = OpTypePointer UniformConstant %f_storage_3d
199 
200     ; Now all the same, but for unsigned integer sampled type.
201 
202     %ptr_u_texture_1d          = OpTypePointer UniformConstant %u_texture_1d
203     %ptr_u_texture_2d          = OpTypePointer UniformConstant %u_texture_2d
204     %ptr_u_texture_2d_ms       = OpTypePointer UniformConstant %u_texture_2d_ms
205     %ptr_u_texture_2d_array    = OpTypePointer UniformConstant %u_texture_2d_array
206     %ptr_u_texture_2d_ms_array = OpTypePointer UniformConstant %u_texture_2d_ms_array
207     %ptr_u_texture_3d          = OpTypePointer UniformConstant %u_texture_3d
208     %ptr_u_texture_cube        = OpTypePointer UniformConstant %u_texture_cube
209     %ptr_u_texture_cube_array  = OpTypePointer UniformConstant %u_texture_cube_array
210 
211     %ptr_u_storage_1d         = OpTypePointer UniformConstant %u_storage_1d
212     %ptr_u_storage_2d         = OpTypePointer UniformConstant %u_storage_2d
213     %ptr_u_storage_2d_array   = OpTypePointer UniformConstant %u_storage_2d_array
214     %ptr_u_storage_3d         = OpTypePointer UniformConstant %u_storage_3d
215 
216     ; Now all the same, but for signed integer sampled type.
217 
218     %ptr_i_texture_1d          = OpTypePointer UniformConstant %i_texture_1d
219     %ptr_i_texture_2d          = OpTypePointer UniformConstant %i_texture_2d
220     %ptr_i_texture_2d_ms       = OpTypePointer UniformConstant %i_texture_2d_ms
221     %ptr_i_texture_2d_array    = OpTypePointer UniformConstant %i_texture_2d_array
222     %ptr_i_texture_2d_ms_array = OpTypePointer UniformConstant %i_texture_2d_ms_array
223     %ptr_i_texture_3d          = OpTypePointer UniformConstant %i_texture_3d
224     %ptr_i_texture_cube        = OpTypePointer UniformConstant %i_texture_cube
225     %ptr_i_texture_cube_array  = OpTypePointer UniformConstant %i_texture_cube_array
226 
227     %ptr_i_storage_1d         = OpTypePointer UniformConstant %i_storage_1d
228     %ptr_i_storage_2d         = OpTypePointer UniformConstant %i_storage_2d
229     %ptr_i_storage_2d_array   = OpTypePointer UniformConstant %i_storage_2d_array
230     %ptr_i_storage_3d         = OpTypePointer UniformConstant %i_storage_3d
231 
232   )";
233 }
234 
CommonTypes()235 std::string CommonTypes() {
236   return CommonBasicTypes() + CommonImageTypes();
237 }
238 
Bindings(std::vector<uint32_t> ids)239 std::string Bindings(std::vector<uint32_t> ids) {
240   std::ostringstream os;
241   int binding = 0;
242   for (auto id : ids) {
243     os << "  OpDecorate %" << id << " DescriptorSet 0\n"
244        << "  OpDecorate %" << id << " Binding " << binding++ << "\n";
245   }
246   return os.str();
247 }
248 
TEST_F(SpvParserHandleTest,GetMemoryObjectDeclarationForHandle_WellFormedButNotAHandle)249 TEST_F(SpvParserHandleTest,
250        GetMemoryObjectDeclarationForHandle_WellFormedButNotAHandle) {
251   const auto assembly = Preamble() + FragMain() + CommonTypes() + R"(
252      %10 = OpConstantNull %ptr_sampler
253      %20 = OpConstantNull %ptr_f_texture_1d
254   )" + MainBody();
255   auto p = parser(test::Assemble(assembly));
256   ASSERT_TRUE(p->BuildInternalModule()) << assembly;
257   const auto* sampler = p->GetMemoryObjectDeclarationForHandle(10, false);
258   const auto* image = p->GetMemoryObjectDeclarationForHandle(20, true);
259 
260   EXPECT_EQ(sampler, nullptr);
261   EXPECT_EQ(image, nullptr);
262   EXPECT_TRUE(p->error().empty());
263 
264   p->DeliberatelyInvalidSpirv();  // WGSL does not have null pointers.
265 }
266 
TEST_F(SpvParserHandleTest,GetMemoryObjectDeclarationForHandle_Variable_Direct)267 TEST_F(SpvParserHandleTest,
268        GetMemoryObjectDeclarationForHandle_Variable_Direct) {
269   const auto assembly =
270       Preamble() + FragMain() + Bindings({10, 20}) + CommonTypes() + R"(
271      %10 = OpVariable %ptr_sampler UniformConstant
272      %20 = OpVariable %ptr_f_texture_1d UniformConstant
273   )" + MainBody();
274   auto p = parser(test::Assemble(assembly));
275   ASSERT_TRUE(p->BuildInternalModule());
276   EXPECT_TRUE(p->error().empty());
277   const auto* sampler = p->GetMemoryObjectDeclarationForHandle(10, false);
278   const auto* image = p->GetMemoryObjectDeclarationForHandle(20, true);
279 
280   ASSERT_TRUE(sampler != nullptr);
281   EXPECT_EQ(sampler->result_id(), 10u);
282 
283   ASSERT_TRUE(image != nullptr);
284   EXPECT_EQ(image->result_id(), 20u);
285 }
286 
TEST_F(SpvParserHandleTest,GetMemoryObjectDeclarationForHandle_Variable_AccessChain)287 TEST_F(SpvParserHandleTest,
288        GetMemoryObjectDeclarationForHandle_Variable_AccessChain) {
289   // Show that we would generalize to arrays of handles, even though that
290   // is not supported in WGSL MVP.
291   const auto assembly =
292       Preamble() + FragMain() + Bindings({10, 20}) + CommonTypes() + R"(
293 
294      %sampler_array = OpTypeArray %sampler %uint_100
295      %image_array = OpTypeArray %f_texture_1d %uint_100
296 
297      %ptr_sampler_array = OpTypePointer UniformConstant %sampler_array
298      %ptr_image_array = OpTypePointer UniformConstant %image_array
299 
300      %10 = OpVariable %ptr_sampler_array UniformConstant
301      %20 = OpVariable %ptr_image_array UniformConstant
302 
303      %main = OpFunction %void None %voidfn
304      %entry = OpLabel
305 
306      %110 = OpAccessChain %ptr_sampler %10 %uint_1
307      %120 = OpAccessChain %ptr_f_texture_1d %20 %uint_2
308 
309      OpReturn
310      OpFunctionEnd
311   )";
312   auto p = parser(test::Assemble(assembly));
313   ASSERT_TRUE(p->BuildInternalModule());
314   EXPECT_TRUE(p->error().empty());
315   const auto* sampler = p->GetMemoryObjectDeclarationForHandle(110, false);
316   const auto* image = p->GetMemoryObjectDeclarationForHandle(120, true);
317 
318   ASSERT_TRUE(sampler != nullptr);
319   EXPECT_EQ(sampler->result_id(), 10u);
320 
321   ASSERT_TRUE(image != nullptr);
322   EXPECT_EQ(image->result_id(), 20u);
323 
324   // WGSL does not support arrays of textures and samplers.
325   p->DeliberatelyInvalidSpirv();
326 }
327 
TEST_F(SpvParserHandleTest,GetMemoryObjectDeclarationForHandle_Variable_InBoundsAccessChain)328 TEST_F(SpvParserHandleTest,
329        GetMemoryObjectDeclarationForHandle_Variable_InBoundsAccessChain) {
330   const auto assembly =
331       Preamble() + FragMain() + Bindings({10, 20}) + CommonTypes() + R"(
332 
333      %sampler_array = OpTypeArray %sampler %uint_100
334      %image_array = OpTypeArray %f_texture_1d %uint_100
335 
336      %ptr_sampler_array = OpTypePointer UniformConstant %sampler_array
337      %ptr_image_array = OpTypePointer UniformConstant %image_array
338 
339      %10 = OpVariable %ptr_sampler_array UniformConstant
340      %20 = OpVariable %ptr_image_array UniformConstant
341 
342      %main = OpFunction %void None %voidfn
343      %entry = OpLabel
344 
345      %110 = OpInBoundsAccessChain %ptr_sampler %10 %uint_1
346      %120 = OpInBoundsAccessChain %ptr_f_texture_1d %20 %uint_2
347 
348      OpReturn
349      OpFunctionEnd
350   )";
351   auto p = parser(test::Assemble(assembly));
352   ASSERT_TRUE(p->BuildInternalModule());
353   EXPECT_TRUE(p->error().empty());
354   const auto* sampler = p->GetMemoryObjectDeclarationForHandle(110, false);
355   const auto* image = p->GetMemoryObjectDeclarationForHandle(120, true);
356 
357   ASSERT_TRUE(sampler != nullptr);
358   EXPECT_EQ(sampler->result_id(), 10u);
359 
360   ASSERT_TRUE(image != nullptr);
361   EXPECT_EQ(image->result_id(), 20u);
362 
363   // WGSL does not support arrays of textures and samplers.
364   p->DeliberatelyInvalidSpirv();
365 }
366 
TEST_F(SpvParserHandleTest,GetMemoryObjectDeclarationForHandle_Variable_PtrAccessChain)367 TEST_F(SpvParserHandleTest,
368        GetMemoryObjectDeclarationForHandle_Variable_PtrAccessChain) {
369   // Show that we would generalize to arrays of handles, even though that
370   // is not supported in WGSL MVP.
371   // Use VariablePointers for the OpInBoundsPtrAccessChain.
372   const auto assembly = "OpCapability VariablePointers " + Preamble() +
373                         FragMain() + Bindings({10, 20}) + CommonTypes() + R"(
374 
375      %sampler_array = OpTypeArray %sampler %uint_100
376      %image_array = OpTypeArray %f_texture_1d %uint_100
377 
378      %ptr_sampler_array = OpTypePointer UniformConstant %sampler_array
379      %ptr_image_array = OpTypePointer UniformConstant %image_array
380 
381      %10 = OpVariable %ptr_sampler_array UniformConstant
382      %20 = OpVariable %ptr_image_array UniformConstant
383 
384      %main = OpFunction %void None %voidfn
385      %entry = OpLabel
386 
387      %110 = OpPtrAccessChain %ptr_sampler %10 %uint_1 %uint_1
388      %120 = OpPtrAccessChain %ptr_f_texture_1d %20 %uint_1 %uint_2
389 
390      OpReturn
391      OpFunctionEnd
392   )";
393   auto p = parser(test::Assemble(assembly));
394   ASSERT_TRUE(p->BuildInternalModule());
395   EXPECT_TRUE(p->error().empty());
396   const auto* sampler = p->GetMemoryObjectDeclarationForHandle(110, false);
397   const auto* image = p->GetMemoryObjectDeclarationForHandle(120, true);
398 
399   ASSERT_TRUE(sampler != nullptr);
400   EXPECT_EQ(sampler->result_id(), 10u);
401 
402   ASSERT_TRUE(image != nullptr);
403   EXPECT_EQ(image->result_id(), 20u);
404 
405   // Variable pointers is not allowed for WGSL. So don't dump it.
406   p->DeliberatelyInvalidSpirv();
407 }
408 
TEST_F(SpvParserHandleTest,GetMemoryObjectDeclarationForHandle_Variable_InBoundsPtrAccessChain)409 TEST_F(SpvParserHandleTest,
410        GetMemoryObjectDeclarationForHandle_Variable_InBoundsPtrAccessChain) {
411   // Use VariablePointers for the OpInBoundsPtrAccessChain.
412   const auto assembly = "OpCapability VariablePointers " + Preamble() +
413                         FragMain() + Bindings({10, 20}) + CommonTypes() + R"(
414 
415      %sampler_array = OpTypeArray %sampler %uint_100
416      %image_array = OpTypeArray %f_texture_1d %uint_100
417 
418      %ptr_sampler_array = OpTypePointer UniformConstant %sampler_array
419      %ptr_image_array = OpTypePointer UniformConstant %image_array
420 
421      %10 = OpVariable %ptr_sampler_array UniformConstant
422      %20 = OpVariable %ptr_image_array UniformConstant
423 
424      %main = OpFunction %void None %voidfn
425      %entry = OpLabel
426 
427      %110 = OpInBoundsPtrAccessChain %ptr_sampler %10 %uint_1 %uint_1
428      %120 = OpInBoundsPtrAccessChain %ptr_f_texture_1d %20 %uint_1 %uint_2
429 
430      OpReturn
431      OpFunctionEnd
432   )";
433   auto p = parser(test::Assemble(assembly));
434   ASSERT_TRUE(p->BuildInternalModule());
435   EXPECT_TRUE(p->error().empty());
436   const auto* sampler = p->GetMemoryObjectDeclarationForHandle(110, false);
437   const auto* image = p->GetMemoryObjectDeclarationForHandle(120, true);
438 
439   ASSERT_TRUE(sampler != nullptr);
440   EXPECT_EQ(sampler->result_id(), 10u);
441 
442   ASSERT_TRUE(image != nullptr);
443   EXPECT_EQ(image->result_id(), 20u);
444 
445   // Variable pointers is not allowed for WGSL. So don't dump it.
446   p->DeliberatelyInvalidSpirv();
447 }
448 
TEST_F(SpvParserHandleTest,GetMemoryObjectDeclarationForHandle_Variable_CopyObject)449 TEST_F(SpvParserHandleTest,
450        GetMemoryObjectDeclarationForHandle_Variable_CopyObject) {
451   const auto assembly =
452       Preamble() + FragMain() + Bindings({10, 20}) + CommonTypes() + R"(
453 
454      %10 = OpVariable %ptr_sampler UniformConstant
455      %20 = OpVariable %ptr_f_texture_1d UniformConstant
456 
457      %main = OpFunction %void None %voidfn
458      %entry = OpLabel
459 
460      %110 = OpCopyObject %ptr_sampler %10
461      %120 = OpCopyObject %ptr_f_texture_1d %20
462 
463      OpReturn
464      OpFunctionEnd
465   )";
466   auto p = parser(test::Assemble(assembly));
467   ASSERT_TRUE(p->BuildInternalModule());
468   EXPECT_TRUE(p->error().empty());
469   const auto* sampler = p->GetMemoryObjectDeclarationForHandle(110, false);
470   const auto* image = p->GetMemoryObjectDeclarationForHandle(120, true);
471 
472   ASSERT_TRUE(sampler != nullptr);
473   EXPECT_EQ(sampler->result_id(), 10u);
474 
475   ASSERT_TRUE(image != nullptr);
476   EXPECT_EQ(image->result_id(), 20u);
477 }
478 
TEST_F(SpvParserHandleTest,GetMemoryObjectDeclarationForHandle_Variable_Load)479 TEST_F(SpvParserHandleTest, GetMemoryObjectDeclarationForHandle_Variable_Load) {
480   const auto assembly =
481       Preamble() + FragMain() + Bindings({10, 20}) + CommonTypes() + R"(
482 
483      %10 = OpVariable %ptr_sampler UniformConstant
484      %20 = OpVariable %ptr_f_texture_1d UniformConstant
485 
486      %main = OpFunction %void None %voidfn
487      %entry = OpLabel
488 
489      %110 = OpLoad %sampler %10
490      %120 = OpLoad %f_texture_1d %20
491 
492      OpReturn
493      OpFunctionEnd
494   )";
495   auto p = parser(test::Assemble(assembly));
496   ASSERT_TRUE(p->BuildInternalModule());
497   EXPECT_TRUE(p->error().empty());
498   const auto* sampler = p->GetMemoryObjectDeclarationForHandle(110, false);
499   const auto* image = p->GetMemoryObjectDeclarationForHandle(120, true);
500 
501   ASSERT_TRUE(sampler != nullptr);
502   EXPECT_EQ(sampler->result_id(), 10u);
503 
504   ASSERT_TRUE(image != nullptr);
505   EXPECT_EQ(image->result_id(), 20u);
506 }
507 
TEST_F(SpvParserHandleTest,GetMemoryObjectDeclarationForHandle_Variable_SampledImage)508 TEST_F(SpvParserHandleTest,
509        GetMemoryObjectDeclarationForHandle_Variable_SampledImage) {
510   // Trace through the sampled image instruction, but in two different
511   // directions.
512   const auto assembly =
513       Preamble() + FragMain() + Bindings({10, 20}) + CommonTypes() + R"(
514      %sampled_image_type = OpTypeSampledImage %f_texture_1d
515 
516      %10 = OpVariable %ptr_sampler UniformConstant
517      %20 = OpVariable %ptr_f_texture_1d UniformConstant
518 
519      %main = OpFunction %void None %voidfn
520      %entry = OpLabel
521 
522      %s = OpLoad %sampler %10
523      %im = OpLoad %f_texture_1d %20
524      %100 = OpSampledImage %sampled_image_type %im %s
525 
526      OpReturn
527      OpFunctionEnd
528   )";
529   auto p = parser(test::Assemble(assembly));
530   ASSERT_TRUE(p->BuildInternalModule());
531   EXPECT_TRUE(p->error().empty());
532   const auto* sampler = p->GetMemoryObjectDeclarationForHandle(100, false);
533   const auto* image = p->GetMemoryObjectDeclarationForHandle(100, true);
534 
535   ASSERT_TRUE(sampler != nullptr);
536   EXPECT_EQ(sampler->result_id(), 10u);
537 
538   ASSERT_TRUE(image != nullptr);
539   EXPECT_EQ(image->result_id(), 20u);
540 }
541 
TEST_F(SpvParserHandleTest,GetMemoryObjectDeclarationForHandle_Variable_Image)542 TEST_F(SpvParserHandleTest,
543        GetMemoryObjectDeclarationForHandle_Variable_Image) {
544   const auto assembly =
545       Preamble() + FragMain() + Bindings({10, 20}) + CommonTypes() + R"(
546      %sampled_image_type = OpTypeSampledImage %f_texture_1d
547 
548      %10 = OpVariable %ptr_sampler UniformConstant
549      %20 = OpVariable %ptr_f_texture_1d UniformConstant
550 
551      %main = OpFunction %void None %voidfn
552      %entry = OpLabel
553 
554      %s = OpLoad %sampler %10
555      %im = OpLoad %f_texture_1d %20
556      %100 = OpSampledImage %sampled_image_type %im %s
557      %200 = OpImage %f_texture_1d %100
558 
559      OpReturn
560      OpFunctionEnd
561   )";
562   auto p = parser(test::Assemble(assembly));
563   ASSERT_TRUE(p->BuildInternalModule());
564   EXPECT_TRUE(p->error().empty());
565 
566   const auto* image = p->GetMemoryObjectDeclarationForHandle(200, true);
567   ASSERT_TRUE(image != nullptr);
568   EXPECT_EQ(image->result_id(), 20u);
569 }
570 
TEST_F(SpvParserHandleTest,GetMemoryObjectDeclarationForHandle_FuncParam_Direct)571 TEST_F(SpvParserHandleTest,
572        GetMemoryObjectDeclarationForHandle_FuncParam_Direct) {
573   const auto assembly = Preamble() + FragMain() + CommonTypes() + R"(
574      %fty = OpTypeFunction %void %ptr_sampler %ptr_f_texture_1d
575 
576      %func = OpFunction %void None %fty
577      %10 = OpFunctionParameter %ptr_sampler
578      %20 = OpFunctionParameter %ptr_f_texture_1d
579      %entry = OpLabel
580      OpReturn
581      OpFunctionEnd
582   )" + MainBody();
583   auto p = parser(test::Assemble(assembly));
584   ASSERT_TRUE(p->BuildInternalModule());
585   EXPECT_TRUE(p->error().empty());
586   const auto* sampler = p->GetMemoryObjectDeclarationForHandle(10, false);
587   const auto* image = p->GetMemoryObjectDeclarationForHandle(20, true);
588 
589   ASSERT_TRUE(sampler != nullptr);
590   EXPECT_EQ(sampler->result_id(), 10u);
591 
592   ASSERT_TRUE(image != nullptr);
593   EXPECT_EQ(image->result_id(), 20u);
594 
595   p->SkipDumpingPending("crbug.com/tint/1039");
596 }
597 
TEST_F(SpvParserHandleTest,GetMemoryObjectDeclarationForHandle_FuncParam_AccessChain)598 TEST_F(SpvParserHandleTest,
599        GetMemoryObjectDeclarationForHandle_FuncParam_AccessChain) {
600   // Show that we would generalize to arrays of handles, even though that
601   // is not supported in WGSL MVP.
602   const auto assembly = Preamble() + FragMain() + CommonTypes() + R"(
603      %sampler_array = OpTypeArray %sampler %uint_100
604      %image_array = OpTypeArray %f_texture_1d %uint_100
605 
606      %ptr_sampler_array = OpTypePointer UniformConstant %sampler_array
607      %ptr_image_array = OpTypePointer UniformConstant %image_array
608 
609      %fty = OpTypeFunction %void %ptr_sampler_array %ptr_image_array
610 
611      %func = OpFunction %void None %fty
612      %10 = OpFunctionParameter %ptr_sampler_array
613      %20 = OpFunctionParameter %ptr_image_array
614      %entry = OpLabel
615 
616      %110 = OpAccessChain %ptr_sampler %10 %uint_1
617      %120 = OpAccessChain %ptr_f_texture_1d %20 %uint_2
618 
619      OpReturn
620      OpFunctionEnd
621   )" + MainBody();
622   auto p = parser(test::Assemble(assembly));
623   ASSERT_TRUE(p->BuildInternalModule());
624   EXPECT_TRUE(p->error().empty());
625   const auto* sampler = p->GetMemoryObjectDeclarationForHandle(110, false);
626   const auto* image = p->GetMemoryObjectDeclarationForHandle(120, true);
627 
628   ASSERT_TRUE(sampler != nullptr);
629   EXPECT_EQ(sampler->result_id(), 10u);
630 
631   ASSERT_TRUE(image != nullptr);
632   EXPECT_EQ(image->result_id(), 20u);
633 
634   // WGSL does not support arrays of textures or samplers
635   p->DeliberatelyInvalidSpirv();
636 }
637 
TEST_F(SpvParserHandleTest,GetMemoryObjectDeclarationForHandle_FuncParam_InBoundsAccessChain)638 TEST_F(SpvParserHandleTest,
639        GetMemoryObjectDeclarationForHandle_FuncParam_InBoundsAccessChain) {
640   const auto assembly = Preamble() + FragMain() + CommonTypes() + R"(
641      %sampler_array = OpTypeArray %sampler %uint_100
642      %image_array = OpTypeArray %f_texture_1d %uint_100
643 
644      %ptr_sampler_array = OpTypePointer UniformConstant %sampler_array
645      %ptr_image_array = OpTypePointer UniformConstant %image_array
646 
647      %fty = OpTypeFunction %void %ptr_sampler_array %ptr_image_array
648 
649      %func = OpFunction %void None %fty
650      %10 = OpFunctionParameter %ptr_sampler_array
651      %20 = OpFunctionParameter %ptr_image_array
652      %entry = OpLabel
653 
654      %110 = OpInBoundsAccessChain %ptr_sampler %10 %uint_1
655      %120 = OpInBoundsAccessChain %ptr_f_texture_1d %20 %uint_2
656 
657      OpReturn
658      OpFunctionEnd
659   )" + MainBody();
660   auto p = parser(test::Assemble(assembly));
661   ASSERT_TRUE(p->BuildInternalModule());
662   EXPECT_TRUE(p->error().empty());
663   const auto* sampler = p->GetMemoryObjectDeclarationForHandle(110, false);
664   const auto* image = p->GetMemoryObjectDeclarationForHandle(120, true);
665 
666   ASSERT_TRUE(sampler != nullptr);
667   EXPECT_EQ(sampler->result_id(), 10u);
668 
669   ASSERT_TRUE(image != nullptr);
670   EXPECT_EQ(image->result_id(), 20u);
671 
672   // WGSL does not support arrays of textures or samplers
673   p->DeliberatelyInvalidSpirv();
674 }
675 
TEST_F(SpvParserHandleTest,GetMemoryObjectDeclarationForHandle_FuncParam_PtrAccessChain)676 TEST_F(SpvParserHandleTest,
677        GetMemoryObjectDeclarationForHandle_FuncParam_PtrAccessChain) {
678   // Show that we would generalize to arrays of handles, even though that
679   // is not supported in WGSL MVP.
680   const auto assembly = Preamble() + FragMain() + CommonTypes() + R"(
681      %sampler_array = OpTypeArray %sampler %uint_100
682      %image_array = OpTypeArray %f_texture_1d %uint_100
683 
684      %ptr_sampler_array = OpTypePointer UniformConstant %sampler_array
685      %ptr_image_array = OpTypePointer UniformConstant %image_array
686 
687      %fty = OpTypeFunction %void %ptr_sampler_array %ptr_image_array
688 
689      %func = OpFunction %void None %fty
690      %10 = OpFunctionParameter %ptr_sampler_array
691      %20 = OpFunctionParameter %ptr_image_array
692      %entry = OpLabel
693 
694      %110 = OpPtrAccessChain %ptr_sampler %10 %uint_1 %uint_1
695      %120 = OpPtrAccessChain %ptr_f_texture_1d %20 %uint_1 %uint_2
696 
697      OpReturn
698      OpFunctionEnd
699   )" + MainBody();
700   auto p = parser(test::Assemble(assembly));
701   ASSERT_TRUE(p->BuildInternalModule());
702   EXPECT_TRUE(p->error().empty());
703   const auto* sampler = p->GetMemoryObjectDeclarationForHandle(110, false);
704   const auto* image = p->GetMemoryObjectDeclarationForHandle(120, true);
705 
706   ASSERT_TRUE(sampler != nullptr);
707   EXPECT_EQ(sampler->result_id(), 10u);
708 
709   ASSERT_TRUE(image != nullptr);
710   EXPECT_EQ(image->result_id(), 20u);
711 
712   // Variable pointers is not allowed for WGSL. So don't dump it.
713   p->DeliberatelyInvalidSpirv();
714 }
715 
TEST_F(SpvParserHandleTest,GetMemoryObjectDeclarationForHandle_FuncParam_InBoundsPtrAccessChain)716 TEST_F(SpvParserHandleTest,
717        GetMemoryObjectDeclarationForHandle_FuncParam_InBoundsPtrAccessChain) {
718   const auto assembly = Preamble() + FragMain() + CommonTypes() + R"(
719      %sampler_array = OpTypeArray %sampler %uint_100
720      %image_array = OpTypeArray %f_texture_1d %uint_100
721 
722      %ptr_sampler_array = OpTypePointer UniformConstant %sampler_array
723      %ptr_image_array = OpTypePointer UniformConstant %image_array
724 
725      %fty = OpTypeFunction %void %ptr_sampler_array %ptr_image_array
726 
727      %func = OpFunction %void None %fty
728      %10 = OpFunctionParameter %ptr_sampler_array
729      %20 = OpFunctionParameter %ptr_image_array
730      %entry = OpLabel
731 
732      %110 = OpInBoundsPtrAccessChain %ptr_sampler %10 %uint_1 %uint_1
733      %120 = OpInBoundsPtrAccessChain %ptr_f_texture_1d %20 %uint_1 %uint_2
734 
735      OpReturn
736      OpFunctionEnd
737   )" + MainBody();
738   auto p = parser(test::Assemble(assembly));
739   ASSERT_TRUE(p->BuildInternalModule());
740   EXPECT_TRUE(p->error().empty());
741   const auto* sampler = p->GetMemoryObjectDeclarationForHandle(110, false);
742   const auto* image = p->GetMemoryObjectDeclarationForHandle(120, true);
743 
744   ASSERT_TRUE(sampler != nullptr);
745   EXPECT_EQ(sampler->result_id(), 10u);
746 
747   ASSERT_TRUE(image != nullptr);
748   EXPECT_EQ(image->result_id(), 20u);
749 
750   // Variable pointers is not allowed for WGSL. So don't dump it.
751   p->DeliberatelyInvalidSpirv();
752 }
753 
TEST_F(SpvParserHandleTest,GetMemoryObjectDeclarationForHandle_FuncParam_CopyObject)754 TEST_F(SpvParserHandleTest,
755        GetMemoryObjectDeclarationForHandle_FuncParam_CopyObject) {
756   const auto assembly = Preamble() + FragMain() + CommonTypes() + R"(
757      %fty = OpTypeFunction %void %ptr_sampler %ptr_f_texture_1d
758 
759      %func = OpFunction %void None %fty
760      %10 = OpFunctionParameter %ptr_sampler
761      %20 = OpFunctionParameter %ptr_f_texture_1d
762      %entry = OpLabel
763 
764      %110 = OpCopyObject %ptr_sampler %10
765      %120 = OpCopyObject %ptr_f_texture_1d %20
766 
767      OpReturn
768      OpFunctionEnd
769   )" + MainBody();
770   auto p = parser(test::Assemble(assembly));
771   ASSERT_TRUE(p->BuildInternalModule());
772   EXPECT_TRUE(p->error().empty());
773   const auto* sampler = p->GetMemoryObjectDeclarationForHandle(110, false);
774   const auto* image = p->GetMemoryObjectDeclarationForHandle(120, true);
775 
776   ASSERT_TRUE(sampler != nullptr);
777   EXPECT_EQ(sampler->result_id(), 10u);
778 
779   ASSERT_TRUE(image != nullptr);
780   EXPECT_EQ(image->result_id(), 20u);
781 
782   p->SkipDumpingPending("crbug.com/tint/1039");
783 }
784 
TEST_F(SpvParserHandleTest,GetMemoryObjectDeclarationForHandle_FuncParam_Load)785 TEST_F(SpvParserHandleTest,
786        GetMemoryObjectDeclarationForHandle_FuncParam_Load) {
787   const auto assembly = Preamble() + FragMain() + CommonTypes() + R"(
788      %fty = OpTypeFunction %void %ptr_sampler %ptr_f_texture_1d
789 
790      %func = OpFunction %void None %fty
791      %10 = OpFunctionParameter %ptr_sampler
792      %20 = OpFunctionParameter %ptr_f_texture_1d
793      %entry = OpLabel
794 
795      %110 = OpLoad %sampler %10
796      %120 = OpLoad %f_texture_1d %20
797 
798      OpReturn
799      OpFunctionEnd
800   )" + MainBody();
801   auto p = parser(test::Assemble(assembly));
802   ASSERT_TRUE(p->BuildInternalModule());
803   EXPECT_TRUE(p->error().empty());
804   const auto* sampler = p->GetMemoryObjectDeclarationForHandle(110, false);
805   const auto* image = p->GetMemoryObjectDeclarationForHandle(120, true);
806 
807   ASSERT_TRUE(sampler != nullptr);
808   EXPECT_EQ(sampler->result_id(), 10u);
809 
810   ASSERT_TRUE(image != nullptr);
811   EXPECT_EQ(image->result_id(), 20u);
812 
813   p->SkipDumpingPending("crbug.com/tint/1039");
814 }
815 
TEST_F(SpvParserHandleTest,GetMemoryObjectDeclarationForHandle_FuncParam_SampledImage)816 TEST_F(SpvParserHandleTest,
817        GetMemoryObjectDeclarationForHandle_FuncParam_SampledImage) {
818   // Trace through the sampled image instruction, but in two different
819   // directions.
820   const auto assembly = Preamble() + FragMain() + CommonTypes() + R"(
821      %sampled_image_type = OpTypeSampledImage %f_texture_1d
822 
823      %fty = OpTypeFunction %void %ptr_sampler %ptr_f_texture_1d
824 
825      %func = OpFunction %void None %fty
826      %10 = OpFunctionParameter %ptr_sampler
827      %20 = OpFunctionParameter %ptr_f_texture_1d
828      %entry = OpLabel
829 
830      %s = OpLoad %sampler %10
831      %im = OpLoad %f_texture_1d %20
832      %100 = OpSampledImage %sampled_image_type %im %s
833 
834      OpReturn
835      OpFunctionEnd
836   )" + MainBody();
837   auto p = parser(test::Assemble(assembly));
838   ASSERT_TRUE(p->BuildInternalModule());
839   EXPECT_TRUE(p->error().empty());
840   const auto* sampler = p->GetMemoryObjectDeclarationForHandle(100, false);
841   const auto* image = p->GetMemoryObjectDeclarationForHandle(100, true);
842 
843   ASSERT_TRUE(sampler != nullptr);
844   EXPECT_EQ(sampler->result_id(), 10u);
845 
846   ASSERT_TRUE(image != nullptr);
847   EXPECT_EQ(image->result_id(), 20u);
848 
849   p->SkipDumpingPending("crbug.com/tint/1039");
850 }
851 
TEST_F(SpvParserHandleTest,GetMemoryObjectDeclarationForHandle_FuncParam_Image)852 TEST_F(SpvParserHandleTest,
853        GetMemoryObjectDeclarationForHandle_FuncParam_Image) {
854   const auto assembly = Preamble() + FragMain() + CommonTypes() + R"(
855      %sampled_image_type = OpTypeSampledImage %f_texture_1d
856 
857      %fty = OpTypeFunction %void %ptr_sampler %ptr_f_texture_1d
858 
859      %func = OpFunction %void None %fty
860      %10 = OpFunctionParameter %ptr_sampler
861      %20 = OpFunctionParameter %ptr_f_texture_1d
862      %entry = OpLabel
863 
864      %s = OpLoad %sampler %10
865      %im = OpLoad %f_texture_1d %20
866      %100 = OpSampledImage %sampled_image_type %im %s
867      %200 = OpImage %f_texture_1d %100
868 
869      OpReturn
870      OpFunctionEnd
871   )" + MainBody();
872   auto p = parser(test::Assemble(assembly));
873   ASSERT_TRUE(p->BuildInternalModule());
874   EXPECT_TRUE(p->error().empty());
875 
876   const auto* image = p->GetMemoryObjectDeclarationForHandle(200, true);
877   ASSERT_TRUE(image != nullptr);
878   EXPECT_EQ(image->result_id(), 20u);
879 
880   p->SkipDumpingPending("crbug.com/tint/1039");
881 }
882 
883 // Test RegisterHandleUsage, sampled image cases
884 
885 struct UsageImageAccessCase {
886   std::string inst;
887   std::string expected_sampler_usage;
888   std::string expected_image_usage;
889 };
operator <<(std::ostream & out,const UsageImageAccessCase & c)890 inline std::ostream& operator<<(std::ostream& out,
891                                 const UsageImageAccessCase& c) {
892   out << "UsageImageAccessCase(" << c.inst << ", " << c.expected_sampler_usage
893       << ", " << c.expected_image_usage << ")";
894   return out;
895 }
896 
897 using SpvParserHandleTest_RegisterHandleUsage_SampledImage =
898     SpvParserTestBase<::testing::TestWithParam<UsageImageAccessCase>>;
899 
TEST_P(SpvParserHandleTest_RegisterHandleUsage_SampledImage,Variable)900 TEST_P(SpvParserHandleTest_RegisterHandleUsage_SampledImage, Variable) {
901   const std::string inst = GetParam().inst;
902   const auto assembly = Preamble() + FragMain() + Bindings({10, 20}) +
903                         CommonTypes() + R"(
904      %si_ty = OpTypeSampledImage %f_texture_2d
905      %coords = OpConstantNull %v2float
906      %coords3d = OpConstantNull %v3float ; needed for Proj variants
907 
908      %10 = OpVariable %ptr_sampler UniformConstant
909      %20 = OpVariable %ptr_f_texture_2d UniformConstant
910 
911      %main = OpFunction %void None %voidfn
912      %entry = OpLabel
913 
914      %sam = OpLoad %sampler %10
915      %im = OpLoad %f_texture_2d %20
916      %sampled_image = OpSampledImage %si_ty %im %sam
917 )" + GetParam().inst + R"(
918 
919      OpReturn
920      OpFunctionEnd
921   )";
922   auto p = parser(test::Assemble(assembly));
923   ASSERT_TRUE(p->BuildInternalModule());
924   EXPECT_TRUE(p->RegisterHandleUsage());
925   EXPECT_TRUE(p->error().empty());
926   Usage su = p->GetHandleUsage(10);
927   Usage iu = p->GetHandleUsage(20);
928 
929   EXPECT_THAT(su.to_str(), Eq(GetParam().expected_sampler_usage));
930   EXPECT_THAT(iu.to_str(), Eq(GetParam().expected_image_usage));
931 
932   if (inst.find("Gather") != std::string::npos) {
933     // WGSL does not support Gather instructions yet.
934     // So don't emit them as part of a "passing" corpus.
935     p->DeliberatelyInvalidSpirv();
936   }
937   if (inst.find("ImageQueryLod") != std::string::npos) {
938     // WGSL does not support querying image level of detail.
939     // So don't emit them as part of a "passing" corpus.
940     p->DeliberatelyInvalidSpirv();
941   }
942   if (inst.find("ImageSampleDrefExplicitLod") != std::string::npos) {
943     p->SkipDumpingPending("crbug.com/tint/425");  // gpuweb issue #1319
944   }
945 }
946 
TEST_P(SpvParserHandleTest_RegisterHandleUsage_SampledImage,FunctionParam)947 TEST_P(SpvParserHandleTest_RegisterHandleUsage_SampledImage, FunctionParam) {
948   const std::string inst = GetParam().inst;
949   const auto assembly = Preamble() + FragMain() + Bindings({10, 20}) +
950                         CommonTypes() + R"(
951      %f_ty = OpTypeFunction %void %ptr_sampler %ptr_f_texture_2d
952      %si_ty = OpTypeSampledImage %f_texture_2d
953      %coords = OpConstantNull %v2float
954      %coords3d = OpConstantNull %v3float ; needed for Proj variants
955      %component = OpConstant %uint 1
956 
957      %10 = OpVariable %ptr_sampler UniformConstant
958      %20 = OpVariable %ptr_f_texture_2d UniformConstant
959 
960      %func = OpFunction %void None %f_ty
961      %110 = OpFunctionParameter %ptr_sampler
962      %120 = OpFunctionParameter %ptr_f_texture_2d
963      %func_entry = OpLabel
964      %sam = OpLoad %sampler %110
965      %im = OpLoad %f_texture_2d %120
966      %sampled_image = OpSampledImage %si_ty %im %sam
967 
968 )" + inst + R"(
969 
970      OpReturn
971      OpFunctionEnd
972 
973      %main = OpFunction %void None %voidfn
974      %entry = OpLabel
975      %foo = OpFunctionCall %void %func %10 %20
976      OpReturn
977      OpFunctionEnd
978   )";
979   auto p = parser(test::Assemble(assembly));
980   ASSERT_TRUE(p->BuildInternalModule()) << p->error() << assembly << std::endl;
981   EXPECT_TRUE(p->RegisterHandleUsage()) << p->error() << assembly << std::endl;
982   EXPECT_TRUE(p->error().empty()) << p->error() << assembly << std::endl;
983   Usage su = p->GetHandleUsage(10);
984   Usage iu = p->GetHandleUsage(20);
985 
986   EXPECT_THAT(su.to_str(), Eq(GetParam().expected_sampler_usage));
987   EXPECT_THAT(iu.to_str(), Eq(GetParam().expected_image_usage));
988 
989   if (inst.find("Gather") != std::string::npos) {
990     // WGSL does not support Gather instructions yet.
991     // So don't emit them as part of a "passing" corpus.
992     p->DeliberatelyInvalidSpirv();
993   }
994   if (inst.find("ImageQueryLod") != std::string::npos) {
995     // WGSL does not support querying image level of detail.
996     // So don't emit them as part of a "passing" corpus.
997     p->DeliberatelyInvalidSpirv();
998   }
999   p->SkipDumpingPending("crbug.com/tint/785");
1000 }
1001 
1002 INSTANTIATE_TEST_SUITE_P(
1003     Samples,
1004     SpvParserHandleTest_RegisterHandleUsage_SampledImage,
1005     ::testing::Values(
1006 
1007         // Test image gather even though WGSL doesn't support it yet.
1008 
1009         // OpImageGather
1010         UsageImageAccessCase{"%result = OpImageGather "
1011                              "%v4float %sampled_image %coords %uint_1",
1012                              "Usage(Sampler( ))",
1013                              "Usage(Texture( is_sampled ))"},
1014         // OpImageDrefGather
1015         UsageImageAccessCase{"%result = OpImageDrefGather "
1016                              "%v4float %sampled_image %coords %depth",
1017                              "Usage(Sampler( comparison ))",
1018                              "Usage(Texture( is_sampled depth ))"},
1019 
1020         // Sample the texture.
1021 
1022         // OpImageSampleImplicitLod
1023         UsageImageAccessCase{"%result = OpImageSampleImplicitLod "
1024                              "%v4float %sampled_image %coords",
1025                              "Usage(Sampler( ))",
1026                              "Usage(Texture( is_sampled ))"},
1027         // OpImageSampleExplicitLod
1028         UsageImageAccessCase{"%result = OpImageSampleExplicitLod "
1029                              "%v4float %sampled_image %coords Lod %float_null",
1030                              "Usage(Sampler( ))",
1031                              "Usage(Texture( is_sampled ))"},
1032         // OpImageSampleDrefImplicitLod
1033         UsageImageAccessCase{"%result = OpImageSampleDrefImplicitLod "
1034                              "%float %sampled_image %coords %depth",
1035                              "Usage(Sampler( comparison ))",
1036                              "Usage(Texture( is_sampled depth ))"},
1037         // OpImageSampleDrefExplicitLod
1038         UsageImageAccessCase{
1039             "%result = OpImageSampleDrefExplicitLod "
1040             "%float %sampled_image %coords %depth Lod %float_null",
1041             "Usage(Sampler( comparison ))",
1042             "Usage(Texture( is_sampled depth ))"},
1043 
1044         // Sample the texture, with *Proj* variants, even though WGSL doesn't
1045         // support them.
1046 
1047         // OpImageSampleProjImplicitLod
1048         UsageImageAccessCase{"%result = OpImageSampleProjImplicitLod "
1049                              "%v4float %sampled_image %coords3d",
1050                              "Usage(Sampler( ))",
1051                              "Usage(Texture( is_sampled ))"},
1052         // OpImageSampleProjExplicitLod
1053         UsageImageAccessCase{
1054             "%result = OpImageSampleProjExplicitLod "
1055             "%v4float %sampled_image %coords3d Lod %float_null",
1056             "Usage(Sampler( ))", "Usage(Texture( is_sampled ))"},
1057         // OpImageSampleProjDrefImplicitLod
1058         UsageImageAccessCase{"%result = OpImageSampleProjDrefImplicitLod "
1059                              "%float %sampled_image %coords3d %depth",
1060                              "Usage(Sampler( comparison ))",
1061                              "Usage(Texture( is_sampled depth ))"},
1062         // OpImageSampleProjDrefExplicitLod
1063         UsageImageAccessCase{
1064             "%result = OpImageSampleProjDrefExplicitLod "
1065             "%float %sampled_image %coords3d %depth Lod %float_null",
1066             "Usage(Sampler( comparison ))",
1067             "Usage(Texture( is_sampled depth ))"},
1068 
1069         // OpImageQueryLod
1070         UsageImageAccessCase{
1071             "%result = OpImageQueryLod %v2float %sampled_image %coords",
1072             "Usage(Sampler( ))", "Usage(Texture( is_sampled ))"}));
1073 
1074 // Test RegisterHandleUsage, raw image cases.
1075 // For these we test the use of an image value directly, and not combined
1076 // with the sampler. The image still could be of sampled image type.
1077 
1078 struct UsageRawImageCase {
1079   std::string type;  // Example: f_storage_1d or f_texture_1d
1080   std::string inst;
1081   std::string expected_image_usage;
1082 };
operator <<(std::ostream & out,const UsageRawImageCase & c)1083 inline std::ostream& operator<<(std::ostream& out, const UsageRawImageCase& c) {
1084   out << "UsageRawImageCase(" << c.type << ", " << c.inst << ", "
1085       << c.expected_image_usage << ")";
1086   return out;
1087 }
1088 
1089 using SpvParserHandleTest_RegisterHandleUsage_RawImage =
1090     SpvParserTestBase<::testing::TestWithParam<UsageRawImageCase>>;
1091 
TEST_P(SpvParserHandleTest_RegisterHandleUsage_RawImage,Variable)1092 TEST_P(SpvParserHandleTest_RegisterHandleUsage_RawImage, Variable) {
1093   const bool is_storage = GetParam().type.find("storage") != std::string::npos;
1094   const bool is_write = GetParam().inst.find("ImageWrite") != std::string::npos;
1095   const auto assembly = Preamble() + FragMain() + Bindings({20}) +
1096                         (is_storage ? std::string("OpDecorate %20 ") +
1097                                           std::string(is_write ? "NonReadable"
1098                                                                : "NonWritable")
1099                                     : std::string("")) +
1100                         " " + CommonTypes() + R"(
1101      %20 = OpVariable %ptr_)" +
1102                         GetParam().type + R"( UniformConstant
1103 
1104      %main = OpFunction %void None %voidfn
1105      %entry = OpLabel
1106 
1107      %im = OpLoad %)" + GetParam().type +
1108                         R"( %20
1109 )" + GetParam().inst + R"(
1110 
1111      OpReturn
1112      OpFunctionEnd
1113   )";
1114   auto p = parser(test::Assemble(assembly));
1115   ASSERT_TRUE(p->BuildInternalModule());
1116   EXPECT_TRUE(p->RegisterHandleUsage());
1117   EXPECT_TRUE(p->error().empty());
1118 
1119   Usage iu = p->GetHandleUsage(20);
1120   EXPECT_THAT(iu.to_str(), Eq(GetParam().expected_image_usage));
1121 
1122   Usage su = p->GetHandleUsage(20);
1123 }
1124 
TEST_P(SpvParserHandleTest_RegisterHandleUsage_RawImage,FunctionParam)1125 TEST_P(SpvParserHandleTest_RegisterHandleUsage_RawImage, FunctionParam) {
1126   const bool is_storage = GetParam().type.find("storage") != std::string::npos;
1127   const bool is_write = GetParam().inst.find("ImageWrite") != std::string::npos;
1128   const auto assembly = Preamble() + FragMain() + Bindings({20}) +
1129                         (is_storage ? std::string("OpDecorate %20 ") +
1130                                           std::string(is_write ? "NonReadable"
1131                                                                : "NonWritable")
1132                                     : std::string("")) +
1133                         " " + CommonTypes() + R"(
1134      %f_ty = OpTypeFunction %void %ptr_)" +
1135                         GetParam().type + R"(
1136 
1137      %20 = OpVariable %ptr_)" +
1138                         GetParam().type + R"( UniformConstant
1139 
1140      %func = OpFunction %void None %f_ty
1141      %i_param = OpFunctionParameter %ptr_)" +
1142                         GetParam().type + R"(
1143      %func_entry = OpLabel
1144      %im = OpLoad %)" + GetParam().type +
1145                         R"( %i_param
1146 
1147 )" + GetParam().inst + R"(
1148 
1149      OpReturn
1150      OpFunctionEnd
1151 
1152      %main = OpFunction %void None %voidfn
1153      %entry = OpLabel
1154      %foo = OpFunctionCall %void %func %20
1155      OpReturn
1156      OpFunctionEnd
1157   )";
1158   auto p = parser(test::Assemble(assembly));
1159   ASSERT_TRUE(p->BuildInternalModule());
1160   EXPECT_TRUE(p->RegisterHandleUsage());
1161   EXPECT_TRUE(p->error().empty());
1162   Usage iu = p->GetHandleUsage(20);
1163 
1164   EXPECT_THAT(iu.to_str(), Eq(GetParam().expected_image_usage));
1165 
1166   // Textures and samplers not yet supported as function parameters.
1167   p->SkipDumpingPending("crbug.com/tint/785");
1168 }
1169 
1170 INSTANTIATE_TEST_SUITE_P(
1171     Samples,
1172     SpvParserHandleTest_RegisterHandleUsage_RawImage,
1173     ::testing::Values(
1174 
1175         // OpImageRead
1176         UsageRawImageCase{"f_storage_1d",
1177                           "%result = OpImageRead %v4float %im %uint_1",
1178                           "Usage(Texture( read ))"},
1179 
1180         // OpImageWrite
1181         UsageRawImageCase{"f_storage_1d",
1182                           "OpImageWrite %im %uint_1 %v4float_null",
1183                           "Usage(Texture( write ))"},
1184 
1185         // OpImageFetch
1186         UsageRawImageCase{"f_texture_1d",
1187                           "%result = OpImageFetch "
1188                           "%v4float %im %uint_0",
1189                           "Usage(Texture( is_sampled ))"},
1190 
1191         // Image queries
1192 
1193         // OpImageQuerySizeLod
1194         UsageRawImageCase{"f_texture_2d",
1195                           "%result = OpImageQuerySizeLod "
1196                           "%v2uint %im %uint_1",
1197                           "Usage(Texture( is_sampled ))"},
1198 
1199         // OpImageQuerySize
1200         // Could be MS=1 or storage image. So it's non-committal.
1201         UsageRawImageCase{"f_storage_2d",
1202                           "%result = OpImageQuerySize "
1203                           "%v2uint %im",
1204                           "Usage()"},
1205 
1206         // OpImageQueryLevels
1207         UsageRawImageCase{"f_texture_2d",
1208                           "%result = OpImageQueryLevels "
1209                           "%uint %im",
1210                           "Usage(Texture( ))"},
1211 
1212         // OpImageQuerySamples
1213         UsageRawImageCase{"f_texture_2d_ms",
1214                           "%result = OpImageQuerySamples "
1215                           "%uint %im",
1216                           "Usage(Texture( is_sampled ms ))"}));
1217 
1218 // Test emission of handle variables.
1219 
1220 // Test emission of variables where we don't have enough clues from their
1221 // use in image access instructions in executable code.  For these we have
1222 // to infer usage from the SPIR-V sampler or image type.
1223 struct DeclUnderspecifiedHandleCase {
1224   std::string decorations;  // SPIR-V decorations
1225   std::string inst;         // SPIR-V variable declarations
1226   std::string var_decl;     // WGSL variable declaration
1227 };
operator <<(std::ostream & out,const DeclUnderspecifiedHandleCase & c)1228 inline std::ostream& operator<<(std::ostream& out,
1229                                 const DeclUnderspecifiedHandleCase& c) {
1230   out << "DeclUnderspecifiedHandleCase(" << c.inst << "\n" << c.var_decl << ")";
1231   return out;
1232 }
1233 
1234 using SpvParserHandleTest_DeclUnderspecifiedHandle =
1235     SpvParserTestBase<::testing::TestWithParam<DeclUnderspecifiedHandleCase>>;
1236 
TEST_P(SpvParserHandleTest_DeclUnderspecifiedHandle,Variable)1237 TEST_P(SpvParserHandleTest_DeclUnderspecifiedHandle, Variable) {
1238   const auto assembly = Preamble() + R"(
1239      OpEntryPoint Fragment %main "main"
1240      OpExecutionMode %main OriginUpperLeft
1241      OpDecorate %10 DescriptorSet 0
1242      OpDecorate %10 Binding 0
1243 )" + GetParam().decorations +
1244                         CommonTypes() + GetParam().inst +
1245                         R"(
1246 
1247      %main = OpFunction %void None %voidfn
1248      %entry = OpLabel
1249      OpReturn
1250      OpFunctionEnd
1251   )";
1252   auto p = parser(test::Assemble(assembly));
1253   ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
1254   EXPECT_TRUE(p->error().empty()) << p->error();
1255   const auto program = test::ToString(p->program());
1256   EXPECT_THAT(program, HasSubstr(GetParam().var_decl)) << program;
1257 }
1258 
1259 INSTANTIATE_TEST_SUITE_P(
1260     Samplers,
1261     SpvParserHandleTest_DeclUnderspecifiedHandle,
1262     ::testing::Values(
1263 
1264         DeclUnderspecifiedHandleCase{
1265             "", R"(
1266          %ptr = OpTypePointer UniformConstant %sampler
1267          %10 = OpVariable %ptr UniformConstant
1268 )",
1269             R"([[group(0), binding(0)]] var x_10 : sampler;)"}));
1270 
1271 INSTANTIATE_TEST_SUITE_P(
1272     Images,
1273     SpvParserHandleTest_DeclUnderspecifiedHandle,
1274     ::testing::Values(
1275 
1276         DeclUnderspecifiedHandleCase{
1277             "", R"(
1278          %10 = OpVariable %ptr_f_texture_1d UniformConstant
1279 )",
1280             R"([[group(0), binding(0)]] var x_10 : texture_1d<f32>;)"},
1281         DeclUnderspecifiedHandleCase{
1282             R"(
1283          OpDecorate %10 NonWritable
1284 )",
1285             R"(
1286          %10 = OpVariable %ptr_f_storage_1d UniformConstant
1287 )",
1288             R"([[group(0), binding(0)]] var x_10 : texture_1d<f32>;)"},
1289         DeclUnderspecifiedHandleCase{
1290             R"(
1291          OpDecorate %10 NonReadable
1292 )",
1293             R"(
1294          %10 = OpVariable %ptr_f_storage_1d UniformConstant
1295 )",
1296             R"([[group(0), binding(0)]] var x_10 : texture_storage_1d<rg32float, write>;)"}));
1297 
1298 // Test handle declaration or error, when there is an image access.
1299 
1300 struct ImageDeclCase {
1301   // SPIR-V image type, excluding result ID and opcode
1302   std::string spirv_image_type_details;
1303   std::string spirv_image_access;  // Optional instruction to provoke use
1304   std::string expected_error;
1305   std::string expected_decl;
1306 };
1307 
operator <<(std::ostream & out,const ImageDeclCase & c)1308 inline std::ostream& operator<<(std::ostream& out, const ImageDeclCase& c) {
1309   out << "ImageDeclCase(" << c.spirv_image_type_details << "\n"
1310       << "access: " << c.spirv_image_access << "\n"
1311       << "error: " << c.expected_error << "\n"
1312       << "decl:" << c.expected_decl << "\n)";
1313   return out;
1314 }
1315 
1316 using SpvParserHandleTest_ImageDeclTest =
1317     SpvParserTestBase<::testing::TestWithParam<ImageDeclCase>>;
1318 
TEST_P(SpvParserHandleTest_ImageDeclTest,DeclareAndUseHandle)1319 TEST_P(SpvParserHandleTest_ImageDeclTest, DeclareAndUseHandle) {
1320   // Only declare the sampled image type, and the associated variable
1321   // if the requested image type is a sampled image type and not multisampled.
1322   const bool is_sampled_image_type = GetParam().spirv_image_type_details.find(
1323                                          "0 1 Unknown") != std::string::npos;
1324   const auto assembly =
1325       Preamble() + R"(
1326      OpEntryPoint Fragment %100 "main"
1327      OpExecutionMode %100 OriginUpperLeft
1328      OpName %float_var "float_var"
1329      OpName %ptr_float "ptr_float"
1330      OpName %i1 "i1"
1331      OpName %vi12 "vi12"
1332      OpName %vi123 "vi123"
1333      OpName %vi1234 "vi1234"
1334      OpName %u1 "u1"
1335      OpName %vu12 "vu12"
1336      OpName %vu123 "vu123"
1337      OpName %vu1234 "vu1234"
1338      OpName %f1 "f1"
1339      OpName %vf12 "vf12"
1340      OpName %vf123 "vf123"
1341      OpName %vf1234 "vf1234"
1342      OpDecorate %10 DescriptorSet 0
1343      OpDecorate %10 Binding 0
1344      OpDecorate %20 DescriptorSet 2
1345      OpDecorate %20 Binding 1
1346      OpDecorate %30 DescriptorSet 0
1347      OpDecorate %30 Binding 1
1348 )" + CommonBasicTypes() +
1349       R"(
1350      %sampler = OpTypeSampler
1351      %ptr_sampler = OpTypePointer UniformConstant %sampler
1352      %im_ty = OpTypeImage )" +
1353       GetParam().spirv_image_type_details + R"(
1354      %ptr_im_ty = OpTypePointer UniformConstant %im_ty
1355 )" + (is_sampled_image_type ? " %si_ty = OpTypeSampledImage %im_ty " : "") +
1356       R"(
1357 
1358      %ptr_float = OpTypePointer Function %float
1359 
1360      %10 = OpVariable %ptr_sampler UniformConstant
1361      %20 = OpVariable %ptr_im_ty UniformConstant
1362      %30 = OpVariable %ptr_sampler UniformConstant ; comparison sampler, when needed
1363 
1364      %100 = OpFunction %void None %voidfn
1365      %entry = OpLabel
1366 
1367      %float_var = OpVariable %ptr_float Function
1368 
1369      %i1 = OpCopyObject %int %int_1
1370      %vi12 = OpCopyObject %v2int %the_vi12
1371      %vi123 = OpCopyObject %v3int %the_vi123
1372      %vi1234 = OpCopyObject %v4int %the_vi1234
1373 
1374      %u1 = OpCopyObject %uint %uint_1
1375      %vu12 = OpCopyObject %v2uint %the_vu12
1376      %vu123 = OpCopyObject %v3uint %the_vu123
1377      %vu1234 = OpCopyObject %v4uint %the_vu1234
1378 
1379      %f1 = OpCopyObject %float %float_1
1380      %vf12 = OpCopyObject %v2float %the_vf12
1381      %vf123 = OpCopyObject %v3float %the_vf123
1382      %vf1234 = OpCopyObject %v4float %the_vf1234
1383 
1384      %sam = OpLoad %sampler %10
1385      %im = OpLoad %im_ty %20
1386 
1387 )" +
1388       (is_sampled_image_type
1389            ? " %sampled_image = OpSampledImage %si_ty %im %sam "
1390            : "") +
1391       GetParam().spirv_image_access +
1392       R"(
1393      ; Use an anchor for the cases when the image access doesn't have a result ID.
1394      %1000 = OpCopyObject %uint %uint_0
1395 
1396      OpReturn
1397      OpFunctionEnd
1398   )";
1399   auto p = parser(test::Assemble(assembly));
1400   const bool succeeded = p->BuildAndParseInternalModule();
1401   if (succeeded) {
1402     EXPECT_TRUE(GetParam().expected_error.empty());
1403     const auto got = test::ToString(p->program());
1404     EXPECT_THAT(got, HasSubstr(GetParam().expected_decl));
1405   } else {
1406     EXPECT_FALSE(GetParam().expected_error.empty());
1407     EXPECT_THAT(p->error(), HasSubstr(GetParam().expected_error));
1408   }
1409 }
1410 
1411 INSTANTIATE_TEST_SUITE_P(
1412     Multisampled_Only2DNonArrayedIsValid,
1413     SpvParserHandleTest_ImageDeclTest,
1414     ::testing::ValuesIn(std::vector<ImageDeclCase>{
1415         {"%float 1D 0 0 1 1 Unknown", "%result = OpImageQuerySamples %uint %im",
1416          "WGSL multisampled textures must be 2d and non-arrayed: ", ""},
1417         {"%float 1D 0 1 1 1 Unknown", "%result = OpImageQuerySamples %uint %im",
1418          "WGSL arrayed textures must be 2d_array or cube_array: ", ""},
1419         {"%float 2D 0 0 1 1 Unknown", "%result = OpImageQuerySamples %uint %im",
1420          "",
1421          "[[group(2), binding(1)]] var x_20 : texture_multisampled_2d<f32>;"},
1422         {"%float 2D 0 1 1 1 Unknown", "%result = OpImageQuerySamples %uint %im",
1423          "WGSL multisampled textures must be 2d and non-arrayed: ", ""},
1424         {"%float 3D 0 0 1 1 Unknown", "%result = OpImageQuerySamples %uint %im",
1425          "WGSL multisampled textures must be 2d and non-arrayed: ", ""},
1426         {"%float 3D 0 1 1 1 Unknown", "%result = OpImageQuerySamples %uint %im",
1427          "WGSL arrayed textures must be 2d_array or cube_array: ", ""},
1428         {"%float Cube 0 0 1 1 Unknown",
1429          "%result = OpImageQuerySamples %uint %im",
1430          "WGSL multisampled textures must be 2d and non-arrayed: ", ""},
1431         {"%float Cube 0 1 1 1 Unknown",
1432          "%result = OpImageQuerySamples %uint %im",
1433          "WGSL multisampled textures must be 2d and non-arrayed: ", ""}}));
1434 
1435 // Test emission of variables when we have image accesses in executable code.
1436 
1437 struct ImageAccessCase {
1438   // SPIR-V image type, excluding result ID and opcode
1439   std::string spirv_image_type_details;
1440   std::string spirv_image_access;  // The provoking image access instruction.
1441   std::string var_decl;            // WGSL variable declaration
1442   std::string texture_builtin;     // WGSL texture usage.
1443 };
operator <<(std::ostream & out,const ImageAccessCase & c)1444 inline std::ostream& operator<<(std::ostream& out, const ImageAccessCase& c) {
1445   out << "ImageCase(" << c.spirv_image_type_details << "\n"
1446       << c.spirv_image_access << "\n"
1447       << c.var_decl << "\n"
1448       << c.texture_builtin << ")";
1449   return out;
1450 }
1451 
1452 using SpvParserHandleTest_SampledImageAccessTest =
1453     SpvParserTestBase<::testing::TestWithParam<ImageAccessCase>>;
1454 
TEST_P(SpvParserHandleTest_SampledImageAccessTest,Variable)1455 TEST_P(SpvParserHandleTest_SampledImageAccessTest, Variable) {
1456   // Only declare the sampled image type, and the associated variable
1457   // if the requested image type is a sampled image type, and not a
1458   // multisampled texture
1459   const bool is_sampled_image_type = GetParam().spirv_image_type_details.find(
1460                                          "0 1 Unknown") != std::string::npos;
1461   const auto assembly =
1462       Preamble() + R"(
1463      OpEntryPoint Fragment %main "main"
1464      OpExecutionMode %main OriginUpperLeft
1465      OpName %f1 "f1"
1466      OpName %vf12 "vf12"
1467      OpName %vf21 "vf21"
1468      OpName %vf123 "vf123"
1469      OpName %vf1234 "vf1234"
1470      OpName %u1 "u1"
1471      OpName %vu12 "vu12"
1472      OpName %vu123 "vu123"
1473      OpName %vu1234 "vu1234"
1474      OpName %i1 "i1"
1475      OpName %vi12 "vi12"
1476      OpName %vi123 "vi123"
1477      OpName %vi1234 "vi1234"
1478      OpName %coords1 "coords1"
1479      OpName %coords12 "coords12"
1480      OpName %coords123 "coords123"
1481      OpName %coords1234 "coords1234"
1482      OpName %offsets2d "offsets2d"
1483      OpName %u_offsets2d "u_offsets2d"
1484      OpDecorate %10 DescriptorSet 0
1485      OpDecorate %10 Binding 0
1486      OpDecorate %20 DescriptorSet 2
1487      OpDecorate %20 Binding 1
1488      OpDecorate %30 DescriptorSet 0
1489      OpDecorate %30 Binding 1
1490 )" + CommonBasicTypes() +
1491       R"(
1492      %sampler = OpTypeSampler
1493      %ptr_sampler = OpTypePointer UniformConstant %sampler
1494      %im_ty = OpTypeImage )" +
1495       GetParam().spirv_image_type_details + R"(
1496      %ptr_im_ty = OpTypePointer UniformConstant %im_ty
1497 )" + (is_sampled_image_type ? " %si_ty = OpTypeSampledImage %im_ty " : "") +
1498       R"(
1499 
1500      %10 = OpVariable %ptr_sampler UniformConstant
1501      %20 = OpVariable %ptr_im_ty UniformConstant
1502      %30 = OpVariable %ptr_sampler UniformConstant ; comparison sampler, when needed
1503 
1504      ; ConstOffset operands must be constants
1505      %offsets2d = OpConstantComposite %v2int %int_3 %int_4
1506      %u_offsets2d = OpConstantComposite %v2uint %uint_3 %uint_4
1507 
1508      %main = OpFunction %void None %voidfn
1509      %entry = OpLabel
1510 
1511      %f1 = OpCopyObject %float %float_1
1512      %vf12 = OpCopyObject %v2float %the_vf12
1513      %vf21 = OpCopyObject %v2float %the_vf21
1514      %vf123 = OpCopyObject %v3float %the_vf123
1515      %vf1234 = OpCopyObject %v4float %the_vf1234
1516 
1517      %i1 = OpCopyObject %int %int_1
1518      %vi12 = OpCopyObject %v2int %the_vi12
1519      %vi123 = OpCopyObject %v3int %the_vi123
1520      %vi1234 = OpCopyObject %v4int %the_vi1234
1521 
1522      %u1 = OpCopyObject %uint %uint_1
1523      %vu12 = OpCopyObject %v2uint %the_vu12
1524      %vu123 = OpCopyObject %v3uint %the_vu123
1525      %vu1234 = OpCopyObject %v4uint %the_vu1234
1526 
1527      %coords1 = OpCopyObject %float %float_1
1528      %coords12 = OpCopyObject %v2float %vf12
1529      %coords123 = OpCopyObject %v3float %vf123
1530      %coords1234 = OpCopyObject %v4float %vf1234
1531 
1532      %sam = OpLoad %sampler %10
1533      %im = OpLoad %im_ty %20
1534 )" +
1535       (is_sampled_image_type
1536            ? " %sampled_image = OpSampledImage %si_ty %im %sam\n"
1537            : "") +
1538       GetParam().spirv_image_access +
1539       R"(
1540 
1541      OpReturn
1542      OpFunctionEnd
1543   )";
1544   auto p = parser(test::Assemble(assembly));
1545   ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
1546   EXPECT_TRUE(p->error().empty()) << p->error();
1547   const auto program = test::ToString(p->program());
1548   EXPECT_THAT(program, HasSubstr(GetParam().var_decl))
1549       << "DECLARATIONS ARE BAD " << program;
1550   EXPECT_THAT(program, HasSubstr(GetParam().texture_builtin))
1551       << "TEXTURE BUILTIN IS BAD " << program << assembly;
1552 
1553   const bool is_query_size =
1554       GetParam().spirv_image_access.find("ImageQuerySize") != std::string::npos;
1555   const bool is_1d =
1556       GetParam().spirv_image_type_details.find("1D") != std::string::npos;
1557   if (is_query_size && is_1d) {
1558     p->SkipDumpingPending("crbug.com/tint/788");
1559   }
1560 }
1561 
1562 // TODO(dneto): Test variable declaration and texture builtins provoked by
1563 // use of an image access instruction inside helper function.
TEST_P(SpvParserHandleTest_RegisterHandleUsage_SampledImage,DISABLED_FunctionParam)1564 TEST_P(SpvParserHandleTest_RegisterHandleUsage_SampledImage,
1565        DISABLED_FunctionParam) {}
1566 
1567 INSTANTIATE_TEST_SUITE_P(
1568     DISABLED_ImageGather,
1569     SpvParserHandleTest_SampledImageAccessTest,
1570     ::testing::ValuesIn(std::vector<ImageAccessCase>{
1571         // TODO(dneto): OpImageGather
1572         // TODO(dneto): OpImageGather with ConstOffset (signed and unsigned)
1573         // TODO(dneto): OpImageGather with Offset (signed and unsigned)
1574         // TODO(dneto): OpImageGather with Offsets (signed and unsigned)
1575     }));
1576 
1577 INSTANTIATE_TEST_SUITE_P(
1578     DISABLED_ImageDrefGather,
1579     SpvParserHandleTest_SampledImageAccessTest,
1580     ::testing::ValuesIn(std::vector<ImageAccessCase>{
1581         // TODO(dneto): OpImageDrefGather
1582         // TODO(dneto): OpImageDrefGather with ConstOffset (signed and
1583         // unsigned)
1584         // TODO(dneto): OpImageDrefGather with Offset (signed and unsigned)
1585         // TODO(dneto): OpImageDrefGather with Offsets (signed and unsigned)
1586     }));
1587 
1588 INSTANTIATE_TEST_SUITE_P(
1589     ImageSampleImplicitLod,
1590     SpvParserHandleTest_SampledImageAccessTest,
1591     ::testing::Values(
1592 
1593         // OpImageSampleImplicitLod
1594         ImageAccessCase{"%float 2D 0 0 0 1 Unknown",
1595                         "%result = OpImageSampleImplicitLod "
1596                         "%v4float %sampled_image %coords12",
1597                         R"([[group(0), binding(0)]] var x_10 : sampler;
1598 
1599 [[group(2), binding(1)]] var x_20 : texture_2d<f32>;)",
1600                         "textureSample(x_20, x_10, coords12)"},
1601 
1602         // OpImageSampleImplicitLod arrayed
1603         ImageAccessCase{
1604             "%float 2D 0 1 0 1 Unknown",
1605             "%result = OpImageSampleImplicitLod "
1606             "%v4float %sampled_image %coords123",
1607             R"([[group(0), binding(0)]] var x_10 : sampler;
1608 
1609 [[group(2), binding(1)]] var x_20 : texture_2d_array<f32>;)",
1610             "textureSample(x_20, x_10, coords123.xy, i32(round(coords123.z)))"},
1611 
1612         // OpImageSampleImplicitLod with ConstOffset
1613         ImageAccessCase{
1614             "%float 2D 0 0 0 1 Unknown",
1615             "%result = OpImageSampleImplicitLod "
1616             "%v4float %sampled_image %coords12 ConstOffset %offsets2d",
1617             R"([[group(0), binding(0)]] var x_10 : sampler;
1618 
1619 [[group(2), binding(1)]] var x_20 : texture_2d<f32>;)",
1620             "textureSample(x_20, x_10, coords12, vec2<i32>(3, 4))"},
1621 
1622         // OpImageSampleImplicitLod arrayed with ConstOffset
1623         ImageAccessCase{
1624             "%float 2D 0 1 0 1 Unknown",
1625             "%result = OpImageSampleImplicitLod "
1626             "%v4float %sampled_image %coords123 ConstOffset %offsets2d",
1627             R"([[group(0), binding(0)]] var x_10 : sampler;
1628 
1629 [[group(2), binding(1)]] var x_20 : texture_2d_array<f32>;)",
1630             R"(textureSample(x_20, x_10, coords123.xy, i32(round(coords123.z)), vec2<i32>(3, 4)))"},
1631 
1632         // OpImageSampleImplicitLod with Bias
1633         ImageAccessCase{"%float 2D 0 0 0 1 Unknown",
1634                         "%result = OpImageSampleImplicitLod "
1635                         "%v4float %sampled_image %coords12 Bias %float_7",
1636                         R"([[group(0), binding(0)]] var x_10 : sampler;
1637 
1638 [[group(2), binding(1)]] var x_20 : texture_2d<f32>;)",
1639                         "textureSampleBias(x_20, x_10, coords12, 7.0)"},
1640 
1641         // OpImageSampleImplicitLod arrayed with Bias
1642         ImageAccessCase{
1643             "%float 2D 0 1 0 1 Unknown",
1644             "%result = OpImageSampleImplicitLod "
1645             "%v4float %sampled_image %coords123 Bias %float_7",
1646             R"([[group(0), binding(0)]] var x_10 : sampler;
1647 
1648 [[group(2), binding(1)]] var x_20 : texture_2d_array<f32>;)",
1649             R"(textureSampleBias(x_20, x_10, coords123.xy, i32(round(coords123.z)), 7.0))"},
1650 
1651         // OpImageSampleImplicitLod with Bias and signed ConstOffset
1652         ImageAccessCase{
1653             "%float 2D 0 0 0 1 Unknown",
1654             "%result = OpImageSampleImplicitLod "
1655             "%v4float %sampled_image %coords12 Bias|ConstOffset "
1656             "%float_7 %offsets2d",
1657             R"([[group(0), binding(0)]] var x_10 : sampler;
1658 
1659 [[group(2), binding(1)]] var x_20 : texture_2d<f32>;)",
1660             R"(textureSampleBias(x_20, x_10, coords12, 7.0, vec2<i32>(3, 4))"},
1661 
1662         // OpImageSampleImplicitLod with Bias and unsigned ConstOffset
1663         // Convert ConstOffset to signed
1664         ImageAccessCase{
1665             "%float 2D 0 0 0 1 Unknown",
1666             "%result = OpImageSampleImplicitLod "
1667             "%v4float %sampled_image %coords12 Bias|ConstOffset "
1668             "%float_7 %u_offsets2d",
1669             R"([[group(0), binding(0)]] var x_10 : sampler;
1670 
1671 [[group(2), binding(1)]] var x_20 : texture_2d<f32>;)",
1672             R"(textureSampleBias(x_20, x_10, coords12, 7.0, vec2<i32>(vec2<u32>(3u, 4u)))"},
1673         // OpImageSampleImplicitLod arrayed with Bias
1674         ImageAccessCase{
1675             "%float 2D 0 1 0 1 Unknown",
1676             "%result = OpImageSampleImplicitLod "
1677             "%v4float %sampled_image %coords123 Bias|ConstOffset "
1678             "%float_7 %offsets2d",
1679             R"([[group(0), binding(0)]] var x_10 : sampler;
1680 
1681 [[group(2), binding(1)]] var x_20 : texture_2d_array<f32>;)",
1682             R"(textureSampleBias(x_20, x_10, coords123.xy, i32(round(coords123.z)), 7.0, vec2<i32>(3, 4))"}));
1683 
1684 INSTANTIATE_TEST_SUITE_P(
1685     // This test shows the use of a sampled image used with both regular
1686     // sampling and depth-reference sampling.  The texture is a depth-texture,
1687     // and we use builtins textureSample and textureSampleCompare
1688     ImageSampleImplicitLod_BothDrefAndNonDref,
1689     SpvParserHandleTest_SampledImageAccessTest,
1690     ::testing::Values(
1691 
1692         // OpImageSampleImplicitLod
1693         ImageAccessCase{"%float 2D 0 0 0 1 Unknown", R"(
1694      %sam_dref = OpLoad %sampler %30
1695      %sampled_dref_image = OpSampledImage %si_ty %im %sam_dref
1696 
1697      %200 = OpImageSampleImplicitLod %v4float %sampled_image %coords12
1698      %210 = OpImageSampleDrefImplicitLod %float %sampled_dref_image %coords12 %depth
1699 )",
1700                         R"([[group(0), binding(0)]] var x_10 : sampler;
1701 
1702 [[group(2), binding(1)]] var x_20 : texture_depth_2d;
1703 
1704 [[group(0), binding(1)]] var x_30 : sampler_comparison;
1705 )",
1706                         R"(
1707   let x_200 : vec4<f32> = vec4<f32>(textureSample(x_20, x_10, coords12), 0.0, 0.0, 0.0);
1708   let x_210 : f32 = textureSampleCompare(x_20, x_30, coords12, 0.200000003);
1709 )"}));
1710 
1711 INSTANTIATE_TEST_SUITE_P(
1712     ImageSampleDrefImplicitLod,
1713     SpvParserHandleTest_SampledImageAccessTest,
1714     ::testing::Values(
1715         // ImageSampleDrefImplicitLod
1716         ImageAccessCase{
1717             "%float 2D 0 0 0 1 Unknown",
1718             "%result = OpImageSampleDrefImplicitLod "
1719             "%float %sampled_image %coords12 %depth",
1720             R"([[group(0), binding(0)]] var x_10 : sampler_comparison;
1721 
1722 [[group(2), binding(1)]] var x_20 : texture_depth_2d;
1723 )",
1724             R"(textureSampleCompare(x_20, x_10, coords12, 0.200000003))"},
1725         // ImageSampleDrefImplicitLod - arrayed
1726         ImageAccessCase{
1727             "%float 2D 0 1 0 1 Unknown",
1728             "%result = OpImageSampleDrefImplicitLod "
1729             "%float %sampled_image %coords123 %depth",
1730             R"([[group(0), binding(0)]] var x_10 : sampler_comparison;
1731 
1732 [[group(2), binding(1)]] var x_20 : texture_depth_2d_array;)",
1733             R"(textureSampleCompare(x_20, x_10, coords123.xy, i32(round(coords123.z)), 0.200000003))"},
1734         // ImageSampleDrefImplicitLod with ConstOffset
1735         ImageAccessCase{
1736             "%float 2D 0 0 0 1 Unknown",
1737             "%result = OpImageSampleDrefImplicitLod %float "
1738             "%sampled_image %coords12 %depth ConstOffset %offsets2d",
1739             R"([[group(0), binding(0)]] var x_10 : sampler_comparison;
1740 
1741 [[group(2), binding(1)]] var x_20 : texture_depth_2d;
1742 )",
1743             R"(textureSampleCompare(x_20, x_10, coords12, 0.200000003, vec2<i32>(3, 4)))"},
1744         // ImageSampleDrefImplicitLod arrayed with ConstOffset
1745         ImageAccessCase{
1746             "%float 2D 0 1 0 1 Unknown",
1747             "%result = OpImageSampleDrefImplicitLod %float "
1748             "%sampled_image %coords123 %depth ConstOffset %offsets2d",
1749             R"([[group(0), binding(0)]] var x_10 : sampler_comparison;
1750 
1751 [[group(2), binding(1)]] var x_20 : texture_depth_2d_array;)",
1752             R"(textureSampleCompare(x_20, x_10, coords123.xy, i32(round(coords123.z)), 0.200000003, vec2<i32>(3, 4)))"}));
1753 
1754 INSTANTIATE_TEST_SUITE_P(
1755     ImageSampleDrefExplicitLod,
1756     SpvParserHandleTest_SampledImageAccessTest,
1757     // Lod must be float constant 0 due to a Metal constraint.
1758     // Another test checks cases where the Lod is not float constant 0.
1759     ::testing::Values(
1760         // 2D
1761         ImageAccessCase{
1762             "%float 2D 1 0 0 1 Unknown",
1763             "%result = OpImageSampleDrefExplicitLod "
1764             "%float %sampled_image %coords12 %depth Lod %float_0",
1765             R"([[group(0), binding(0)]] var x_10 : sampler_comparison;
1766 
1767 [[group(2), binding(1)]] var x_20 : texture_depth_2d;
1768 )",
1769             R"(textureSampleCompareLevel(x_20, x_10, coords12, 0.200000003))"},
1770         // 2D array
1771         ImageAccessCase{
1772             "%float 2D 1 1 0 1 Unknown",
1773             "%result = OpImageSampleDrefExplicitLod "
1774             "%float %sampled_image %coords123 %depth Lod %float_0",
1775             R"([[group(0), binding(0)]] var x_10 : sampler_comparison;
1776 
1777 [[group(2), binding(1)]] var x_20 : texture_depth_2d_array;)",
1778             R"(textureSampleCompareLevel(x_20, x_10, coords123.xy, i32(round(coords123.z)), 0.200000003))"},
1779         // 2D, ConstOffset
1780         ImageAccessCase{
1781             "%float 2D 1 0 0 1 Unknown",
1782             "%result = OpImageSampleDrefExplicitLod %float "
1783             "%sampled_image %coords12 %depth Lod|ConstOffset "
1784             "%float_0 %offsets2d",
1785             R"([[group(0), binding(0)]] var x_10 : sampler_comparison;
1786 
1787 [[group(2), binding(1)]] var x_20 : texture_depth_2d;
1788 )",
1789             R"(textureSampleCompareLevel(x_20, x_10, coords12, 0.200000003, vec2<i32>(3, 4)))"},
1790         // 2D array, ConstOffset
1791         ImageAccessCase{
1792             "%float 2D 1 1 0 1 Unknown",
1793             "%result = OpImageSampleDrefExplicitLod %float "
1794             "%sampled_image %coords123 %depth Lod|ConstOffset "
1795             "%float_0 %offsets2d",
1796             R"([[group(0), binding(0)]] var x_10 : sampler_comparison;
1797 
1798 [[group(2), binding(1)]] var x_20 : texture_depth_2d_array;)",
1799             R"(textureSampleCompareLevel(x_20, x_10, coords123.xy, i32(round(coords123.z)), 0.200000003, vec2<i32>(3, 4)))"},
1800         // Cube
1801         ImageAccessCase{
1802             "%float Cube 1 0 0 1 Unknown",
1803             "%result = OpImageSampleDrefExplicitLod "
1804             "%float %sampled_image %coords123 %depth Lod %float_0",
1805             R"([[group(0), binding(0)]] var x_10 : sampler_comparison;
1806 
1807 [[group(2), binding(1)]] var x_20 : texture_depth_cube;)",
1808             R"(textureSampleCompareLevel(x_20, x_10, coords123, 0.200000003))"},
1809         // Cube array
1810         ImageAccessCase{
1811             "%float Cube 1 1 0 1 Unknown",
1812             "%result = OpImageSampleDrefExplicitLod "
1813             "%float %sampled_image %coords1234 %depth Lod %float_0",
1814             R"([[group(0), binding(0)]] var x_10 : sampler_comparison;
1815 
1816 [[group(2), binding(1)]] var x_20 : texture_depth_cube_array;)",
1817             R"(textureSampleCompareLevel(x_20, x_10, coords1234.xyz, i32(round(coords1234.w)), 0.200000003))"}));
1818 
1819 INSTANTIATE_TEST_SUITE_P(
1820     ImageSampleExplicitLod_UsingLod,
1821     SpvParserHandleTest_SampledImageAccessTest,
1822     ::testing::Values(
1823 
1824         // OpImageSampleExplicitLod - using Lod
1825         ImageAccessCase{"%float 2D 0 0 0 1 Unknown",
1826                         "%result = OpImageSampleExplicitLod "
1827                         "%v4float %sampled_image %coords12 Lod %float_null",
1828                         R"([[group(0), binding(0)]] var x_10 : sampler;
1829 
1830 [[group(2), binding(1)]] var x_20 : texture_2d<f32>;)",
1831                         R"(textureSampleLevel(x_20, x_10, coords12, 0.0))"},
1832 
1833         // OpImageSampleExplicitLod arrayed - using Lod
1834         ImageAccessCase{
1835             "%float 2D 0 1 0 1 Unknown",
1836             "%result = OpImageSampleExplicitLod "
1837             "%v4float %sampled_image %coords123 Lod %float_null",
1838             R"([[group(0), binding(0)]] var x_10 : sampler;
1839 
1840 [[group(2), binding(1)]] var x_20 : texture_2d_array<f32>;)",
1841             R"(textureSampleLevel(x_20, x_10, coords123.xy, i32(round(coords123.z)), 0.0))"},
1842 
1843         // OpImageSampleExplicitLod - using Lod and ConstOffset
1844         ImageAccessCase{
1845             "%float 2D 0 0 0 1 Unknown",
1846             "%result = OpImageSampleExplicitLod "
1847             "%v4float %sampled_image %coords12 Lod|ConstOffset "
1848             "%float_null %offsets2d",
1849             R"([[group(0), binding(0)]] var x_10 : sampler;
1850 
1851 [[group(2), binding(1)]] var x_20 : texture_2d<f32>;)",
1852             R"(textureSampleLevel(x_20, x_10, coords12, 0.0, vec2<i32>(3, 4)))"},
1853 
1854         // OpImageSampleExplicitLod - using Lod and unsigned ConstOffset
1855         // Convert the ConstOffset operand to signed
1856         ImageAccessCase{
1857             "%float 2D 0 0 0 1 Unknown",
1858             "%result = OpImageSampleExplicitLod "
1859             "%v4float %sampled_image %coords12 Lod|ConstOffset "
1860             "%float_null %u_offsets2d",
1861             R"([[group(0), binding(0)]] var x_10 : sampler;
1862 
1863 [[group(2), binding(1)]] var x_20 : texture_2d<f32>;)",
1864             R"(textureSampleLevel(x_20, x_10, coords12, 0.0, vec2<i32>(vec2<u32>(3u, 4u)))"},
1865 
1866         // OpImageSampleExplicitLod arrayed - using Lod and ConstOffset
1867         ImageAccessCase{
1868             "%float 2D 0 1 0 1 Unknown",
1869             "%result = OpImageSampleExplicitLod "
1870             "%v4float %sampled_image %coords123 Lod|ConstOffset "
1871             "%float_null %offsets2d",
1872             R"([[group(0), binding(0)]] var x_10 : sampler;
1873 
1874 [[group(2), binding(1)]] var x_20 : texture_2d_array<f32>;)",
1875             R"(textureSampleLevel(x_20, x_10, coords123.xy, i32(round(coords123.z)), 0.0, vec2<i32>(3, 4)))"}));
1876 
1877 INSTANTIATE_TEST_SUITE_P(
1878     ImageSampleExplicitLod_UsingGrad,
1879     SpvParserHandleTest_SampledImageAccessTest,
1880     ::testing::Values(
1881 
1882         // OpImageSampleExplicitLod - using Grad
1883         ImageAccessCase{
1884             "%float 2D 0 0 0 1 Unknown",
1885             "%result = OpImageSampleExplicitLod "
1886             "%v4float %sampled_image %coords12 Grad %vf12 %vf21",
1887             R"([[group(0), binding(0)]] var x_10 : sampler;
1888 
1889 [[group(2), binding(1)]] var x_20 : texture_2d<f32>;)",
1890             R"(textureSampleGrad(x_20, x_10, coords12, vf12, vf21))"},
1891 
1892         // OpImageSampleExplicitLod arrayed - using Grad
1893         ImageAccessCase{
1894             "%float 2D 0 1 0 1 Unknown",
1895             "%result = OpImageSampleExplicitLod "
1896             "%v4float %sampled_image %coords123 Grad %vf12 %vf21",
1897             R"([[group(0), binding(0)]] var x_10 : sampler;
1898 
1899 [[group(2), binding(1)]] var x_20 : texture_2d_array<f32>;)",
1900             R"(textureSampleGrad(x_20, x_10, coords123.xy, i32(round(coords123.z)), vf12, vf21))"},
1901 
1902         // OpImageSampleExplicitLod - using Grad and ConstOffset
1903         ImageAccessCase{
1904             "%float 2D 0 0 0 1 Unknown",
1905             "%result = OpImageSampleExplicitLod "
1906             "%v4float %sampled_image %coords12 Grad|ConstOffset "
1907             "%vf12 %vf21 %offsets2d",
1908             R"([[group(0), binding(0)]] var x_10 : sampler;
1909 
1910 [[group(2), binding(1)]] var x_20 : texture_2d<f32>;)",
1911             R"(textureSampleGrad(x_20, x_10, coords12, vf12, vf21, vec2<i32>(3, 4)))"},
1912 
1913         // OpImageSampleExplicitLod - using Grad and unsigned ConstOffset
1914         ImageAccessCase{
1915             "%float 2D 0 0 0 1 Unknown",
1916             "%result = OpImageSampleExplicitLod "
1917             "%v4float %sampled_image %coords12 Grad|ConstOffset "
1918             "%vf12 %vf21 %u_offsets2d",
1919             R"([[group(0), binding(0)]] var x_10 : sampler;
1920 
1921 [[group(2), binding(1)]] var x_20 : texture_2d<f32>;)",
1922             R"(textureSampleGrad(x_20, x_10, coords12, vf12, vf21, vec2<i32>(vec2<u32>(3u, 4u)))"},
1923 
1924         // OpImageSampleExplicitLod arrayed - using Grad and ConstOffset
1925         ImageAccessCase{
1926             "%float 2D 0 1 0 1 Unknown",
1927             "%result = OpImageSampleExplicitLod "
1928             "%v4float %sampled_image %coords123 Grad|ConstOffset "
1929             "%vf12 %vf21 %offsets2d",
1930             R"([[group(0), binding(0)]] var x_10 : sampler;
1931 
1932 [[group(2), binding(1)]] var x_20 : texture_2d_array<f32>;)",
1933             R"(textureSampleGrad(x_20, x_10, coords123.xy, i32(round(coords123.z)), vf12, vf21, vec2<i32>(3, 4)))"},
1934 
1935         // OpImageSampleExplicitLod arrayed - using Grad and unsigned
1936         // ConstOffset
1937         ImageAccessCase{
1938             "%float 2D 0 1 0 1 Unknown",
1939             "%result = OpImageSampleExplicitLod "
1940             "%v4float %sampled_image %coords123 Grad|ConstOffset "
1941             "%vf12 %vf21 %u_offsets2d",
1942             R"([[group(0), binding(0)]] var x_10 : sampler;
1943 
1944 [[group(2), binding(1)]] var x_20 : texture_2d_array<f32>;)",
1945             R"(textureSampleGrad(x_20, x_10, coords123.xy, i32(round(coords123.z)), vf12, vf21, vec2<i32>(vec2<u32>(3u, 4u))))"}));
1946 
1947 // Test crbug.com/378:
1948 // In WGSL, sampling from depth texture with explicit level of detail
1949 // requires the Lod parameter as an unsigned integer.
1950 // This corresponds to SPIR-V OpSampleExplicitLod and WGSL textureSampleLevel.
1951 INSTANTIATE_TEST_SUITE_P(
1952     ImageSampleExplicitLod_DepthTexture,
1953     SpvParserHandleTest_SampledImageAccessTest,
1954     ::testing::ValuesIn(std::vector<ImageAccessCase>{
1955         // Test a non-depth case.
1956         // (This is already tested above in the ImageSampleExplicitLod suite,
1957         // but I'm repeating here for the contrast with the depth case.)
1958         {"%float 2D 0 0 0 1 Unknown",
1959          "%result = OpImageSampleExplicitLod %v4float "
1960          "%sampled_image %vf12 Lod %f1",
1961          R"([[group(0), binding(0)]] var x_10 : sampler;
1962 
1963 [[group(2), binding(1)]] var x_20 : texture_2d<f32>;)",
1964          R"(textureSampleLevel(x_20, x_10, vf12, f1))"},
1965         // Test a depth case
1966         {"%float 2D 1 0 0 1 Unknown",
1967          "%result = OpImageSampleExplicitLod %v4float "
1968          "%sampled_image %vf12 Lod %f1",
1969          R"([[group(0), binding(0)]] var x_10 : sampler;
1970 
1971 [[group(2), binding(1)]] var x_20 : texture_depth_2d;
1972 )",
1973          R"(vec4<f32>(textureSampleLevel(x_20, x_10, vf12, i32(f1)), 0.0, 0.0, 0.0))"}}));
1974 
1975 /////
1976 // Projection sampling
1977 /////
1978 
1979 // Test matrix for projection sampling:
1980 // sampling
1981 //   Dimensions: 1D, 2D, 3D, 2DShadow
1982 //   Variations: Proj { ImplicitLod { | Bias } | ExplicitLod { Lod | Grad | } }
1983 //   x { | ConstOffset }
1984 // depth-compare sampling
1985 //   Dimensions: 2D
1986 //   Variations: Proj Dref { ImplicitLod { | Bias } | ExplicitLod { Lod | Grad |
1987 //   } } x { | ConstOffset }
1988 //
1989 // Expanded:
1990 //    ImageSampleProjImplicitLod        // { | ConstOffset }
1991 //    ImageSampleProjImplicitLod_Bias   // { | ConstOffset }
1992 //    ImageSampleProjExplicitLod_Lod    // { | ConstOffset }
1993 //    ImageSampleProjExplicitLod_Grad   // { | ConstOffset }
1994 //
1995 //    ImageSampleProjImplicitLod_DepthTexture
1996 //
1997 //    ImageSampleProjDrefImplicitLod        // { | ConstOffset }
1998 //    ImageSampleProjDrefExplicitLod_Lod    // { | ConstOffset }
1999 
2000 INSTANTIATE_TEST_SUITE_P(
2001     ImageSampleProjImplicitLod,
2002     SpvParserHandleTest_SampledImageAccessTest,
2003     ::testing::Values(
2004 
2005         // OpImageSampleProjImplicitLod 1D
2006         ImageAccessCase{
2007             "%float 1D 0 0 0 1 Unknown",
2008             "%result = OpImageSampleProjImplicitLod "
2009             "%v4float %sampled_image %coords12",
2010             R"([[group(0), binding(0)]] var x_10 : sampler;
2011 
2012 [[group(2), binding(1)]] var x_20 : texture_1d<f32>;)",
2013             R"(textureSample(x_20, x_10, (coords12.x / coords12.y)))"},
2014 
2015         // OpImageSampleProjImplicitLod 2D
2016         ImageAccessCase{
2017             "%float 2D 0 0 0 1 Unknown",
2018             "%result = OpImageSampleProjImplicitLod "
2019             "%v4float %sampled_image %coords123",
2020             R"([[group(0), binding(0)]] var x_10 : sampler;
2021 
2022 [[group(2), binding(1)]] var x_20 : texture_2d<f32>;)",
2023             R"(textureSample(x_20, x_10, (coords123.xy / coords123.z)))"},
2024 
2025         // OpImageSampleProjImplicitLod 3D
2026         ImageAccessCase{
2027             "%float 3D 0 0 0 1 Unknown",
2028             "%result = OpImageSampleProjImplicitLod "
2029             "%v4float %sampled_image %coords1234",
2030             R"([[group(0), binding(0)]] var x_10 : sampler;
2031 
2032 [[group(2), binding(1)]] var x_20 : texture_3d<f32>;)",
2033             R"(textureSample(x_20, x_10, (coords1234.xyz / coords1234.w)))"},
2034 
2035         // OpImageSampleProjImplicitLod 2D with ConstOffset
2036         // (Don't need to test with 1D or 3D, as the hard part was the splatted
2037         // division.) This case tests handling of the ConstOffset
2038         ImageAccessCase{
2039             "%float 2D 0 0 0 1 Unknown",
2040             "%result = OpImageSampleProjImplicitLod "
2041             "%v4float %sampled_image %coords123 ConstOffset %offsets2d",
2042             R"([[group(0), binding(0)]] var x_10 : sampler;
2043 
2044 [[group(2), binding(1)]] var x_20 : texture_2d<f32>;)",
2045             R"(textureSample(x_20, x_10, (coords123.xy / coords123.z), vec2<i32>(3, 4)))"}));
2046 
2047 INSTANTIATE_TEST_SUITE_P(
2048     ImageSampleProjImplicitLod_Bias,
2049     SpvParserHandleTest_SampledImageAccessTest,
2050     ::testing::Values(
2051 
2052         // OpImageSampleProjImplicitLod with Bias
2053         // Only testing 2D
2054         ImageAccessCase{
2055             "%float 2D 0 0 0 1 Unknown",
2056             "%result = OpImageSampleProjImplicitLod "
2057             "%v4float %sampled_image %coords123 Bias %float_7",
2058             R"([[group(0), binding(0)]] var x_10 : sampler;
2059 
2060 [[group(2), binding(1)]] var x_20 : texture_2d<f32>;)",
2061             R"(textureSampleBias(x_20, x_10, (coords123.xy / coords123.z), 7.0))"},
2062 
2063         // OpImageSampleProjImplicitLod with Bias and signed ConstOffset
2064         ImageAccessCase{
2065             "%float 2D 0 0 0 1 Unknown",
2066             "%result = OpImageSampleProjImplicitLod "
2067             "%v4float %sampled_image %coords123 Bias|ConstOffset "
2068             "%float_7 %offsets2d",
2069             R"([[group(0), binding(0)]] var x_10 : sampler;
2070 
2071 [[group(2), binding(1)]] var x_20 : texture_2d<f32>;)",
2072             R"(textureSampleBias(x_20, x_10, (coords123.xy / coords123.z), 7.0, vec2<i32>(3, 4)))"},
2073 
2074         // OpImageSampleProjImplicitLod with Bias and unsigned ConstOffset
2075         // Convert ConstOffset to signed
2076         ImageAccessCase{
2077             "%float 2D 0 0 0 1 Unknown",
2078             "%result = OpImageSampleProjImplicitLod "
2079             "%v4float %sampled_image %coords123 Bias|ConstOffset "
2080             "%float_7 %u_offsets2d",
2081             R"([[group(0), binding(0)]] var x_10 : sampler;
2082 
2083 [[group(2), binding(1)]] var x_20 : texture_2d<f32>;)",
2084             R"(textureSampleBias(x_20, x_10, (coords123.xy / coords123.z), 7.0, vec2<i32>(vec2<u32>(3u, 4u))))"}));
2085 
2086 INSTANTIATE_TEST_SUITE_P(
2087     ImageSampleProjExplicitLod_Lod,
2088     SpvParserHandleTest_SampledImageAccessTest,
2089     ::testing::Values(
2090         // OpImageSampleProjExplicitLod 2D
2091         ImageAccessCase{
2092             "%float 2D 0 0 0 1 Unknown",
2093             "%result = OpImageSampleProjExplicitLod "
2094             "%v4float %sampled_image %coords123 Lod %f1",
2095             R"([[group(0), binding(0)]] var x_10 : sampler;
2096 
2097 [[group(2), binding(1)]] var x_20 : texture_2d<f32>;)",
2098             R"(textureSampleLevel(x_20, x_10, (coords123.xy / coords123.z), f1))"},
2099 
2100         // OpImageSampleProjExplicitLod 2D Lod with ConstOffset
2101         ImageAccessCase{
2102             "%float 2D 0 0 0 1 Unknown",
2103             "%result = OpImageSampleProjExplicitLod "
2104             "%v4float %sampled_image %coords123 Lod|ConstOffset %f1 %offsets2d",
2105             R"([[group(0), binding(0)]] var x_10 : sampler;
2106 
2107 [[group(2), binding(1)]] var x_20 : texture_2d<f32>;)",
2108             R"(textureSampleLevel(x_20, x_10, (coords123.xy / coords123.z), f1, vec2<i32>(3, 4)))"}));
2109 
2110 INSTANTIATE_TEST_SUITE_P(
2111     ImageSampleProjExplicitLod_Grad,
2112     SpvParserHandleTest_SampledImageAccessTest,
2113     ::testing::Values(
2114         // OpImageSampleProjExplicitLod 2D Grad
2115         ImageAccessCase{
2116             "%float 2D 0 0 0 1 Unknown",
2117             "%result = OpImageSampleProjExplicitLod "
2118             "%v4float %sampled_image %coords123 Grad %vf12 %vf21",
2119             R"([[group(0), binding(0)]] var x_10 : sampler;
2120 
2121 [[group(2), binding(1)]] var x_20 : texture_2d<f32>;)",
2122             R"(textureSampleGrad(x_20, x_10, (coords123.xy / coords123.z), vf12, vf21))"},
2123 
2124         // OpImageSampleProjExplicitLod 2D Lod Grad ConstOffset
2125         ImageAccessCase{
2126             "%float 2D 0 0 0 1 Unknown",
2127             "%result = OpImageSampleProjExplicitLod "
2128             "%v4float %sampled_image %coords123 Grad|ConstOffset "
2129             "%vf12 %vf21 %offsets2d",
2130             R"([[group(0), binding(0)]] var x_10 : sampler;
2131 
2132 [[group(2), binding(1)]] var x_20 : texture_2d<f32>;)",
2133             R"(textureSampleGrad(x_20, x_10, (coords123.xy / coords123.z), vf12, vf21, vec2<i32>(3, 4)))"}));
2134 
2135 INSTANTIATE_TEST_SUITE_P(
2136     // Ordinary (non-comparison) sampling on a depth texture.
2137     ImageSampleProjImplicitLod_DepthTexture,
2138     SpvParserHandleTest_SampledImageAccessTest,
2139     ::testing::Values(
2140         // OpImageSampleProjImplicitLod 2D depth
2141         ImageAccessCase{
2142             "%float 2D 1 0 0 1 Unknown",
2143             "%result = OpImageSampleProjImplicitLod "
2144             "%v4float %sampled_image %coords123",
2145             R"([[group(0), binding(0)]] var x_10 : sampler;
2146 
2147 [[group(2), binding(1)]] var x_20 : texture_depth_2d;
2148 )",
2149             // Sampling the depth texture yields an f32, but the
2150             // SPIR-V operation yiedls vec4<f32>, so fill out the
2151             // remaining components with 0.
2152             R"(vec4<f32>(textureSample(x_20, x_10, (coords123.xy / coords123.z)), 0.0, 0.0, 0.0))"}));
2153 
2154 INSTANTIATE_TEST_SUITE_P(
2155     ImageSampleProjDrefImplicitLod,
2156     SpvParserHandleTest_SampledImageAccessTest,
2157     ::testing::Values(
2158 
2159         // OpImageSampleProjDrefImplicitLod 2D depth-texture
2160         ImageAccessCase{
2161             "%float 2D 1 0 0 1 Unknown",
2162             "%result = OpImageSampleProjDrefImplicitLod "
2163             "%float %sampled_image %coords123 %f1",
2164             R"([[group(0), binding(0)]] var x_10 : sampler_comparison;
2165 
2166 [[group(2), binding(1)]] var x_20 : texture_depth_2d;
2167 )",
2168             R"(textureSampleCompare(x_20, x_10, (coords123.xy / coords123.z), f1))"},
2169 
2170         // OpImageSampleProjDrefImplicitLod 2D depth-texture, ConstOffset
2171         ImageAccessCase{
2172             "%float 2D 1 0 0 1 Unknown",
2173             "%result = OpImageSampleProjDrefImplicitLod "
2174             "%float %sampled_image %coords123 %f1 ConstOffset %offsets2d",
2175             R"([[group(0), binding(0)]] var x_10 : sampler_comparison;
2176 
2177 [[group(2), binding(1)]] var x_20 : texture_depth_2d;
2178 )",
2179             R"(textureSampleCompare(x_20, x_10, (coords123.xy / coords123.z), f1, vec2<i32>(3, 4)))"}));
2180 
2181 INSTANTIATE_TEST_SUITE_P(
2182     DISABLED_ImageSampleProjDrefExplicitLod_Lod,
2183     SpvParserHandleTest_SampledImageAccessTest,
2184     ::testing::Values(
2185 
2186         // Lod must be float constant 0 due to a Metal constraint.
2187         // Another test checks cases where the Lod is not float constant 0.
2188 
2189         // OpImageSampleProjDrefExplicitLod 2D depth-texture Lod
2190         ImageAccessCase{
2191             "%float 2D 1 0 0 1 Unknown",
2192             "%result = OpImageSampleProjDrefExplicitLod "
2193             "%float %sampled_image %coords123 %depth Lod %float_0",
2194             R"([[group(0), binding(0)]] var x_10 : sampler_comparison;
2195 
2196 [[group(2), binding(1)]] var x_20 : texture_depth_2d;
2197 )",
2198             R"(textureSampleCompare(x_20, x_10, (coords123.xy / coords123.z), 0.200000003, 0.0))"},
2199 
2200         // OpImageSampleProjDrefImplicitLod 2D depth-texture, Lod ConstOffset
2201         ImageAccessCase{
2202             "%float 2D 1 0 0 1 Unknown",
2203             "%result = OpImageSampleProjDrefExplicitLod "
2204             "%float %sampled_image %coords123 %depth "
2205             "Lod|ConstOffset %float_0 %offsets2d",
2206             R"([[group(0), binding(0)]] var x_10 : sampler_comparison;
2207 
2208 [[group(2), binding(1)]] var x_20 : texture_depth_2d;
2209 )",
2210             R"(textureSampleCompareLevel(x_20, x_10, (coords123.xy / coords123.z), 0.200000003, 0.0, vec2<i32>(3, 4)))"}));
2211 
2212 /////
2213 // End projection sampling
2214 /////
2215 
2216 using SpvParserHandleTest_ImageAccessTest =
2217     SpvParserTestBase<::testing::TestWithParam<ImageAccessCase>>;
2218 
TEST_P(SpvParserHandleTest_ImageAccessTest,Variable)2219 TEST_P(SpvParserHandleTest_ImageAccessTest, Variable) {
2220   // In this test harness, we only create an image.
2221   const auto assembly = Preamble() + R"(
2222      OpEntryPoint Fragment %main "main"
2223      OpExecutionMode %main OriginUpperLeft
2224      OpName %f1 "f1"
2225      OpName %vf12 "vf12"
2226      OpName %vf123 "vf123"
2227      OpName %vf1234 "vf1234"
2228      OpName %u1 "u1"
2229      OpName %vu12 "vu12"
2230      OpName %vu123 "vu123"
2231      OpName %vu1234 "vu1234"
2232      OpName %i1 "i1"
2233      OpName %vi12 "vi12"
2234      OpName %vi123 "vi123"
2235      OpName %vi1234 "vi1234"
2236      OpName %offsets2d "offsets2d"
2237      OpDecorate %20 DescriptorSet 2
2238      OpDecorate %20 Binding 1
2239 )" + CommonBasicTypes() +
2240                         R"(
2241      %im_ty = OpTypeImage )" +
2242                         GetParam().spirv_image_type_details + R"(
2243      %ptr_im_ty = OpTypePointer UniformConstant %im_ty
2244      %20 = OpVariable %ptr_im_ty UniformConstant
2245 
2246      %main = OpFunction %void None %voidfn
2247      %entry = OpLabel
2248 
2249      %f1 = OpCopyObject %float %float_1
2250      %vf12 = OpCopyObject %v2float %the_vf12
2251      %vf123 = OpCopyObject %v3float %the_vf123
2252      %vf1234 = OpCopyObject %v4float %the_vf1234
2253 
2254      %i1 = OpCopyObject %int %int_1
2255      %vi12 = OpCopyObject %v2int %the_vi12
2256      %vi123 = OpCopyObject %v3int %the_vi123
2257      %vi1234 = OpCopyObject %v4int %the_vi1234
2258 
2259      %u1 = OpCopyObject %uint %uint_1
2260      %vu12 = OpCopyObject %v2uint %the_vu12
2261      %vu123 = OpCopyObject %v3uint %the_vu123
2262      %vu1234 = OpCopyObject %v4uint %the_vu1234
2263 
2264      %value_offset = OpCompositeConstruct %v2int %int_3 %int_4
2265      %offsets2d = OpCopyObject %v2int %value_offset
2266      %im = OpLoad %im_ty %20
2267 
2268 )" + GetParam().spirv_image_access +
2269                         R"(
2270      OpReturn
2271      OpFunctionEnd
2272   )";
2273   auto p = parser(test::Assemble(assembly));
2274   ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
2275   EXPECT_TRUE(p->error().empty()) << p->error();
2276   const auto program = test::ToString(p->program());
2277   EXPECT_THAT(program, HasSubstr(GetParam().var_decl))
2278       << "DECLARATIONS ARE BAD " << program;
2279   EXPECT_THAT(program, HasSubstr(GetParam().texture_builtin))
2280       << "TEXTURE BUILTIN IS BAD " << program << assembly;
2281 }
2282 
2283 INSTANTIATE_TEST_SUITE_P(ImageWrite_OptionalParams,
2284                          SpvParserHandleTest_ImageAccessTest,
2285                          ::testing::ValuesIn(std::vector<ImageAccessCase>{
2286                              // OpImageWrite with no extra params
2287                              {"%float 2D 0 0 0 2 Rgba32f",
2288                               "OpImageWrite %im %vi12 %vf1234",
2289                               "[[group(2), binding(1)]] var x_20 : "
2290                               "texture_storage_2d<rgba32float, write>;",
2291                               "textureStore(x_20, vi12, vf1234);"}}));
2292 
2293 INSTANTIATE_TEST_SUITE_P(
2294     // SPIR-V's texel parameter is a scalar or vector with at least as many
2295     // components as there are channels in the underlying format, and the
2296     // componet type matches the sampled type (modulo signed/unsigned integer).
2297     // WGSL's texel parameter is a 4-element vector scalar or vector, with
2298     // component type equal to the 32-bit form of the channel type.
2299     ImageWrite_ConvertTexelOperand_Arity,
2300     SpvParserHandleTest_ImageAccessTest,
2301     ::testing::ValuesIn(std::vector<ImageAccessCase>{
2302         // Source 1 component
2303         {"%float 2D 0 0 0 2 R32f", "OpImageWrite %im %vi12 %f1",
2304          R"([[group(2), binding(1)]] var x_20 : texture_storage_2d<r32float, write>;)",
2305          "textureStore(x_20, vi12, vec4<f32>(f1, 0.0, 0.0, 0.0));"},
2306         // Source 2 component, dest 1 component
2307         {"%float 2D 0 0 0 2 R32f", "OpImageWrite %im %vi12 %vf12",
2308          R"([[group(2), binding(1)]] var x_20 : texture_storage_2d<r32float, write>;)",
2309          "textureStore(x_20, vi12, vec4<f32>(vf12, 0.0, 0.0));"},
2310         // Source 3 component, dest 1 component
2311         {"%float 2D 0 0 0 2 R32f", "OpImageWrite %im %vi12 %vf123",
2312          R"([[group(2), binding(1)]] var x_20 : texture_storage_2d<r32float, write>;)",
2313          "textureStore(x_20, vi12, vec4<f32>(vf123, 0.0));"},
2314         // Source 4 component, dest 1 component
2315         {"%float 2D 0 0 0 2 R32f", "OpImageWrite %im %vi12 %vf1234",
2316          R"([[group(2), binding(1)]] var x_20 : texture_storage_2d<r32float, write>;)",
2317          "textureStore(x_20, vi12, vf1234);"},
2318         // Source 2 component, dest 2 component
2319         {"%float 2D 0 0 0 2 Rg32f", "OpImageWrite %im %vi12 %vf12",
2320          R"([[group(2), binding(1)]] var x_20 : texture_storage_2d<rg32float, write>;)",
2321          "textureStore(x_20, vi12, vec4<f32>(vf12, 0.0, 0.0));"},
2322         // Source 3 component, dest 2 component
2323         {"%float 2D 0 0 0 2 Rg32f", "OpImageWrite %im %vi12 %vf123",
2324          R"([[group(2), binding(1)]] var x_20 : texture_storage_2d<rg32float, write>;)",
2325          "textureStore(x_20, vi12, vec4<f32>(vf123, 0.0));"},
2326         // Source 4 component, dest 2 component
2327         {"%float 2D 0 0 0 2 Rg32f", "OpImageWrite %im %vi12 %vf1234",
2328          R"([[group(2), binding(1)]] var x_20 : texture_storage_2d<rg32float, write>;)",
2329          "textureStore(x_20, vi12, vf1234);"},
2330         // WGSL does not support 3-component storage textures.
2331         // Source 4 component, dest 4 component
2332         {"%float 2D 0 0 0 2 Rgba32f", "OpImageWrite %im %vi12 %vf1234",
2333          "[[group(2), binding(1)]] var x_20 : "
2334          "texture_storage_2d<rgba32float, write>;",
2335          "textureStore(x_20, vi12, vf1234);"}}));
2336 
TEST_F(SpvParserHandleTest,ImageWrite_TooFewSrcTexelComponents_1_vs_4)2337 TEST_F(SpvParserHandleTest, ImageWrite_TooFewSrcTexelComponents_1_vs_4) {
2338   const auto assembly = Preamble() + R"(
2339      OpEntryPoint Fragment %main "main"
2340      OpExecutionMode %main OriginUpperLeft
2341      OpName %f1 "f1"
2342      OpName %coords12 "coords12"
2343      OpDecorate %20 DescriptorSet 2
2344      OpDecorate %20 Binding 1
2345 )" + CommonBasicTypes() +
2346                         R"(
2347      %im_ty = OpTypeImage %void 2D 0 0 0 2 Rgba32f
2348      %ptr_im_ty = OpTypePointer UniformConstant %im_ty
2349 
2350      %20 = OpVariable %ptr_im_ty UniformConstant
2351 
2352      %main = OpFunction %void None %voidfn
2353      %entry = OpLabel
2354 
2355      %f1 = OpCopyObject %float %float_1
2356 
2357      %coords12 = OpCopyObject %v2float %the_vf12
2358 
2359      %im = OpLoad %im_ty %20
2360      OpImageWrite %im %coords12 %f1
2361      OpReturn
2362      OpFunctionEnd
2363   )";
2364   auto p = parser(test::Assemble(assembly));
2365   EXPECT_FALSE(p->BuildAndParseInternalModule());
2366   EXPECT_THAT(p->error(),
2367               Eq("texel has too few components for storage texture: 1 provided "
2368                  "but 4 required, in: OpImageWrite %54 %3 %2"))
2369       << p->error();
2370 }
2371 
TEST_F(SpvParserHandleTest,ImageWrite_ThreeComponentStorageTexture_IsError)2372 TEST_F(SpvParserHandleTest, ImageWrite_ThreeComponentStorageTexture_IsError) {
2373   // SPIR-V doesn't allow a 3-element storage texture format.
2374   const auto assembly = Preamble() + R"(
2375      OpEntryPoint Fragment %main "main"
2376      OpExecutionMode %main OriginUpperLeft
2377      OpName %vf123 "vf123"
2378      OpName %coords12 "coords12"
2379      OpDecorate %20 DescriptorSet 2
2380      OpDecorate %20 Binding 1
2381 )" + CommonBasicTypes() +
2382                         R"(
2383      %im_ty = OpTypeImage %void 2D 0 0 0 2 Rgb32f
2384      %ptr_im_ty = OpTypePointer UniformConstant %im_ty
2385 
2386      %20 = OpVariable %ptr_im_ty UniformConstant
2387 
2388      %main = OpFunction %void None %voidfn
2389      %entry = OpLabel
2390 
2391      %vf123 = OpCopyObject %v3float %the_vf123
2392 
2393      %coords12 = OpCopyObject %v2float %the_vf12
2394 
2395      %im = OpLoad %im_ty %20
2396      OpImageWrite %im %coords12 %vf123
2397      OpReturn
2398      OpFunctionEnd
2399   )";
2400   auto error = test::AssembleFailure(assembly);
2401   EXPECT_THAT(error, HasSubstr("Invalid image format 'Rgb32f'"));
2402 }
2403 
2404 INSTANTIATE_TEST_SUITE_P(
2405     // The texel operand signedness must match the channel type signedness.
2406     // SPIR-V validation checks that.
2407     // This suite is for the cases where they are integral and the same
2408     // signedness.
2409     ImageWrite_ConvertTexelOperand_SameSignedness,
2410     SpvParserHandleTest_ImageAccessTest,
2411     ::testing::ValuesIn(std::vector<ImageAccessCase>{
2412         // Sampled type is unsigned int, texel is unsigned int
2413         {"%uint 2D 0 0 0 2 Rgba32ui", "OpImageWrite %im %vi12 %vu1234",
2414          R"([[group(2), binding(1)]] var x_20 : texture_storage_2d<rgba32uint, write>;)",
2415          R"(textureStore(x_20, vi12, vu1234))"},
2416         // Sampled type is signed int, texel is signed int
2417         {"%int 2D 0 0 0 2 Rgba32i", "OpImageWrite %im %vi12 %vi1234",
2418          R"([[group(2), binding(1)]] var x_20 : texture_storage_2d<rgba32sint, write>;)",
2419          R"(textureStore(x_20, vi12, vi1234))"}}));
2420 
2421 INSTANTIATE_TEST_SUITE_P(
2422     // Error out when OpImageWrite write texel differ in float vs. integral
2423     ImageWrite_ConvertTexelOperand_DifferentFloatishness_IsError,
2424     // Use the ImageDeclTest so we can check the error.
2425     SpvParserHandleTest_ImageDeclTest,
2426     ::testing::ValuesIn(std::vector<ImageDeclCase>{
2427         // Sampled type is float, texel is signed int
2428         {"%uint 2D 0 0 0 2 Rgba32f", "OpImageWrite %im %vi12 %vi1234",
2429          "invalid texel type for storage texture write: component must be "
2430          "float, signed integer, or unsigned integer to match the texture "
2431          "channel type: OpImageWrite",
2432          R"([[group(2), binding(1)]] var x_20 : texture_storage_2d<rgba32float, write>;)"},
2433         // Sampled type is float, texel is unsigned int
2434         {"%int 2D 0 0 0 2 Rgba32f", "OpImageWrite %im %vi12 %vu1234",
2435          "invalid texel type for storage texture write: component must be "
2436          "float, signed integer, or unsigned integer to match the texture "
2437          "channel type: OpImageWrite",
2438          R"([[group(2), binding(1)]] var x_20 : texture_storage_2d<rgba32float, write>;)"},
2439         // Sampled type is unsigned int, texel is float
2440         {"%uint 2D 0 0 0 2 Rgba32ui", "OpImageWrite %im %vi12 %vf1234",
2441          "invalid texel type for storage texture write: component must be "
2442          "float, signed integer, or unsigned integer to match the texture "
2443          "channel type: OpImageWrite",
2444          R"([[group(2), binding(1)]] var x_20 : texture_storage_2d<rgba32uint, write>;)"},
2445         // Sampled type is signed int, texel is float
2446         {"%int 2D 0 0 0 2 Rgba32i", "OpImageWrite %im %vi12 %vf1234",
2447          "invalid texel type for storage texture write: component must be "
2448          "float, signed integer, or unsigned integer to match the texture "
2449          "channel type: OpImageWrite",
2450          R"([[group(2), binding(1)]] var x_20 : texture_storage_2d<rgba32sint, write>;
2451   })"}}));
2452 
2453 INSTANTIATE_TEST_SUITE_P(
2454     // Error out when OpImageWrite write texel signedness is different.
2455     ImageWrite_ConvertTexelOperand_DifferentSignedness_IsError,
2456     // Use the ImageDeclTest so we can check the error.
2457     SpvParserHandleTest_ImageDeclTest,
2458     ::testing::ValuesIn(std::vector<ImageDeclCase>{
2459         // Sampled type is unsigned int, texel is signed int
2460         {"%uint 2D 0 0 0 2 Rgba32ui", "OpImageWrite %im %vi12 %vi1234",
2461          "invalid texel type for storage texture write: component must be "
2462          "float, signed integer, or unsigned integer to match the texture "
2463          "channel type: OpImageWrite",
2464          R"([[group(2), binding(1)]] var x_20 : texture_storage_2d<rgba32uint, write>;)"},
2465         // Sampled type is signed int, texel is unsigned int
2466         {"%int 2D 0 0 0 2 Rgba32i", "OpImageWrite %im %vi12 %vu1234",
2467          "invalid texel type for storage texture write: component must be "
2468          "float, signed integer, or unsigned integer to match the texture "
2469          "channel type: OpImageWrite",
2470          R"([[group(2), binding(1)]] var x_20 : texture_storage_2d<rgba32sint, write>;
2471   })"}}));
2472 
2473 INSTANTIATE_TEST_SUITE_P(
2474     // Show that zeros of the correct integer signedness are
2475     // created when expanding an integer vector.
2476     ImageWrite_ConvertTexelOperand_Signedness_AndWidening,
2477     SpvParserHandleTest_ImageAccessTest,
2478     ::testing::ValuesIn(std::vector<ImageAccessCase>{
2479         // Source unsigned, dest unsigned
2480         {"%uint 2D 0 0 0 2 R32ui", "OpImageWrite %im %vi12 %vu12",
2481          R"([[group(2), binding(1)]] var x_20 : texture_storage_2d<r32uint, write>;)",
2482          R"(textureStore(x_20, vi12, vec4<u32>(vu12, 0u, 0u)))"},
2483         // Source signed, dest signed
2484         {"%int 2D 0 0 0 2 R32i", "OpImageWrite %im %vi12 %vi12",
2485          R"([[group(2), binding(1)]] var x_20 : texture_storage_2d<r32sint, write>;)",
2486          R"(textureStore(x_20, vi12, vec4<i32>(vi12, 0, 0)))"}}));
2487 
2488 INSTANTIATE_TEST_SUITE_P(
2489     ImageFetch_OptionalParams,
2490     SpvParserHandleTest_ImageAccessTest,
2491     ::testing::ValuesIn(std::vector<ImageAccessCase>{
2492         // OpImageFetch with no extra params, on sampled texture
2493         // Level of detail is injected for sampled texture
2494         {"%float 2D 0 0 0 1 Unknown", "%99 = OpImageFetch %v4float %im %vi12",
2495          R"([[group(2), binding(1)]] var x_20 : texture_2d<f32>;)",
2496          R"(let x_99 : vec4<f32> = textureLoad(x_20, vi12, 0);)"},
2497         // OpImageFetch with explicit level, on sampled texture
2498         {"%float 2D 0 0 0 1 Unknown",
2499          "%99 = OpImageFetch %v4float %im %vi12 Lod %int_3",
2500          R"([[group(2), binding(1)]] var x_20 : texture_2d<f32>;)",
2501          R"(let x_99 : vec4<f32> = textureLoad(x_20, vi12, 3);)"},
2502         // OpImageFetch with no extra params, on depth texture
2503         // Level of detail is injected for depth texture
2504         {"%float 2D 1 0 0 1 Unknown", "%99 = OpImageFetch %v4float %im %vi12",
2505          R"([[group(2), binding(1)]] var x_20 : texture_depth_2d;)",
2506          R"(let x_99 : vec4<f32> = vec4<f32>(textureLoad(x_20, vi12, 0), 0.0, 0.0, 0.0);)"},
2507         // OpImageFetch with extra params, on depth texture
2508         {"%float 2D 1 0 0 1 Unknown",
2509          "%99 = OpImageFetch %v4float %im %vi12 Lod %int_3",
2510          R"([[group(2), binding(1)]] var x_20 : texture_depth_2d;)",
2511          R"(let x_99 : vec4<f32> = vec4<f32>(textureLoad(x_20, vi12, 3), 0.0, 0.0, 0.0);)"}}));
2512 
2513 INSTANTIATE_TEST_SUITE_P(
2514     ImageFetch_Depth,
2515     // In SPIR-V OpImageFetch always yields a vector of 4
2516     // elements, even for depth images.  But in WGSL,
2517     // textureLoad on a depth image yields f32.
2518     // crbug.com/tint/439
2519     SpvParserHandleTest_ImageAccessTest,
2520     ::testing::ValuesIn(std::vector<ImageAccessCase>{
2521         // ImageFetch on depth image.
2522         {"%float 2D 1 0 0 1 Unknown", "%99 = OpImageFetch %v4float %im %vi12 ",
2523          R"([[group(2), binding(1)]] var x_20 : texture_depth_2d;)",
2524          R"(let x_99 : vec4<f32> = vec4<f32>(textureLoad(x_20, vi12, 0), 0.0, 0.0, 0.0);)"}}));
2525 
2526 INSTANTIATE_TEST_SUITE_P(
2527     ImageFetch_DepthMultisampled,
2528     // In SPIR-V OpImageFetch always yields a vector of 4
2529     // elements, even for depth images.  But in WGSL,
2530     // textureLoad on a depth image yields f32.
2531     // crbug.com/tint/439
2532     SpvParserHandleTest_ImageAccessTest,
2533     ::testing::ValuesIn(std::vector<ImageAccessCase>{
2534         // ImageFetch on multisampled depth image.
2535         {"%float 2D 1 0 1 1 Unknown",
2536          "%99 = OpImageFetch %v4float %im %vi12 Sample %i1",
2537          R"([[group(2), binding(1)]] var x_20 : texture_depth_multisampled_2d;)",
2538          R"(let x_99 : vec4<f32> = vec4<f32>(textureLoad(x_20, vi12, i1), 0.0, 0.0, 0.0);)"}}));
2539 
2540 INSTANTIATE_TEST_SUITE_P(
2541     ImageFetch_Multisampled,
2542     SpvParserHandleTest_ImageAccessTest,
2543     ::testing::ValuesIn(std::vector<ImageAccessCase>{
2544         // SPIR-V requires a Sample image operand when operating on a
2545         // multisampled image.
2546 
2547         // ImageFetch arrayed
2548         // Not in WebGPU
2549 
2550         // ImageFetch non-arrayed
2551         {"%float 2D 0 0 1 1 Unknown",
2552          "%99 = OpImageFetch %v4float %im %vi12 Sample %i1",
2553          R"([[group(2), binding(1)]] var x_20 : texture_multisampled_2d<f32>;)",
2554          R"(let x_99 : vec4<f32> = textureLoad(x_20, vi12, i1);)"}}));
2555 
2556 INSTANTIATE_TEST_SUITE_P(
2557     ImageFetch_Multisampled_ConvertSampleOperand,
2558     SpvParserHandleTest_ImageAccessTest,
2559     ::testing::ValuesIn(std::vector<ImageAccessCase>{
2560         {"%float 2D 0 0 1 1 Unknown",
2561          "%99 = OpImageFetch %v4float %im %vi12 Sample %u1",
2562          R"([[group(2), binding(1)]] var x_20 : texture_multisampled_2d<f32>;)",
2563          R"(let x_99 : vec4<f32> = textureLoad(x_20, vi12, i32(u1));)"}}));
2564 
2565 INSTANTIATE_TEST_SUITE_P(
2566     ConvertResultSignedness,
2567     SpvParserHandleTest_SampledImageAccessTest,
2568     ::testing::ValuesIn(std::vector<ImageAccessCase>{
2569         // Valid SPIR-V only has:
2570         //      float scalar sampled type vs. floating result
2571         //      integral scalar sampled type vs. integral result
2572         // Any of the sampling, reading, or fetching use the same codepath.
2573 
2574         // We'll test with:
2575         //     OpImageFetch
2576         //     OpImageRead
2577         //     OpImageSampleImplicitLod - representative of sampling
2578 
2579         //
2580         // OpImageRead
2581         //
2582 
2583         // OpImageFetch requires no conversion, float -> v4float
2584         {"%float 2D 0 0 0 1 Unknown", "%99 = OpImageFetch %v4float %im %vi12",
2585          R"([[group(2), binding(1)]] var x_20 : texture_2d<f32>;)",
2586          R"(let x_99 : vec4<f32> = textureLoad(x_20, vi12, 0);)"},
2587         // OpImageFetch requires no conversion, uint -> v4uint
2588         {"%uint 2D 0 0 0 1 Unknown", "%99 = OpImageFetch %v4uint %im %vi12",
2589          R"([[group(2), binding(1)]] var x_20 : texture_2d<u32>;)",
2590          R"(let x_99 : vec4<u32> = textureLoad(x_20, vi12, 0);)"},
2591         // OpImageFetch requires conversion, uint -> v4int
2592         // is invalid SPIR-V:
2593         // "Expected Image 'Sampled Type' to be the same as Result Type
2594         // components"
2595 
2596         // OpImageFetch requires no conversion, int -> v4int
2597         {"%int 2D 0 0 0 1 Unknown", "%99 = OpImageFetch %v4int %im %vi12",
2598          R"([[group(2), binding(1)]] var x_20 : texture_2d<i32>;)",
2599          R"(let x_99 : vec4<i32> = textureLoad(x_20, vi12, 0);)"},
2600         // OpImageFetch requires conversion, int -> v4uint
2601         // is invalid SPIR-V:
2602         // "Expected Image 'Sampled Type' to be the same as Result Type
2603         // components"
2604 
2605         //
2606         // OpImageRead
2607         //
2608 
2609         // OpImageRead requires no conversion, float -> v4float
2610         {"%float 2D 0 0 0 2 Rgba32f", "%99 = OpImageRead %v4float %im %vi12",
2611          R"([[group(2), binding(1)]] var x_20 : texture_2d<f32>;)",
2612          R"(let x_99 : vec4<f32> = textureLoad(x_20, vi12, 0);)"},
2613         // OpImageRead requires no conversion, uint -> v4uint
2614         {"%uint 2D 0 0 0 2 Rgba32ui", "%99 = OpImageRead %v4uint %im %vi12",
2615          R"([[group(2), binding(1)]] var x_20 : texture_2d<u32>;)",
2616          R"(let x_99 : vec4<u32> = textureLoad(x_20, vi12, 0);)"},
2617 
2618         // OpImageRead requires conversion, uint -> v4int
2619         // is invalid SPIR-V:
2620         // "Expected Image 'Sampled Type' to be the same as Result Type
2621         // components"
2622 
2623         // OpImageRead requires no conversion, int -> v4int
2624         {"%int 2D 0 0 0 2 Rgba32i", "%99 = OpImageRead %v4int %im %vi12",
2625          R"([[group(2), binding(1)]] var x_20 : texture_2d<i32>;)",
2626          R"(let x_99 : vec4<i32> = textureLoad(x_20, vi12, 0);)"},
2627 
2628         // OpImageRead requires conversion, int -> v4uint
2629         // is invalid SPIR-V:
2630         // "Expected Image 'Sampled Type' to be the same as Result Type
2631         // components"
2632 
2633         //
2634         // Sampling operations, using OpImageSampleImplicitLod as an example.
2635         // WGSL sampling operations only work on textures with a float sampled
2636         // component.  So we can only test the float -> float (non-conversion)
2637         // case.
2638 
2639         // OpImageSampleImplicitLod requires no conversion, float -> v4float
2640         {"%float 2D 0 0 0 1 Unknown",
2641          "%99 = OpImageSampleImplicitLod %v4float %sampled_image %vf12",
2642          R"([[group(0), binding(0)]] var x_10 : sampler;
2643 
2644 [[group(2), binding(1)]] var x_20 : texture_2d<f32>;)",
2645          R"(let x_99 : vec4<f32> = textureSample(x_20, x_10, vf12);)"}}));
2646 
2647 INSTANTIATE_TEST_SUITE_P(
2648     ImageQuerySize_NonArrayed_SignedResult,
2649     // ImageQuerySize requires storage image or multisampled
2650     // For storage image, use another instruction to indicate whether it
2651     // is readonly or writeonly.
2652     SpvParserHandleTest_SampledImageAccessTest,
2653     ::testing::ValuesIn(std::vector<ImageAccessCase>{
2654         // 1D storage image
2655         {"%float 1D 0 0 0 2 Rgba32f",
2656          "%99 = OpImageQuerySize %int %im \n"
2657          "%98 = OpImageRead %v4float %im %i1\n",  // Implicitly mark as
2658                                                   // NonWritable
2659          R"([[group(2), binding(1)]] var x_20 : texture_1d<f32>;)",
2660          R"(let x_99 : i32 = i32(textureDimensions(x_20));)"},
2661         // 2D storage image
2662         {"%float 2D 0 0 0 2 Rgba32f",
2663          "%99 = OpImageQuerySize %v2int %im \n"
2664          "%98 = OpImageRead %v4float %im %vi12\n",  // Implicitly mark as
2665                                                     // NonWritable
2666          R"([[group(2), binding(1)]] var x_20 : texture_2d<f32>;)",
2667          R"(let x_99 : vec2<i32> = vec2<i32>(textureDimensions(x_20))"},
2668         // 3D storage image
2669         {"%float 3D 0 0 0 2 Rgba32f",
2670          "%99 = OpImageQuerySize %v3int %im \n"
2671          "%98 = OpImageRead %v4float %im %vi123\n",  // Implicitly mark as
2672                                                      // NonWritable
2673          R"([[group(2), binding(1)]] var x_20 : texture_3d<f32>;)",
2674          R"(let x_99 : vec3<i32> = vec3<i32>(textureDimensions(x_20));)"},
2675 
2676         // Multisampled
2677         {"%float 2D 0 0 1 1 Unknown", "%99 = OpImageQuerySize %v2int %im \n",
2678          R"([[group(2), binding(1)]] var x_20 : texture_multisampled_2d<f32>;)",
2679          R"(let x_99 : vec2<i32> = vec2<i32>(textureDimensions(x_20));)"}}));
2680 
2681 INSTANTIATE_TEST_SUITE_P(
2682     ImageQuerySize_Arrayed_SignedResult,
2683     // ImageQuerySize requires storage image or multisampled
2684     // For storage image, use another instruction to indicate whether it
2685     // is readonly or writeonly.
2686     SpvParserHandleTest_SampledImageAccessTest,
2687     ::testing::ValuesIn(std::vector<ImageAccessCase>{
2688         // 1D array storage image doesn't exist.
2689 
2690         // 2D array storage image
2691         {"%float 2D 0 1 0 2 Rgba32f",
2692          "%99 = OpImageQuerySize %v3int %im \n"
2693          "%98 = OpImageRead %v4float %im %vi123\n",
2694          R"([[group(2), binding(1)]] var x_20 : texture_2d_array<f32>;)",
2695          R"(let x_99 : vec3<i32> = vec3<i32>(textureDimensions(x_20), textureNumLayers(x_20));)"}
2696         // 3D array storage image doesn't exist.
2697 
2698         // Multisampled array
2699         // Not in WebGPU
2700     }));
2701 
2702 INSTANTIATE_TEST_SUITE_P(
2703     ImageQuerySizeLod_NonArrayed_SignedResult_SignedLevel,
2704     // From VUID-StandaloneSpirv-OpImageQuerySizeLod-04659:
2705     //  ImageQuerySizeLod requires Sampled=1
2706     SpvParserHandleTest_SampledImageAccessTest,
2707     ::testing::ValuesIn(std::vector<ImageAccessCase>{
2708         // 1D
2709         {"%float 1D 0 0 0 1 Unknown",
2710          "%99 = OpImageQuerySizeLod %int %im %i1\n",
2711          R"([[group(2), binding(1)]] var x_20 : texture_1d<f32>;)",
2712          R"(let x_99 : i32 = i32(textureDimensions(x_20, i1)))"},
2713 
2714         // 2D
2715         {"%float 2D 0 0 0 1 Unknown",
2716          "%99 = OpImageQuerySizeLod %v2int %im %i1\n",
2717          R"([[group(2), binding(1)]] var x_20 : texture_2d<f32>;)",
2718          R"(let x_99 : vec2<i32> = vec2<i32>(textureDimensions(x_20, i1));)"},
2719 
2720         // 3D
2721         {"%float 3D 0 0 0 1 Unknown",
2722          "%99 = OpImageQuerySizeLod %v3int %im %i1\n",
2723          R"([[group(2), binding(1)]] var x_20 : texture_3d<f32>;)",
2724          R"(let x_99 : vec3<i32> = vec3<i32>(textureDimensions(x_20, i1));)"},
2725 
2726         // Cube
2727         {"%float Cube 0 0 0 1 Unknown",
2728          "%99 = OpImageQuerySizeLod %v2int %im %i1\n",
2729          R"([[group(2), binding(1)]] var x_20 : texture_cube<f32>;)",
2730          R"(let x_99 : vec2<i32> = vec2<i32>(textureDimensions(x_20, i1).xy);)"},
2731 
2732         // Depth 2D
2733         {"%float 2D 1 0 0 1 Unknown",
2734          "%99 = OpImageQuerySizeLod %v2int %im %i1\n",
2735          R"([[group(2), binding(1)]] var x_20 : texture_depth_2d;)",
2736          R"(let x_99 : vec2<i32> = vec2<i32>(textureDimensions(x_20, i1));)"},
2737 
2738         // Depth Cube
2739         {"%float Cube 1 0 0 1 Unknown",
2740          "%99 = OpImageQuerySizeLod %v2int %im %i1\n",
2741          R"([[group(2), binding(1)]] var x_20 : texture_depth_cube;)",
2742          R"(let x_99 : vec2<i32> = vec2<i32>(textureDimensions(x_20, i1).xy);)"}}));
2743 
2744 INSTANTIATE_TEST_SUITE_P(
2745     ImageQuerySizeLod_Arrayed_SignedResult_SignedLevel,
2746     // ImageQuerySize requires storage image or multisampled
2747     // For storage image, use another instruction to indicate whether it
2748     // is readonly or writeonly.
2749     SpvParserHandleTest_SampledImageAccessTest,
2750     ::testing::ValuesIn(std::vector<ImageAccessCase>{
2751 
2752         // There is no 1D array
2753 
2754         // 2D array
2755         {"%float 2D 0 1 0 1 Unknown",
2756          "%99 = OpImageQuerySizeLod %v3int %im %i1\n",
2757          R"([[group(2), binding(1)]] var x_20 : texture_2d_array<f32>;)",
2758          R"(let x_99 : vec3<i32> = vec3<i32>(textureDimensions(x_20, i1), textureNumLayers(x_20));)"},
2759 
2760         // There is no 3D array
2761 
2762         // Cube array
2763         //
2764         // Currently textureDimension on cube returns vec3 but maybe should
2765         // return vec2
2766         // https://github.com/gpuweb/gpuweb/issues/1345
2767         {"%float Cube 0 1 0 1 Unknown",
2768          "%99 = OpImageQuerySizeLod %v3int %im %i1\n",
2769          R"([[group(2), binding(1)]] var x_20 : texture_cube_array<f32>;)",
2770          R"(let x_99 : vec3<i32> = vec3<i32>(textureDimensions(x_20, i1).xy, textureNumLayers(x_20));)"},
2771 
2772         // Depth 2D array
2773         {"%float 2D 1 1 0 1 Unknown",
2774          "%99 = OpImageQuerySizeLod %v3int %im %i1\n",
2775          R"([[group(2), binding(1)]] var x_20 : texture_depth_2d_array;)",
2776          R"(let x_99 : vec3<i32> = vec3<i32>(textureDimensions(x_20, i1), textureNumLayers(x_20));)"},
2777 
2778         // Depth Cube Array
2779         //
2780         // Currently textureDimension on cube returns vec3 but maybe should
2781         // return vec2
2782         // https://github.com/gpuweb/gpuweb/issues/1345
2783         {"%float Cube 1 1 0 1 Unknown",
2784          "%99 = OpImageQuerySizeLod %v3int %im %i1\n",
2785          R"([[group(2), binding(1)]] var x_20 : texture_depth_cube_array;)",
2786          R"(let x_99 : vec3<i32> = vec3<i32>(textureDimensions(x_20, i1).xy, textureNumLayers(x_20));)"}}));
2787 
2788 INSTANTIATE_TEST_SUITE_P(
2789     // When the level-of-detail value is given as an unsigned
2790     // integer, we must convert it before using it as an argument
2791     // to textureDimensions.
2792     ImageQuerySizeLod_NonArrayed_SignedResult_UnsignedLevel,
2793     SpvParserHandleTest_SampledImageAccessTest,
2794     ::testing::ValuesIn(std::vector<ImageAccessCase>{
2795 
2796         {"%float 1D 0 0 0 1 Unknown",
2797          "%99 = OpImageQuerySizeLod %int %im %u1\n",
2798          R"([[group(2), binding(1)]] var x_20 : texture_1d<f32>;)",
2799          R"(let x_99 : i32 = i32(textureDimensions(x_20, i32(u1)));)"}}));
2800 
2801 INSTANTIATE_TEST_SUITE_P(
2802     // When SPIR-V wants the result type to be unsigned, we have to
2803     // insert a type constructor or bitcast for WGSL to do the type
2804     // coercion. But the algorithm already does that as a matter
2805     // of course.
2806     ImageQuerySizeLod_NonArrayed_UnsignedResult_SignedLevel,
2807     SpvParserHandleTest_SampledImageAccessTest,
2808     ::testing::ValuesIn(std::vector<ImageAccessCase>{
2809 
2810         {"%float 1D 0 0 0 1 Unknown",
2811          "%99 = OpImageQuerySizeLod %uint %im %i1\n",
2812          R"([[group(2), binding(1)]] var x_20 : texture_1d<f32>;)",
2813          R"(let x_99 : u32 = u32(textureDimensions(x_20, i1));)"}}));
2814 
2815 INSTANTIATE_TEST_SUITE_P(
2816     ImageQueryLevels_SignedResult,
2817     SpvParserHandleTest_SampledImageAccessTest,
2818     ::testing::ValuesIn(std::vector<ImageAccessCase>{
2819         // In Vulkan:
2820         //      Dim must be 1D, 2D, 3D, Cube
2821         // WGSL allows 2d, 2d_array, 3d, cube, cube_array
2822         // depth_2d, depth_2d_array, depth_cube, depth_cube_array
2823 
2824         // 2D
2825         {"%float 2D 0 0 0 1 Unknown", "%99 = OpImageQueryLevels %int %im\n",
2826          R"([[group(2), binding(1)]] var x_20 : texture_2d<f32>;)",
2827          R"(let x_99 : i32 = textureNumLevels(x_20);)"},
2828 
2829         // 2D array
2830         {"%float 2D 0 1 0 1 Unknown", "%99 = OpImageQueryLevels %int %im\n",
2831          R"([[group(2), binding(1)]] var x_20 : texture_2d_array<f32>;)",
2832          R"(let x_99 : i32 = textureNumLevels(x_20);)"},
2833 
2834         // 3D
2835         {"%float 3D 0 0 0 1 Unknown", "%99 = OpImageQueryLevels %int %im\n",
2836          R"([[group(2), binding(1)]] var x_20 : texture_3d<f32>;)",
2837          R"(let x_99 : i32 = textureNumLevels(x_20);)"},
2838 
2839         // Cube
2840         {"%float Cube 0 0 0 1 Unknown", "%99 = OpImageQueryLevels %int %im\n",
2841          R"([[group(2), binding(1)]] var x_20 : texture_cube<f32>;)",
2842          R"(let x_99 : i32 = textureNumLevels(x_20);)"},
2843 
2844         // Cube array
2845         {"%float Cube 0 1 0 1 Unknown", "%99 = OpImageQueryLevels %int %im\n",
2846          R"([[group(2), binding(1)]] var x_20 : texture_cube_array<f32>;)",
2847          R"(let x_99 : i32 = textureNumLevels(x_20);)"},
2848 
2849         // depth 2d
2850         {"%float 2D 1 0 0 1 Unknown", "%99 = OpImageQueryLevels %int %im\n",
2851          R"([[group(2), binding(1)]] var x_20 : texture_depth_2d;)",
2852          R"(let x_99 : i32 = textureNumLevels(x_20);)"},
2853 
2854         // depth 2d array
2855         {"%float 2D 1 1 0 1 Unknown", "%99 = OpImageQueryLevels %int %im\n",
2856          R"([[group(2), binding(1)]] var x_20 : texture_depth_2d_array;)",
2857          R"(let x_99 : i32 = textureNumLevels(x_20);)"},
2858 
2859         // depth cube
2860         {"%float Cube 1 0 0 1 Unknown", "%99 = OpImageQueryLevels %int %im\n",
2861          R"([[group(2), binding(1)]] var x_20 : texture_depth_cube;)",
2862          R"(let x_99 : i32 = textureNumLevels(x_20);)"},
2863 
2864         // depth cube array
2865         {"%float Cube 1 1 0 1 Unknown", "%99 = OpImageQueryLevels %int %im\n",
2866          R"([[group(2), binding(1)]] var x_20 : texture_depth_cube_array;)",
2867          R"(let x_99 : i32 = textureNumLevels(x_20);)"}}));
2868 
2869 INSTANTIATE_TEST_SUITE_P(
2870     // Spot check that a type conversion is inserted when SPIR-V asks for
2871     // an unsigned int result.
2872     ImageQueryLevels_UnsignedResult,
2873     SpvParserHandleTest_SampledImageAccessTest,
2874     ::testing::ValuesIn(std::vector<ImageAccessCase>{
2875         {"%float 2D 0 0 0 1 Unknown", "%99 = OpImageQueryLevels %uint %im\n",
2876          R"([[group(2), binding(1)]] var x_20 : texture_2d<f32>;)",
2877          R"(let x_99 : u32 = u32(textureNumLevels(x_20));)"}}));
2878 
2879 INSTANTIATE_TEST_SUITE_P(
2880     ImageQuerySamples_SignedResult,
2881     SpvParserHandleTest_SampledImageAccessTest,
2882     ::testing::ValuesIn(std::vector<ImageAccessCase>{
2883         // Multsample 2D
2884         {"%float 2D 0 0 1 1 Unknown", "%99 = OpImageQuerySamples %int %im\n",
2885          R"([[group(2), binding(1)]] var x_20 : texture_multisampled_2d<f32>;)",
2886          R"(let x_99 : i32 = textureNumSamples(x_20);)"}  // namespace
2887 
2888         // Multisample 2D array
2889         // Not in WebGPU
2890     }));
2891 
2892 INSTANTIATE_TEST_SUITE_P(
2893     // Translation must inject a type coersion from signed to unsigned.
2894     ImageQuerySamples_UnsignedResult,
2895     SpvParserHandleTest_SampledImageAccessTest,
2896     ::testing::ValuesIn(std::vector<ImageAccessCase>{
2897         // Multsample 2D
2898         {"%float 2D 0 0 1 1 Unknown", "%99 = OpImageQuerySamples %uint %im\n",
2899          R"([[group(2), binding(1)]] var x_20 : texture_multisampled_2d<f32>;)",
2900          R"(let x_99 : u32 = u32(textureNumSamples(x_20));)"}
2901 
2902         // Multisample 2D array
2903         // Not in WebGPU
2904     }));
2905 
2906 struct ImageCoordsCase {
2907   // SPIR-V image type, excluding result ID and opcode
2908   std::string spirv_image_type_details;
2909   std::string spirv_image_access;
2910   std::string expected_error;
2911   std::vector<std::string> expected_expressions;
2912 };
2913 
operator <<(std::ostream & out,const ImageCoordsCase & c)2914 inline std::ostream& operator<<(std::ostream& out, const ImageCoordsCase& c) {
2915   out << "ImageCoordsCase(" << c.spirv_image_type_details << "\n"
2916       << c.spirv_image_access << "\n"
2917       << "expected_error(" << c.expected_error << ")\n";
2918 
2919   for (auto e : c.expected_expressions) {
2920     out << e << ",";
2921   }
2922   out << ")" << std::endl;
2923   return out;
2924 }
2925 
2926 using SpvParserHandleTest_ImageCoordsTest =
2927     SpvParserTestBase<::testing::TestWithParam<ImageCoordsCase>>;
2928 
TEST_P(SpvParserHandleTest_ImageCoordsTest,MakeCoordinateOperandsForImageAccess)2929 TEST_P(SpvParserHandleTest_ImageCoordsTest,
2930        MakeCoordinateOperandsForImageAccess) {
2931   // Only declare the sampled image type, and the associated variable
2932   // if the requested image type is a sampled image type and not multisampled.
2933   const bool is_sampled_image_type = GetParam().spirv_image_type_details.find(
2934                                          "0 1 Unknown") != std::string::npos;
2935   const auto assembly =
2936       Preamble() + R"(
2937      OpEntryPoint Fragment %100 "main"
2938      OpExecutionMode %100 OriginUpperLeft
2939      OpName %float_var "float_var"
2940      OpName %ptr_float "ptr_float"
2941      OpName %i1 "i1"
2942      OpName %vi12 "vi12"
2943      OpName %vi123 "vi123"
2944      OpName %vi1234 "vi1234"
2945      OpName %u1 "u1"
2946      OpName %vu12 "vu12"
2947      OpName %vu123 "vu123"
2948      OpName %vu1234 "vu1234"
2949      OpName %f1 "f1"
2950      OpName %vf12 "vf12"
2951      OpName %vf123 "vf123"
2952      OpName %vf1234 "vf1234"
2953      OpDecorate %10 DescriptorSet 0
2954      OpDecorate %10 Binding 0
2955      OpDecorate %20 DescriptorSet 2
2956      OpDecorate %20 Binding 1
2957      OpDecorate %30 DescriptorSet 0
2958      OpDecorate %30 Binding 1
2959 )" + CommonBasicTypes() +
2960       R"(
2961      %sampler = OpTypeSampler
2962      %ptr_sampler = OpTypePointer UniformConstant %sampler
2963      %im_ty = OpTypeImage )" +
2964       GetParam().spirv_image_type_details + R"(
2965      %ptr_im_ty = OpTypePointer UniformConstant %im_ty
2966 )" + (is_sampled_image_type ? " %si_ty = OpTypeSampledImage %im_ty " : "") +
2967       R"(
2968 
2969      %ptr_float = OpTypePointer Function %float
2970 
2971      %10 = OpVariable %ptr_sampler UniformConstant
2972      %20 = OpVariable %ptr_im_ty UniformConstant
2973      %30 = OpVariable %ptr_sampler UniformConstant ; comparison sampler, when needed
2974 
2975      %100 = OpFunction %void None %voidfn
2976      %entry = OpLabel
2977 
2978      %float_var = OpVariable %ptr_float Function
2979 
2980      %i1 = OpCopyObject %int %int_1
2981      %vi12 = OpCopyObject %v2int %the_vi12
2982      %vi123 = OpCopyObject %v3int %the_vi123
2983      %vi1234 = OpCopyObject %v4int %the_vi1234
2984 
2985      %u1 = OpCopyObject %uint %uint_1
2986      %vu12 = OpCopyObject %v2uint %the_vu12
2987      %vu123 = OpCopyObject %v3uint %the_vu123
2988      %vu1234 = OpCopyObject %v4uint %the_vu1234
2989 
2990      %f1 = OpCopyObject %float %float_1
2991      %vf12 = OpCopyObject %v2float %the_vf12
2992      %vf123 = OpCopyObject %v3float %the_vf123
2993      %vf1234 = OpCopyObject %v4float %the_vf1234
2994 
2995      %sam = OpLoad %sampler %10
2996      %im = OpLoad %im_ty %20
2997 
2998 )" +
2999       (is_sampled_image_type
3000            ? " %sampled_image = OpSampledImage %si_ty %im %sam "
3001            : "") +
3002       GetParam().spirv_image_access +
3003       R"(
3004      ; Use an anchor for the cases when the image access doesn't have a result ID.
3005      %1000 = OpCopyObject %uint %uint_0
3006 
3007      OpReturn
3008      OpFunctionEnd
3009   )";
3010   auto p = parser(test::Assemble(assembly));
3011   if (!p->BuildAndParseInternalModule()) {
3012     EXPECT_THAT(p->error(), StartsWith(GetParam().expected_error)) << assembly;
3013   } else {
3014     EXPECT_TRUE(p->error().empty()) << p->error();
3015     auto fe = p->function_emitter(100);
3016     // We actually have to generate the module to cache expressions for the
3017     // result IDs, particularly the OpCopyObject
3018     fe.Emit();
3019 
3020     const spvtools::opt::Instruction* anchor = p->GetInstructionForTest(1000);
3021     ASSERT_NE(anchor, nullptr);
3022     const spvtools::opt::Instruction& image_access = *(anchor->PreviousNode());
3023 
3024     ast::ExpressionList result =
3025         fe.MakeCoordinateOperandsForImageAccess(image_access);
3026     if (GetParam().expected_error.empty()) {
3027       EXPECT_TRUE(fe.success()) << p->error();
3028       EXPECT_TRUE(p->error().empty());
3029       std::vector<std::string> result_strings;
3030       Program program = p->program();
3031       for (auto* expr : result) {
3032         ASSERT_NE(expr, nullptr);
3033         result_strings.push_back(test::ToString(program, expr));
3034       }
3035       EXPECT_THAT(result_strings,
3036                   ::testing::ContainerEq(GetParam().expected_expressions));
3037     } else {
3038       EXPECT_FALSE(fe.success());
3039       EXPECT_THAT(p->error(), Eq(GetParam().expected_error)) << assembly;
3040       EXPECT_TRUE(result.empty());
3041     }
3042   }
3043 
3044   const bool is_sample_level =
3045       GetParam().spirv_image_access.find("ImageSampleExplicitLod") !=
3046       std::string::npos;
3047   const bool is_comparison_sample_level =
3048       GetParam().spirv_image_access.find("ImageSampleDrefExplicitLod") !=
3049       std::string::npos;
3050   const bool is_1d =
3051       GetParam().spirv_image_type_details.find("1D") != std::string::npos;
3052   if (is_sample_level && is_1d) {
3053     p->SkipDumpingPending("crbug.com/tint/789");
3054   }
3055   if (is_comparison_sample_level) {
3056     p->SkipDumpingPending("crbug.com/tint/425");
3057   }
3058 }
3059 
3060 INSTANTIATE_TEST_SUITE_P(Good_1D,
3061                          SpvParserHandleTest_ImageCoordsTest,
3062                          ::testing::ValuesIn(std::vector<ImageCoordsCase>{
3063                              {"%float 1D 0 0 0 1 Unknown",
3064                               "%result = OpImageSampleImplicitLod %v4float "
3065                               "%sampled_image %f1",
3066                               "",
3067                               {"f1"}},
3068                              {"%float 1D 0 0 0 1 Unknown",
3069                               "%result = OpImageSampleImplicitLod %v4float "
3070                               "%sampled_image %vf12",  // one excess arg
3071                               "",
3072                               {"vf12.x"}},
3073                              {"%float 1D 0 0 0 1 Unknown",
3074                               "%result = OpImageSampleImplicitLod %v4float "
3075                               "%sampled_image %vf123",  // two excess args
3076                               "",
3077                               {"vf123.x"}},
3078                              {"%float 1D 0 0 0 1 Unknown",
3079                               "%result = OpImageSampleImplicitLod %v4float "
3080                               "%sampled_image %vf1234",  // three excess args
3081                               "",
3082                               {"vf1234.x"}}}));
3083 
3084 INSTANTIATE_TEST_SUITE_P(Good_1DArray,
3085                          SpvParserHandleTest_ImageCoordsTest,
3086                          ::testing::ValuesIn(std::vector<ImageCoordsCase>{
3087                              {"%float 1D 0 1 0 1 Unknown",
3088                               "%result = OpImageSampleImplicitLod %v4float "
3089                               "%sampled_image %vf12",
3090                               "",
3091                               {"vf12.x", "i32(round(vf12.y))"}},
3092                              {"%float 1D 0 1 0 1 Unknown",
3093                               "%result = OpImageSampleImplicitLod %v4float "
3094                               "%sampled_image %vf123",  // one excess arg
3095                               "",
3096                               {"vf123.x", "i32(round(vf123.y))"}},
3097                              {"%float 1D 0 1 0 1 Unknown",
3098                               "%result = OpImageSampleImplicitLod %v4float "
3099                               "%sampled_image %vf1234",  // two excess args
3100                               "",
3101                               {"vf1234.x", "i32(round(vf1234.y))"}}}));
3102 
3103 INSTANTIATE_TEST_SUITE_P(Good_2D,
3104                          SpvParserHandleTest_ImageCoordsTest,
3105                          ::testing::ValuesIn(std::vector<ImageCoordsCase>{
3106                              {"%float 2D 0 0 0 1 Unknown",
3107                               "%result = OpImageSampleImplicitLod %v4float "
3108                               "%sampled_image %vf12",
3109                               "",
3110                               {"vf12"}},
3111                              {"%float 2D 0 0 0 1 Unknown",
3112                               "%result = OpImageSampleImplicitLod %v4float "
3113                               "%sampled_image %vf123",  // one excess arg
3114                               "",
3115                               {"vf123.xy"}},
3116                              {"%float 2D 0 0 0 1 Unknown",
3117                               "%result = OpImageSampleImplicitLod %v4float "
3118                               "%sampled_image %vf1234",  // two excess args
3119                               "",
3120                               {"vf1234.xy"}}}));
3121 
3122 INSTANTIATE_TEST_SUITE_P(Good_2DArray,
3123                          SpvParserHandleTest_ImageCoordsTest,
3124                          ::testing::ValuesIn(std::vector<ImageCoordsCase>{
3125                              {"%float 2D 0 1 0 1 Unknown",
3126                               "%result = OpImageSampleImplicitLod %v4float "
3127                               "%sampled_image %vf123",
3128                               "",
3129                               {"vf123.xy", "i32(round(vf123.z))"}},
3130                              {"%float 2D 0 1 0 1 Unknown",
3131                               "%result = OpImageSampleImplicitLod %v4float "
3132                               "%sampled_image %vf1234",  // one excess arg
3133                               "",
3134                               {"vf1234.xy", "i32(round(vf1234.z))"}}}));
3135 
3136 INSTANTIATE_TEST_SUITE_P(Good_3D,
3137                          SpvParserHandleTest_ImageCoordsTest,
3138                          ::testing::ValuesIn(std::vector<ImageCoordsCase>{
3139                              {"%float 3D 0 0 0 1 Unknown",
3140                               "%result = OpImageSampleImplicitLod "
3141                               "%v4float "
3142                               "%sampled_image %vf123",
3143                               "",
3144                               {"vf123"}},
3145                              {"%float 3D 0 0 0 1 Unknown",
3146                               "%result = OpImageSampleImplicitLod "
3147                               "%v4float "
3148                               "%sampled_image %vf1234",  // one excess
3149                                                          // arg
3150                               "",
3151                               {"vf1234.xyz"}}}));
3152 
3153 INSTANTIATE_TEST_SUITE_P(Good_Cube,
3154                          SpvParserHandleTest_ImageCoordsTest,
3155                          ::testing::ValuesIn(std::vector<ImageCoordsCase>{
3156                              {"%float Cube 0 0 0 1 Unknown",
3157                               "%result = OpImageSampleImplicitLod "
3158                               "%v4float "
3159                               "%sampled_image %vf123",
3160                               "",
3161                               {"vf123"}},
3162                              {"%float Cube 0 0 0 1 Unknown",
3163                               "%result = OpImageSampleImplicitLod "
3164                               "%v4float "
3165                               "%sampled_image %vf1234",  // one excess
3166                                                          // arg
3167                               "",
3168                               {"vf1234.xyz"}}}));
3169 
3170 INSTANTIATE_TEST_SUITE_P(Good_CubeArray,
3171                          SpvParserHandleTest_ImageCoordsTest,
3172                          ::testing::ValuesIn(std::vector<ImageCoordsCase>{
3173                              {"%float Cube 0 1 0 1 Unknown",
3174                               "%result = OpImageSampleImplicitLod "
3175                               "%v4float "
3176                               "%sampled_image %vf1234",
3177                               "",
3178                               {"vf1234.xyz", "i32(round(vf1234.w))"}}}));
3179 
3180 INSTANTIATE_TEST_SUITE_P(
3181     PreserveFloatCoords_NonArrayed,
3182     // In SPIR-V, sampling and dref sampling operations use floating point
3183     // coordinates.  Prove that we preserve floating point-ness.
3184     // Test across all such instructions.
3185     SpvParserHandleTest_ImageCoordsTest,
3186     ::testing::ValuesIn(std::vector<ImageCoordsCase>{
3187         // Scalar cases
3188         {"%float 1D 0 0 0 1 Unknown",
3189          "%result = OpImageSampleImplicitLod %v4float %sampled_image %f1",
3190          "",
3191          {"f1"}},
3192         {"%float 1D 0 0 0 1 Unknown",
3193          "%result = OpImageSampleExplicitLod %v4float %sampled_image %f1 Lod "
3194          "%f1",
3195          "",
3196          {"f1"}},
3197         // WGSL does not support depth textures with 1D coordinates
3198         // Vector cases
3199         {"%float 2D 0 0 0 1 Unknown",
3200          "%result = OpImageSampleImplicitLod %v4float %sampled_image %vf12",
3201          "",
3202          {"vf12"}},
3203         {"%float 2D 0 0 0 1 Unknown",
3204          "%result = OpImageSampleExplicitLod %v4float %sampled_image %vf12 Lod "
3205          "%f1",
3206          "",
3207          {"vf12"}},
3208         {"%float 2D 1 0 0 1 Unknown",
3209          "%result = OpImageSampleDrefImplicitLod %float %sampled_image %vf12 "
3210          "%depth",
3211          "",
3212          {"vf12"}},
3213         {"%float 2D 1 0 0 1 Unknown",
3214          "%result = OpImageSampleDrefExplicitLod %float %sampled_image %vf12 "
3215          "%depth Lod %float_0",
3216          "",
3217          {"vf12"}},
3218     }));
3219 
3220 INSTANTIATE_TEST_SUITE_P(
3221     PreserveFloatCoords_Arrayed,
3222     // In SPIR-V, sampling and dref sampling operations use floating point
3223     // coordinates.  Prove that we preserve floating point-ness of the
3224     // coordinate part, but convert the array index to signed integer. Test
3225     // across all such instructions.
3226     SpvParserHandleTest_ImageCoordsTest,
3227     ::testing::ValuesIn(std::vector<ImageCoordsCase>{
3228         {"%float 2D 0 1 0 1 Unknown",
3229          "%result = OpImageSampleImplicitLod %v4float %sampled_image %vf123",
3230          "",
3231          {"vf123.xy", "i32(round(vf123.z))"}},
3232 
3233         {"%float 2D 0 1 0 1 Unknown",
3234          "%result = OpImageSampleExplicitLod %v4float %sampled_image %vf123 "
3235          "Lod %f1",
3236          "",
3237          {"vf123.xy", "i32(round(vf123.z))"}},
3238         {"%float 2D 1 1 0 1 Unknown",
3239          "%result = OpImageSampleDrefImplicitLod %float %sampled_image "
3240          "%vf123 %depth",
3241          "",
3242          {"vf123.xy", "i32(round(vf123.z))"}},
3243         {"%float 2D 1 1 0 1 Unknown",
3244          "%result = OpImageSampleDrefExplicitLod %float %sampled_image "
3245          "%vf123 %depth Lod %float_0",
3246          "",
3247          {"vf123.xy", "i32(round(vf123.z))"}}}));
3248 
3249 INSTANTIATE_TEST_SUITE_P(
3250     PreserveIntCoords_NonArrayed,
3251     // In SPIR-V, image read, fetch, and write use integer coordinates.
3252     // Prove that we preserve signed integer coordinates.
3253     SpvParserHandleTest_ImageCoordsTest,
3254     ::testing::ValuesIn(std::vector<ImageCoordsCase>{
3255         // Scalar cases
3256         {"%float 1D 0 0 0 1 Unknown",
3257          "%result = OpImageFetch %v4float %im %i1",
3258          "",
3259          {"i1"}},
3260         {"%float 1D 0 0 0 2 R32f",
3261          "%result = OpImageRead %v4float %im %i1",
3262          "",
3263          {"i1"}},
3264         {"%float 1D 0 0 0 2 R32f", "OpImageWrite %im %i1 %vf1234", "", {"i1"}},
3265         // Vector cases
3266         {"%float 2D 0 0 0 1 Unknown",
3267          "%result = OpImageFetch %v4float %im %vi12",
3268          "",
3269          {"vi12"}},
3270         {"%float 2D 0 0 0 2 R32f",
3271          "%result = OpImageRead %v4float %im %vi12",
3272          "",
3273          {"vi12"}},
3274         {"%float 2D 0 0 0 2 R32f",
3275          "OpImageWrite %im %vi12 %vf1234",
3276          "",
3277          {"vi12"}}}));
3278 
3279 INSTANTIATE_TEST_SUITE_P(
3280     PreserveIntCoords_Arrayed,
3281     // In SPIR-V, image read, fetch, and write use integer coordinates.
3282     // Prove that we preserve signed integer coordinates.
3283     SpvParserHandleTest_ImageCoordsTest,
3284     ::testing::ValuesIn(std::vector<ImageCoordsCase>{
3285         {"%float 2D 0 1 0 1 Unknown",
3286          "%result = OpImageFetch %v4float %im %vi123",
3287          "",
3288          {"vi123.xy", "vi123.z"}},
3289         {"%float 2D 0 1 0 2 R32f",
3290          "%result = OpImageRead %v4float %im %vi123",
3291          "",
3292          {"vi123.xy", "vi123.z"}},
3293         {"%float 2D 0 1 0 2 R32f",
3294          "OpImageWrite %im %vi123 %vf1234",
3295          "",
3296          {"vi123.xy", "vi123.z"}}}));
3297 
3298 INSTANTIATE_TEST_SUITE_P(
3299     ConvertUintCoords_NonArrayed,
3300     // In SPIR-V, image read, fetch, and write use integer coordinates.
3301     // Prove that we convert unsigned integer coordinates to signed.
3302     SpvParserHandleTest_ImageCoordsTest,
3303     ::testing::ValuesIn(std::vector<ImageCoordsCase>{
3304         // Scalar cases
3305         {"%float 1D 0 0 0 1 Unknown",
3306          "%result = OpImageFetch %v4float %im %u1",
3307          "",
3308          {"i32(u1)"}},
3309         {"%float 1D 0 0 0 2 R32f",
3310          "%result = OpImageRead %v4float %im %u1",
3311          "",
3312          {"i32(u1)"}},
3313         {"%float 1D 0 0 0 2 R32f",
3314          "OpImageWrite %im %u1 %vf1234",
3315          "",
3316          {"i32(u1)"}},
3317         // Vector cases
3318         {"%float 2D 0 0 0 1 Unknown",
3319          "%result = OpImageFetch %v4float %im %vu12",
3320          "",
3321          {"vec2<i32>(vu12)"}},
3322         {"%float 2D 0 0 0 2 R32f",
3323          "%result = OpImageRead %v4float %im %vu12",
3324          "",
3325          {"vec2<i32>(vu12)"}},
3326         {"%float 2D 0 0 0 2 R32f",
3327          "OpImageWrite %im %vu12 %vf1234",
3328          "",
3329          {"vec2<i32>(vu12)"}}}));
3330 
3331 INSTANTIATE_TEST_SUITE_P(
3332     ConvertUintCoords_Arrayed,
3333     // In SPIR-V, image read, fetch, and write use integer coordinates.
3334     // Prove that we convert unsigned integer coordinates to signed.
3335     SpvParserHandleTest_ImageCoordsTest,
3336     ::testing::ValuesIn(std::vector<ImageCoordsCase>{
3337         {"%float 2D 0 1 0 1 Unknown",
3338          "%result = OpImageFetch %v4float %im %vu123",
3339          "",
3340          {"vec2<i32>(vu123.xy)", "i32(vu123.z)"}},
3341         {"%float 2D 0 1 0 2 R32f",
3342          "%result = OpImageRead %v4float %im %vu123",
3343          "",
3344          {"vec2<i32>(vu123.xy)", "i32(vu123.z)"}},
3345         {"%float 2D 0 1 0 2 R32f",
3346          "OpImageWrite %im %vu123 %vf1234",
3347          "",
3348          {"vec2<i32>(vu123.xy)", "i32(vu123.z)"}}}));
3349 
3350 INSTANTIATE_TEST_SUITE_P(
3351     BadInstructions,
3352     SpvParserHandleTest_ImageCoordsTest,
3353     ::testing::ValuesIn(std::vector<ImageCoordsCase>{
3354         {"%float 1D 0 0 0 1 Unknown",
3355          "OpNop",
3356          "not an image access instruction: OpNop",
3357          {}},
3358         {"%float 1D 0 0 0 1 Unknown",
3359          "%50 = OpCopyObject %float %float_1",
3360          "internal error: couldn't find image for "
3361          "%50 = OpCopyObject %18 %45",
3362          {}},
3363         {"%float 1D 0 0 0 1 Unknown",
3364          "OpStore %float_var %float_1",
3365          "invalid type for image or sampler "
3366          "variable or function parameter: %1 = OpVariable %2 Function",
3367          {}},
3368         // An example with a missing coordinate
3369         // won't assemble, so we skip it.
3370     }));
3371 
3372 INSTANTIATE_TEST_SUITE_P(
3373     Bad_Coordinate,
3374     SpvParserHandleTest_ImageCoordsTest,
3375     ::testing::ValuesIn(std::vector<ImageCoordsCase>{
3376         {"%float 1D 0 0 0 1 Unknown",
3377          "%result = OpImageSampleImplicitLod "
3378          // bad type for coordinate: not a number
3379          "%v4float %sampled_image %float_var",
3380          "bad or unsupported coordinate type for image access: %73 = "
3381          "OpImageSampleImplicitLod %42 %72 %1",
3382          {}},
3383         {"%float 2D 0 0 0 1 Unknown",  // 2D
3384          "%result = OpImageSampleImplicitLod "
3385          // 1 component, but need 2
3386          "%v4float %sampled_image %f1",
3387          "image access required 2 coordinate components, but only 1 provided, "
3388          "in: %73 = OpImageSampleImplicitLod %42 %72 %12",
3389          {}},
3390         {"%float 2D 0 1 0 1 Unknown",  // 2DArray
3391          "%result = OpImageSampleImplicitLod "
3392          // 2 component, but need 3
3393          "%v4float %sampled_image %vf12",
3394          "image access required 3 coordinate components, but only 2 provided, "
3395          "in: %73 = OpImageSampleImplicitLod %42 %72 %13",
3396          {}},
3397         {"%float 3D 0 0 0 1 Unknown",  // 3D
3398          "%result = OpImageSampleImplicitLod "
3399          // 2 components, but need 3
3400          "%v4float %sampled_image %vf12",
3401          "image access required 3 coordinate components, but only 2 provided, "
3402          "in: %73 = OpImageSampleImplicitLod %42 %72 %13",
3403          {}},
3404     }));
3405 
3406 INSTANTIATE_TEST_SUITE_P(
3407     SampleNonFloatTexture_IsError,
3408     SpvParserHandleTest_ImageCoordsTest,
3409     ::testing::ValuesIn(std::vector<ImageCoordsCase>{
3410         // ImageSampleImplicitLod
3411         {"%uint 2D 0 0 0 1 Unknown",
3412          "%result = OpImageSampleImplicitLod %v4uint %sampled_image %vf12",
3413          "sampled image must have float component type",
3414          {}},
3415         {"%int 2D 0 0 0 1 Unknown",
3416          "%result = OpImageSampleImplicitLod %v4int %sampled_image %vf12",
3417          "sampled image must have float component type",
3418          {}},
3419         // ImageSampleExplicitLod
3420         {"%uint 2D 0 0 0 1 Unknown",
3421          "%result = OpImageSampleExplicitLod %v4uint %sampled_image %vf12 "
3422          "Lod %f1",
3423          "sampled image must have float component type",
3424          {}},
3425         {"%int 2D 0 0 0 1 Unknown",
3426          "%result = OpImageSampleExplicitLod %v4int %sampled_image %vf12 "
3427          "Lod %f1",
3428          "sampled image must have float component type",
3429          {}},
3430         // ImageSampleDrefImplicitLod
3431         {"%uint 2D 0 0 0 1 Unknown",
3432          "%result = OpImageSampleDrefImplicitLod %uint %sampled_image %vf12 "
3433          "%f1",
3434          "sampled image must have float component type",
3435          {}},
3436         {"%int 2D 0 0 0 1 Unknown",
3437          "%result = OpImageSampleDrefImplicitLod %int %sampled_image %vf12 "
3438          "%f1",
3439          "sampled image must have float component type",
3440          {}},
3441         // ImageSampleDrefExplicitLod
3442         {"%uint 2D 0 0 0 1 Unknown",
3443          "%result = OpImageSampleDrefExplicitLod %uint %sampled_image %vf12 "
3444          "%f1 Lod %float_0",
3445          "sampled image must have float component type",
3446          {}},
3447         {"%int 2D 0 0 0 1 Unknown",
3448          "%result = OpImageSampleDrefExplicitLod %int %sampled_image %vf12 "
3449          "%f1 Lod %float_0",
3450          "sampled image must have float component type",
3451          {}}}));
3452 
3453 INSTANTIATE_TEST_SUITE_P(
3454     ConstOffset_BadInstruction_Errors,
3455     SpvParserHandleTest_ImageCoordsTest,
3456     ::testing::ValuesIn(std::vector<ImageCoordsCase>{
3457         // ImageFetch
3458         {"%uint 2D 0 0 0 1 Unknown",
3459          "%result = OpImageFetch %v4uint %sampled_image %vf12 ConstOffset "
3460          "%the_vu12",
3461          "ConstOffset is only permitted for sampling operations: ",
3462          {}},
3463         // ImageRead
3464         {"%uint 2D 0 0 0 2 Rgba32ui",
3465          "%result = OpImageRead %v4uint %im %vu12 ConstOffset %the_vu12",
3466          "ConstOffset is only permitted for sampling operations: ",
3467          {}},
3468         // ImageWrite
3469         {"%uint 2D 0 0 0 2 Rgba32ui",
3470          "OpImageWrite %im %vu12 %vu1234 ConstOffset %the_vu12",
3471          "ConstOffset is only permitted for sampling operations: ",
3472          {}}
3473         // TODO(dneto): Gather
3474         // TODO(dneto): DrefGather
3475     }));
3476 
3477 INSTANTIATE_TEST_SUITE_P(
3478     ConstOffset_BadDim_Errors,
3479     SpvParserHandleTest_ImageCoordsTest,
3480     ::testing::ValuesIn(std::vector<ImageCoordsCase>{
3481         // 1D
3482         {"%uint 1D 0 0 0 1 Unknown",
3483          "%result = OpImageSampleImplicitLod %v4float %sampled_image %vf1234 "
3484          "ConstOffset %the_vu12",
3485          "ConstOffset is only permitted for 2D, 2D Arrayed, and 3D textures: ",
3486          {}},
3487         // Cube
3488         {"%uint Cube 0 0 0 1 Unknown",
3489          "%result = OpImageSampleImplicitLod %v4float %sampled_image %vf1234 "
3490          "ConstOffset %the_vu12",
3491          "ConstOffset is only permitted for 2D, 2D Arrayed, and 3D textures: ",
3492          {}},
3493         // Cube Array
3494         {"%uint Cube 0 1 0 1 Unknown",
3495          "%result = OpImageSampleImplicitLod %v4float %sampled_image %vf1234 "
3496          "ConstOffset %the_vu12",
3497          "ConstOffset is only permitted for 2D, 2D Arrayed, and 3D textures: ",
3498          {}}}));
3499 
3500 INSTANTIATE_TEST_SUITE_P(
3501     ImageSampleDref_Bias_IsError,
3502     SpvParserHandleTest_ImageCoordsTest,
3503     ::testing::ValuesIn(std::vector<ImageCoordsCase>{
3504         // Implicit Lod
3505         {"%float 2D 1 0 0 1 Unknown",
3506          "%result = OpImageSampleDrefImplicitLod %float %sampled_image %vf1234 "
3507          "%depth Bias %float_null",
3508          "WGSL does not support depth-reference sampling with level-of-detail "
3509          "bias: ",
3510          {}},
3511         // Explicit Lod
3512         {"%float 2D 1 0 0 1 Unknown",
3513          "%result = OpImageSampleDrefExplicitLod %float %sampled_image %vf1234 "
3514          "%depth Lod|Bias %float_null %float_null",
3515          "WGSL does not support depth-reference sampling with level-of-detail "
3516          "bias: ",
3517          {}}}));
3518 
3519 INSTANTIATE_TEST_SUITE_P(
3520     ImageSampleDref_Grad_IsError,
3521     SpvParserHandleTest_ImageCoordsTest,
3522     ::testing::ValuesIn(std::vector<ImageCoordsCase>{
3523         // Implicit Lod
3524         {"%float 2D 1 0 0 1 Unknown",
3525          "%result = OpImageSampleDrefImplicitLod %float %sampled_image %vf1234 "
3526          "%depth Grad %float_1 %float_2",
3527          "WGSL does not support depth-reference sampling with explicit "
3528          "gradient: ",
3529          {}},
3530         // Explicit Lod
3531         {"%float 2D 1 0 0 1 Unknown",
3532          "%result = OpImageSampleDrefExplicitLod %float %sampled_image %vf1234 "
3533          "%depth Lod|Grad %float_null %float_1  %float_2",
3534          "WGSL does not support depth-reference sampling with explicit "
3535          "gradient: ",
3536          {}}}));
3537 
3538 INSTANTIATE_TEST_SUITE_P(
3539     ImageSampleDrefExplicitLod_CheckForLod0,
3540     // Metal requires comparison sampling with explicit Level-of-detail to use
3541     // Lod 0.  The SPIR-V reader requires the operand to be parsed as a constant
3542     // 0 value. SPIR-V validation requires the Lod parameter to be a floating
3543     // point value for non-fetch operations. So only test float values.
3544     SpvParserHandleTest_ImageCoordsTest,
3545     ::testing::ValuesIn(std::vector<ImageCoordsCase>{
3546         // float 0.0 works
3547         {"%float 2D 1 0 0 1 Unknown",
3548          "%result = OpImageSampleDrefExplicitLod %float %sampled_image %vf1234 "
3549          "%depth Lod %float_0",
3550          "",
3551          {"vf1234.xy"}},
3552         // float null works
3553         {"%float 2D 1 0 0 1 Unknown",
3554          "%result = OpImageSampleDrefExplicitLod %float %sampled_image %vf1234 "
3555          "%depth Lod %float_0",
3556          "",
3557          {"vf1234.xy"}},
3558         // float 1.0 fails.
3559         {"%float 2D 1 0 0 1 Unknown",
3560          "%result = OpImageSampleDrefExplicitLod %float %sampled_image %vf1234 "
3561          "%depth Lod %float_1",
3562          "WGSL comparison sampling without derivatives requires "
3563          "level-of-detail "
3564          "0.0",
3565          {}}}));
3566 
3567 INSTANTIATE_TEST_SUITE_P(
3568     ImageSampleProjDrefExplicitLod_CheckForLod0,
3569     // This is like the previous test, but for Projection sampling.
3570     //
3571     // Metal requires comparison sampling with explicit Level-of-detail to use
3572     // Lod 0.  The SPIR-V reader requires the operand to be parsed as a constant
3573     // 0 value. SPIR-V validation requires the Lod parameter to be a floating
3574     // point value for non-fetch operations. So only test float values.
3575     SpvParserHandleTest_ImageCoordsTest,
3576     ::testing::ValuesIn(std::vector<ImageCoordsCase>{
3577         // float 0.0 works
3578         {"%float 2D 1 0 0 1 Unknown",
3579          "%result = OpImageSampleProjDrefExplicitLod %float %sampled_image "
3580          "%vf1234 %depth Lod %float_0",
3581          "",
3582          {"(vf1234.xy / vf1234.z)"}},
3583         // float null works
3584         {"%float 2D 1 0 0 1 Unknown",
3585          "%result = OpImageSampleProjDrefExplicitLod %float %sampled_image "
3586          "%vf1234 %depth Lod %float_0",
3587          "",
3588          {"(vf1234.xy / vf1234.z)"}},
3589         // float 1.0 fails.
3590         {"%float 2D 1 0 0 1 Unknown",
3591          "%result = OpImageSampleProjDrefExplicitLod %float %sampled_image "
3592          "%vf1234 %depth Lod %float_1",
3593          "WGSL comparison sampling without derivatives requires "
3594          "level-of-detail "
3595          "0.0",
3596          {}}}));
3597 
TEST_F(SpvParserHandleTest,CombinedImageSampler_IsError)3598 TEST_F(SpvParserHandleTest, CombinedImageSampler_IsError) {
3599   const auto assembly = Preamble() + R"(
3600      OpEntryPoint Fragment %100 "main"
3601      OpExecutionMode %100 OriginUpperLeft
3602 
3603      OpDecorate %var DescriptorSet 0
3604      OpDecorate %var Binding 0
3605   %float = OpTypeFloat 32
3606      %im = OpTypeImage %float 2D 0 0 0 1 Unknown
3607      %si = OpTypeSampledImage %im
3608  %ptr_si = OpTypePointer UniformConstant %si
3609     %var = OpVariable %ptr_si UniformConstant
3610    %void = OpTypeVoid
3611  %voidfn = OpTypeFunction %void
3612 
3613     %100 = OpFunction %void None %voidfn
3614   %entry = OpLabel
3615            OpReturn
3616            OpFunctionEnd
3617   )";
3618   auto p = parser(test::Assemble(assembly));
3619   EXPECT_FALSE(p->BuildAndParseInternalModule()) << assembly;
3620   EXPECT_THAT(p->error(),
3621               HasSubstr("WGSL does not support combined image-samplers: "));
3622 }
3623 
3624 INSTANTIATE_TEST_SUITE_P(
3625     ImageQueryLod_IsError,
3626     SpvParserHandleTest_ImageCoordsTest,
3627     ::testing::ValuesIn(std::vector<ImageCoordsCase>{
3628         {"%float 2D 0 0 0 1 Unknown",
3629          "%result = OpImageQueryLod %v2int %sampled_image %vf12",
3630          "WGSL does not support querying the level of detail of an image: ",
3631          {}}}));
3632 
TEST_F(SpvParserHandleTest,NeverGenerateConstDeclForHandle_UseVariableDirectly)3633 TEST_F(SpvParserHandleTest,
3634        NeverGenerateConstDeclForHandle_UseVariableDirectly) {
3635   // An ad-hoc test to prove we never had the issue
3636   // feared in crbug.com/tint/265.
3637   // Never create a const-declaration for a pointer to
3638   // a texture or sampler. Code generation always
3639   // traces back to the memory object declaration.
3640   const auto assembly = Preamble() + R"(
3641      OpEntryPoint Fragment %100 "main"
3642      OpExecutionMode %100 OriginUpperLeft
3643 
3644      OpName %var "var"
3645      OpDecorate %var_im DescriptorSet 0
3646      OpDecorate %var_im Binding 0
3647      OpDecorate %var_s DescriptorSet 0
3648      OpDecorate %var_s Binding 1
3649   %float = OpTypeFloat 32
3650   %v4float = OpTypeVector %float 4
3651   %v2float = OpTypeVector %float 2
3652   %v2_0 = OpConstantNull %v2float
3653      %im = OpTypeImage %float 2D 0 0 0 1 Unknown
3654      %si = OpTypeSampledImage %im
3655       %s = OpTypeSampler
3656  %ptr_im = OpTypePointer UniformConstant %im
3657   %ptr_s = OpTypePointer UniformConstant %s
3658  %var_im = OpVariable %ptr_im UniformConstant
3659   %var_s = OpVariable %ptr_s UniformConstant
3660    %void = OpTypeVoid
3661  %voidfn = OpTypeFunction %void
3662  %ptr_v4 = OpTypePointer Function %v4float
3663 
3664     %100 = OpFunction %void None %voidfn
3665   %entry = OpLabel
3666     %var = OpVariable %ptr_v4 Function
3667 
3668 ; Try to induce generating a const-declaration of a pointer to
3669 ; a sampler or texture.
3670 
3671  %var_im_copy = OpCopyObject %ptr_im %var_im
3672   %var_s_copy = OpCopyObject %ptr_s %var_s
3673 
3674          %im0 = OpLoad %im %var_im_copy
3675           %s0 = OpLoad %s %var_s_copy
3676          %si0 = OpSampledImage %si %im0 %s0
3677           %t0 = OpImageSampleImplicitLod %v4float %si0 %v2_0
3678 
3679 
3680          %im1 = OpLoad %im %var_im_copy
3681           %s1 = OpLoad %s %var_s_copy
3682          %si1 = OpSampledImage %si %im1 %s1
3683           %t1 = OpImageSampleImplicitLod %v4float %si1 %v2_0
3684 
3685          %sum = OpFAdd %v4float %t0 %t1
3686            OpStore %var %sum
3687 
3688            OpReturn
3689            OpFunctionEnd
3690   )";
3691   auto p = parser(test::Assemble(assembly));
3692   EXPECT_TRUE(p->BuildAndParseInternalModule()) << assembly;
3693   auto fe = p->function_emitter(100);
3694   EXPECT_TRUE(fe.EmitBody()) << p->error();
3695   EXPECT_TRUE(p->error().empty()) << p->error();
3696   auto ast_body = fe.ast_body();
3697   const auto got = test::ToString(p->program(), ast_body);
3698   auto* expect = R"(var var_1 : vec4<f32>;
3699 let x_22 : vec4<f32> = textureSample(x_2, x_3, vec2<f32>(0.0, 0.0));
3700 let x_26 : vec4<f32> = textureSample(x_2, x_3, vec2<f32>(0.0, 0.0));
3701 var_1 = (x_22 + x_26);
3702 return;
3703 )";
3704   ASSERT_EQ(expect, got);
3705 }
3706 
3707 }  // namespace
3708 }  // namespace spirv
3709 }  // namespace reader
3710 }  // namespace tint
3711