• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 The Tint Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "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