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 "src/ast/override_decoration.h"
16 #include "src/ast/stage_decoration.h"
17 #include "src/ast/struct_block_decoration.h"
18 #include "src/writer/spirv/spv_dump.h"
19 #include "src/writer/spirv/test_helper.h"
20
21 namespace tint {
22 namespace writer {
23 namespace spirv {
24 namespace {
25
26 using BuilderTest = TestHelper;
27
TEST_F(BuilderTest,GlobalVar_WithStorageClass)28 TEST_F(BuilderTest, GlobalVar_WithStorageClass) {
29 auto* v = Global("var", ty.f32(), ast::StorageClass::kPrivate);
30
31 spirv::Builder& b = Build();
32
33 EXPECT_TRUE(b.GenerateGlobalVariable(v)) << b.error();
34 EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %1 "var"
35 )");
36 EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32
37 %2 = OpTypePointer Private %3
38 %4 = OpConstantNull %3
39 %1 = OpVariable %2 Private %4
40 )");
41 }
42
TEST_F(BuilderTest,GlobalVar_WithConstructor)43 TEST_F(BuilderTest, GlobalVar_WithConstructor) {
44 auto* init = vec3<f32>(1.f, 1.f, 3.f);
45
46 auto* v = Global("var", ty.vec3<f32>(), ast::StorageClass::kPrivate, init);
47
48 spirv::Builder& b = Build();
49
50 EXPECT_TRUE(b.GenerateGlobalVariable(v)) << b.error();
51 ASSERT_FALSE(b.has_error()) << b.error();
52
53 EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %6 "var"
54 )");
55 EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
56 %1 = OpTypeVector %2 3
57 %3 = OpConstant %2 1
58 %4 = OpConstant %2 3
59 %5 = OpConstantComposite %1 %3 %3 %4
60 %7 = OpTypePointer Private %1
61 %6 = OpVariable %7 Private %5
62 )");
63 }
64
TEST_F(BuilderTest,GlobalVar_Const)65 TEST_F(BuilderTest, GlobalVar_Const) {
66 auto* init = vec3<f32>(1.f, 1.f, 3.f);
67
68 auto* v = GlobalConst("var", ty.vec3<f32>(), init);
69
70 spirv::Builder& b = Build();
71
72 EXPECT_TRUE(b.GenerateGlobalVariable(v)) << b.error();
73 ASSERT_FALSE(b.has_error()) << b.error();
74
75 EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %5 "var"
76 )");
77 EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
78 %1 = OpTypeVector %2 3
79 %3 = OpConstant %2 1
80 %4 = OpConstant %2 3
81 %5 = OpConstantComposite %1 %3 %3 %4
82 )");
83 }
84
TEST_F(BuilderTest,GlobalVar_Complex_Constructor)85 TEST_F(BuilderTest, GlobalVar_Complex_Constructor) {
86 auto* init = vec3<f32>(ast::ExpressionList{Expr(1.f), Expr(2.f), Expr(3.f)});
87
88 auto* v = GlobalConst("var", ty.vec3<f32>(), init);
89
90 spirv::Builder& b = Build();
91
92 EXPECT_TRUE(b.GenerateGlobalVariable(v)) << b.error();
93 ASSERT_FALSE(b.has_error()) << b.error();
94
95 EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
96 %1 = OpTypeVector %2 3
97 %3 = OpConstant %2 1
98 %4 = OpConstant %2 2
99 %5 = OpConstant %2 3
100 %6 = OpConstantComposite %1 %3 %4 %5
101 )");
102 }
103
TEST_F(BuilderTest,GlobalVar_Complex_ConstructorWithExtract)104 TEST_F(BuilderTest, GlobalVar_Complex_ConstructorWithExtract) {
105 auto* init = vec3<f32>(vec2<f32>(1.f, 2.f), 3.f);
106
107 auto* v = GlobalConst("var", ty.vec3<f32>(), init);
108
109 spirv::Builder& b = Build();
110
111 EXPECT_TRUE(b.GenerateGlobalVariable(v)) << b.error();
112 ASSERT_FALSE(b.has_error()) << b.error();
113
114 EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
115 %1 = OpTypeVector %2 3
116 %3 = OpTypeVector %2 2
117 %4 = OpConstant %2 1
118 %5 = OpConstant %2 2
119 %6 = OpConstantComposite %3 %4 %5
120 %8 = OpTypeInt 32 0
121 %9 = OpConstant %8 0
122 %7 = OpSpecConstantOp %2 CompositeExtract %6 9
123 %11 = OpConstant %8 1
124 %10 = OpSpecConstantOp %2 CompositeExtract %6 11
125 %12 = OpConstant %2 3
126 %13 = OpSpecConstantComposite %1 %7 %10 %12
127 )");
128 }
129
TEST_F(BuilderTest,GlobalVar_WithBindingAndGroup)130 TEST_F(BuilderTest, GlobalVar_WithBindingAndGroup) {
131 auto* v = Global("var", ty.sampler(ast::SamplerKind::kSampler),
132 ast::StorageClass::kNone, nullptr,
133 ast::DecorationList{
134 create<ast::BindingDecoration>(2),
135 create<ast::GroupDecoration>(3),
136 });
137
138 spirv::Builder& b = Build();
139
140 EXPECT_TRUE(b.GenerateGlobalVariable(v)) << b.error();
141 EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %1 "var"
142 )");
143 EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %1 Binding 2
144 OpDecorate %1 DescriptorSet 3
145 )");
146 EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeSampler
147 %2 = OpTypePointer UniformConstant %3
148 %1 = OpVariable %2 UniformConstant
149 )");
150 }
151
TEST_F(BuilderTest,GlobalVar_Override_Bool)152 TEST_F(BuilderTest, GlobalVar_Override_Bool) {
153 auto* v = GlobalConst("var", ty.bool_(), Expr(true),
154 ast::DecorationList{
155 create<ast::OverrideDecoration>(1200),
156 });
157
158 spirv::Builder& b = Build();
159
160 EXPECT_TRUE(b.GenerateGlobalVariable(v)) << b.error();
161 EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %2 "var"
162 )");
163 EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %2 SpecId 1200
164 )");
165 EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeBool
166 %2 = OpSpecConstantTrue %1
167 )");
168 }
169
TEST_F(BuilderTest,GlobalVar_Override_Bool_ZeroValue)170 TEST_F(BuilderTest, GlobalVar_Override_Bool_ZeroValue) {
171 auto* v = GlobalConst("var", ty.bool_(), Construct<bool>(),
172 ast::DecorationList{
173 create<ast::OverrideDecoration>(1200),
174 });
175
176 spirv::Builder& b = Build();
177
178 EXPECT_TRUE(b.GenerateGlobalVariable(v)) << b.error();
179 EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %2 "var"
180 )");
181 EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %2 SpecId 1200
182 )");
183 EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeBool
184 %2 = OpSpecConstantFalse %1
185 )");
186 }
187
TEST_F(BuilderTest,GlobalVar_Override_Bool_NoConstructor)188 TEST_F(BuilderTest, GlobalVar_Override_Bool_NoConstructor) {
189 auto* v = GlobalConst("var", ty.bool_(), nullptr,
190 ast::DecorationList{
191 create<ast::OverrideDecoration>(1200),
192 });
193
194 spirv::Builder& b = Build();
195
196 EXPECT_TRUE(b.GenerateGlobalVariable(v)) << b.error();
197 EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %2 "var"
198 )");
199 EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %2 SpecId 1200
200 )");
201 EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeBool
202 %2 = OpSpecConstantFalse %1
203 )");
204 }
205
TEST_F(BuilderTest,GlobalVar_Override_Scalar)206 TEST_F(BuilderTest, GlobalVar_Override_Scalar) {
207 auto* v = GlobalConst("var", ty.f32(), Expr(2.f),
208 ast::DecorationList{
209 create<ast::OverrideDecoration>(0),
210 });
211
212 spirv::Builder& b = Build();
213
214 EXPECT_TRUE(b.GenerateGlobalVariable(v)) << b.error();
215 EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %2 "var"
216 )");
217 EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %2 SpecId 0
218 )");
219 EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeFloat 32
220 %2 = OpSpecConstant %1 2
221 )");
222 }
223
TEST_F(BuilderTest,GlobalVar_Override_Scalar_ZeroValue)224 TEST_F(BuilderTest, GlobalVar_Override_Scalar_ZeroValue) {
225 auto* v = GlobalConst("var", ty.f32(), Construct<f32>(),
226 ast::DecorationList{
227 create<ast::OverrideDecoration>(0),
228 });
229
230 spirv::Builder& b = Build();
231
232 EXPECT_TRUE(b.GenerateGlobalVariable(v)) << b.error();
233 EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %2 "var"
234 )");
235 EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %2 SpecId 0
236 )");
237 EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeFloat 32
238 %2 = OpSpecConstant %1 0
239 )");
240 }
241
TEST_F(BuilderTest,GlobalVar_Override_Scalar_F32_NoConstructor)242 TEST_F(BuilderTest, GlobalVar_Override_Scalar_F32_NoConstructor) {
243 auto* v = GlobalConst("var", ty.f32(), nullptr,
244 ast::DecorationList{
245 create<ast::OverrideDecoration>(0),
246 });
247
248 spirv::Builder& b = Build();
249
250 EXPECT_TRUE(b.GenerateGlobalVariable(v)) << b.error();
251 EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %2 "var"
252 )");
253 EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %2 SpecId 0
254 )");
255 EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeFloat 32
256 %2 = OpSpecConstant %1 0
257 )");
258 }
259
TEST_F(BuilderTest,GlobalVar_Override_Scalar_I32_NoConstructor)260 TEST_F(BuilderTest, GlobalVar_Override_Scalar_I32_NoConstructor) {
261 auto* v = GlobalConst("var", ty.i32(), nullptr,
262 ast::DecorationList{
263 create<ast::OverrideDecoration>(0),
264 });
265
266 spirv::Builder& b = Build();
267
268 EXPECT_TRUE(b.GenerateGlobalVariable(v)) << b.error();
269 EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %2 "var"
270 )");
271 EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %2 SpecId 0
272 )");
273 EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeInt 32 1
274 %2 = OpSpecConstant %1 0
275 )");
276 }
277
TEST_F(BuilderTest,GlobalVar_Override_Scalar_U32_NoConstructor)278 TEST_F(BuilderTest, GlobalVar_Override_Scalar_U32_NoConstructor) {
279 auto* v = GlobalConst("var", ty.u32(), nullptr,
280 ast::DecorationList{
281 create<ast::OverrideDecoration>(0),
282 });
283
284 spirv::Builder& b = Build();
285
286 EXPECT_TRUE(b.GenerateGlobalVariable(v)) << b.error();
287 EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %2 "var"
288 )");
289 EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %2 SpecId 0
290 )");
291 EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeInt 32 0
292 %2 = OpSpecConstant %1 0
293 )");
294 }
295
TEST_F(BuilderTest,GlobalVar_Override_NoId)296 TEST_F(BuilderTest, GlobalVar_Override_NoId) {
297 auto* var_a = GlobalConst("a", ty.bool_(), Expr(true),
298 ast::DecorationList{
299 create<ast::OverrideDecoration>(0),
300 });
301 auto* var_b = GlobalConst("b", ty.bool_(), Expr(false),
302 ast::DecorationList{
303 create<ast::OverrideDecoration>(),
304 });
305
306 spirv::Builder& b = Build();
307
308 EXPECT_TRUE(b.GenerateGlobalVariable(var_a)) << b.error();
309 EXPECT_TRUE(b.GenerateGlobalVariable(var_b)) << b.error();
310 EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %2 "a"
311 OpName %3 "b"
312 )");
313 EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %2 SpecId 0
314 OpDecorate %3 SpecId 1
315 )");
316 EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeBool
317 %2 = OpSpecConstantTrue %1
318 %3 = OpSpecConstantFalse %1
319 )");
320 }
321
322 struct BuiltinData {
323 ast::Builtin builtin;
324 ast::StorageClass storage;
325 SpvBuiltIn result;
326 };
operator <<(std::ostream & out,BuiltinData data)327 inline std::ostream& operator<<(std::ostream& out, BuiltinData data) {
328 out << data.builtin;
329 return out;
330 }
331 using BuiltinDataTest = TestParamHelper<BuiltinData>;
TEST_P(BuiltinDataTest,Convert)332 TEST_P(BuiltinDataTest, Convert) {
333 auto params = GetParam();
334
335 spirv::Builder& b = Build();
336
337 EXPECT_EQ(b.ConvertBuiltin(params.builtin, params.storage), params.result);
338 }
339 INSTANTIATE_TEST_SUITE_P(
340 BuilderTest_Type,
341 BuiltinDataTest,
342 testing::Values(
343 BuiltinData{ast::Builtin::kNone, ast::StorageClass::kNone,
344 SpvBuiltInMax},
345 BuiltinData{ast::Builtin::kPosition, ast::StorageClass::kInput,
346 SpvBuiltInFragCoord},
347 BuiltinData{ast::Builtin::kPosition, ast::StorageClass::kOutput,
348 SpvBuiltInPosition},
349 BuiltinData{
350 ast::Builtin::kVertexIndex,
351 ast::StorageClass::kInput,
352 SpvBuiltInVertexIndex,
353 },
354 BuiltinData{ast::Builtin::kInstanceIndex, ast::StorageClass::kInput,
355 SpvBuiltInInstanceIndex},
356 BuiltinData{ast::Builtin::kFrontFacing, ast::StorageClass::kInput,
357 SpvBuiltInFrontFacing},
358 BuiltinData{ast::Builtin::kFragDepth, ast::StorageClass::kOutput,
359 SpvBuiltInFragDepth},
360 BuiltinData{ast::Builtin::kLocalInvocationId, ast::StorageClass::kInput,
361 SpvBuiltInLocalInvocationId},
362 BuiltinData{ast::Builtin::kLocalInvocationIndex,
363 ast::StorageClass::kInput, SpvBuiltInLocalInvocationIndex},
364 BuiltinData{ast::Builtin::kGlobalInvocationId,
365 ast::StorageClass::kInput, SpvBuiltInGlobalInvocationId},
366 BuiltinData{ast::Builtin::kWorkgroupId, ast::StorageClass::kInput,
367 SpvBuiltInWorkgroupId},
368 BuiltinData{ast::Builtin::kNumWorkgroups, ast::StorageClass::kInput,
369 SpvBuiltInNumWorkgroups},
370 BuiltinData{ast::Builtin::kSampleIndex, ast::StorageClass::kInput,
371 SpvBuiltInSampleId},
372 BuiltinData{ast::Builtin::kSampleMask, ast::StorageClass::kInput,
373 SpvBuiltInSampleMask},
374 BuiltinData{ast::Builtin::kSampleMask, ast::StorageClass::kOutput,
375 SpvBuiltInSampleMask}));
376
TEST_F(BuilderTest,GlobalVar_DeclReadOnly)377 TEST_F(BuilderTest, GlobalVar_DeclReadOnly) {
378 // struct A {
379 // a : i32;
380 // };
381 // var b<storage, read> : A
382
383 auto* A = Structure("A",
384 {
385 Member("a", ty.i32()),
386 Member("b", ty.i32()),
387 },
388 {create<ast::StructBlockDecoration>()});
389
390 auto* var =
391 Global("b", ty.Of(A), ast::StorageClass::kStorage, ast::Access::kRead,
392 ast::DecorationList{
393 create<ast::BindingDecoration>(0),
394 create<ast::GroupDecoration>(0),
395 });
396
397 spirv::Builder& b = Build();
398
399 EXPECT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
400
401 EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %3 Block
402 OpMemberDecorate %3 0 Offset 0
403 OpMemberDecorate %3 1 Offset 4
404 OpDecorate %1 NonWritable
405 OpDecorate %1 Binding 0
406 OpDecorate %1 DescriptorSet 0
407 )");
408 EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %3 "A"
409 OpMemberName %3 0 "a"
410 OpMemberName %3 1 "b"
411 OpName %1 "b"
412 )");
413 EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeInt 32 1
414 %3 = OpTypeStruct %4 %4
415 %2 = OpTypePointer StorageBuffer %3
416 %1 = OpVariable %2 StorageBuffer
417 )");
418 }
419
TEST_F(BuilderTest,GlobalVar_TypeAliasDeclReadOnly)420 TEST_F(BuilderTest, GlobalVar_TypeAliasDeclReadOnly) {
421 // struct A {
422 // a : i32;
423 // };
424 // type B = A;
425 // var b<storage, read> : B
426
427 auto* A = Structure("A", {Member("a", ty.i32())},
428 {create<ast::StructBlockDecoration>()});
429 auto* B = Alias("B", ty.Of(A));
430 auto* var =
431 Global("b", ty.Of(B), ast::StorageClass::kStorage, ast::Access::kRead,
432 ast::DecorationList{
433 create<ast::BindingDecoration>(0),
434 create<ast::GroupDecoration>(0),
435 });
436
437 spirv::Builder& b = Build();
438
439 EXPECT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
440
441 EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %3 Block
442 OpMemberDecorate %3 0 Offset 0
443 OpDecorate %1 NonWritable
444 OpDecorate %1 Binding 0
445 OpDecorate %1 DescriptorSet 0
446 )");
447 EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %3 "A"
448 OpMemberName %3 0 "a"
449 OpName %1 "b"
450 )");
451 EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeInt 32 1
452 %3 = OpTypeStruct %4
453 %2 = OpTypePointer StorageBuffer %3
454 %1 = OpVariable %2 StorageBuffer
455 )");
456 }
457
TEST_F(BuilderTest,GlobalVar_TypeAliasAssignReadOnly)458 TEST_F(BuilderTest, GlobalVar_TypeAliasAssignReadOnly) {
459 // struct A {
460 // a : i32;
461 // };
462 // type B = A;
463 // var<storage, read> b : B
464
465 auto* A = Structure("A", {Member("a", ty.i32())},
466 {create<ast::StructBlockDecoration>()});
467 auto* B = Alias("B", ty.Of(A));
468 auto* var =
469 Global("b", ty.Of(B), ast::StorageClass::kStorage, ast::Access::kRead,
470 ast::DecorationList{
471 create<ast::BindingDecoration>(0),
472 create<ast::GroupDecoration>(0),
473 });
474
475 spirv::Builder& b = Build();
476
477 EXPECT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
478
479 EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %3 Block
480 OpMemberDecorate %3 0 Offset 0
481 OpDecorate %1 NonWritable
482 OpDecorate %1 Binding 0
483 OpDecorate %1 DescriptorSet 0
484 )");
485 EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %3 "A"
486 OpMemberName %3 0 "a"
487 OpName %1 "b"
488 )");
489 EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeInt 32 1
490 %3 = OpTypeStruct %4
491 %2 = OpTypePointer StorageBuffer %3
492 %1 = OpVariable %2 StorageBuffer
493 )");
494 }
495
TEST_F(BuilderTest,GlobalVar_TwoVarDeclReadOnly)496 TEST_F(BuilderTest, GlobalVar_TwoVarDeclReadOnly) {
497 // struct A {
498 // a : i32;
499 // };
500 // var<storage, read> b : A
501 // var<storage, read_write> c : A
502
503 auto* A = Structure("A", {Member("a", ty.i32())},
504 {create<ast::StructBlockDecoration>()});
505 auto* var_b =
506 Global("b", ty.Of(A), ast::StorageClass::kStorage, ast::Access::kRead,
507 ast::DecorationList{
508 create<ast::GroupDecoration>(0),
509 create<ast::BindingDecoration>(0),
510 });
511 auto* var_c = Global("c", ty.Of(A), ast::StorageClass::kStorage,
512 ast::Access::kReadWrite,
513 ast::DecorationList{
514 create<ast::GroupDecoration>(1),
515 create<ast::BindingDecoration>(0),
516 });
517
518 spirv::Builder& b = Build();
519
520 EXPECT_TRUE(b.GenerateGlobalVariable(var_b)) << b.error();
521 EXPECT_TRUE(b.GenerateGlobalVariable(var_c)) << b.error();
522
523 EXPECT_EQ(DumpInstructions(b.annots()),
524 R"(OpDecorate %3 Block
525 OpMemberDecorate %3 0 Offset 0
526 OpDecorate %1 NonWritable
527 OpDecorate %1 DescriptorSet 0
528 OpDecorate %1 Binding 0
529 OpDecorate %5 DescriptorSet 1
530 OpDecorate %5 Binding 0
531 )");
532 EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %3 "A"
533 OpMemberName %3 0 "a"
534 OpName %1 "b"
535 OpName %5 "c"
536 )");
537 EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeInt 32 1
538 %3 = OpTypeStruct %4
539 %2 = OpTypePointer StorageBuffer %3
540 %1 = OpVariable %2 StorageBuffer
541 %5 = OpVariable %2 StorageBuffer
542 )");
543 }
544
TEST_F(BuilderTest,GlobalVar_TextureStorageWriteOnly)545 TEST_F(BuilderTest, GlobalVar_TextureStorageWriteOnly) {
546 // var<uniform_constant> a : texture_storage_2d<r32uint, write>;
547
548 auto* type =
549 ty.storage_texture(ast::TextureDimension::k2d, ast::ImageFormat::kR32Uint,
550 ast::Access::kWrite);
551
552 auto* var_a = Global("a", type,
553 ast::DecorationList{
554 create<ast::BindingDecoration>(0),
555 create<ast::GroupDecoration>(0),
556 });
557
558 spirv::Builder& b = Build();
559
560 EXPECT_TRUE(b.GenerateGlobalVariable(var_a)) << b.error();
561
562 EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %1 NonReadable
563 OpDecorate %1 Binding 0
564 OpDecorate %1 DescriptorSet 0
565 )");
566 EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeInt 32 0
567 %3 = OpTypeImage %4 2D 0 0 0 2 R32ui
568 %2 = OpTypePointer UniformConstant %3
569 %1 = OpVariable %2 UniformConstant
570 )");
571 }
572
573 // Check that multiple texture_storage types with different access modifiers
574 // only produces a single OpTypeImage.
575 // Test disabled as storage textures currently only support 'write' access. In
576 // the future we'll likely support read_write.
TEST_F(BuilderTest,DISABLED_GlobalVar_TextureStorageWithDifferentAccess)577 TEST_F(BuilderTest, DISABLED_GlobalVar_TextureStorageWithDifferentAccess) {
578 // var<uniform_constant> a : texture_storage_2d<r32uint, read_write>;
579 // var<uniform_constant> b : texture_storage_2d<r32uint, write>;
580
581 auto* type_a =
582 ty.storage_texture(ast::TextureDimension::k2d, ast::ImageFormat::kR32Uint,
583 ast::Access::kReadWrite);
584 auto* var_a = Global("a", type_a, ast::StorageClass::kNone,
585 ast::DecorationList{
586 create<ast::BindingDecoration>(0),
587 create<ast::GroupDecoration>(0),
588 });
589
590 auto* type_b =
591 ty.storage_texture(ast::TextureDimension::k2d, ast::ImageFormat::kR32Uint,
592 ast::Access::kWrite);
593 auto* var_b = Global("b", type_b, ast::StorageClass::kNone,
594 ast::DecorationList{
595 create<ast::BindingDecoration>(1),
596 create<ast::GroupDecoration>(0),
597 });
598
599 spirv::Builder& b = Build();
600
601 EXPECT_TRUE(b.GenerateGlobalVariable(var_a)) << b.error();
602 EXPECT_TRUE(b.GenerateGlobalVariable(var_b)) << b.error();
603
604 EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %1 NonWritable
605 OpDecorate %1 Binding 0
606 OpDecorate %1 DescriptorSet 0
607 OpDecorate %5 NonReadable
608 OpDecorate %5 Binding 1
609 OpDecorate %5 DescriptorSet 0
610 )");
611 // There must only be one OpTypeImage declaration with the same
612 // arguments
613 EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeInt 32 0
614 %3 = OpTypeImage %4 2D 0 0 0 2 R32ui
615 %2 = OpTypePointer UniformConstant %3
616 %1 = OpVariable %2 UniformConstant
617 %6 = OpTypePointer UniformConstant %3
618 %5 = OpVariable %6 UniformConstant
619 )");
620 }
621
622 } // namespace
623 } // namespace spirv
624 } // namespace writer
625 } // namespace tint
626