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