diff --git a/src/inspector/inspector_test.cc b/src/inspector/inspector_test.cc index 21555637cd..826c3b6263 100644 --- a/src/inspector/inspector_test.cc +++ b/src/inspector/inspector_test.cc @@ -241,17 +241,6 @@ class InspectorHelper : public ProgramBuilder { return struct_type; } - /// Returns true if the struct with `member_types` requires a block decoration - /// @param member_types a vector of member types - /// @returns true if block decoration is required - bool StructRequiresBlockDecoration( - std::vector member_types) const { - // Structure needs a [[block]] attribute if the last member is a - // dynamically-sized array. - return member_types.back()->Is( - [](auto&& a) { return a->IsRuntimeArray(); }); - } - /// Generates types appropriate for using in a storage buffer /// @param name name for the type /// @param member_types a vector of member types @@ -261,8 +250,7 @@ class InspectorHelper : public ProgramBuilder { std::tuple MakeStorageBufferTypes( const std::string& name, std::vector member_types) { - bool is_block = StructRequiresBlockDecoration(member_types); - auto* struct_type = MakeStructType(name, member_types, is_block); + auto* struct_type = MakeStructType(name, member_types, true); auto* access_type = create( ast::AccessControl::kReadWrite, struct_type); return {struct_type, std::move(access_type)}; @@ -277,8 +265,7 @@ class InspectorHelper : public ProgramBuilder { std::tuple MakeReadOnlyStorageBufferTypes(const std::string& name, std::vector member_types) { - bool is_block = StructRequiresBlockDecoration(member_types); - auto* struct_type = MakeStructType(name, member_types, is_block); + auto* struct_type = MakeStructType(name, member_types, true); auto* access_type = create(ast::AccessControl::kReadOnly, struct_type); return {struct_type, std::move(access_type)}; diff --git a/src/resolver/host_shareable_validation_test.cc b/src/resolver/host_shareable_validation_test.cc index 9da185c60e..85b67cd17c 100644 --- a/src/resolver/host_shareable_validation_test.cc +++ b/src/resolver/host_shareable_validation_test.cc @@ -15,6 +15,7 @@ #include "src/resolver/resolver.h" #include "gmock/gmock.h" +#include "src/ast/struct_block_decoration.h" #include "src/resolver/resolver_test_helper.h" #include "src/sem/struct.h" #include "src/type/access_control_type.h" @@ -26,7 +27,8 @@ namespace { using ResolverHostShareableValidationTest = ResolverTest; TEST_F(ResolverHostShareableValidationTest, BoolMember) { - auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.bool_())}); + auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.bool_())}, + {create()}); auto* a = ty.access(ast::AccessControl::kReadOnly, s); Global(Source{{56, 78}}, "g", a, ast::StorageClass::kStorage); @@ -40,7 +42,8 @@ TEST_F(ResolverHostShareableValidationTest, BoolMember) { } TEST_F(ResolverHostShareableValidationTest, BoolVectorMember) { - auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.vec3())}); + auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.vec3())}, + {create()}); auto* a = ty.access(ast::AccessControl::kReadOnly, s); Global(Source{{56, 78}}, "g", a, ast::StorageClass::kStorage); @@ -55,7 +58,8 @@ TEST_F(ResolverHostShareableValidationTest, BoolVectorMember) { TEST_F(ResolverHostShareableValidationTest, Aliases) { auto* a1 = ty.alias("a1", ty.bool_()); - auto* s = Structure("S", {Member(Source{{12, 34}}, "x", a1)}); + auto* s = Structure("S", {Member(Source{{12, 34}}, "x", a1)}, + {create()}); auto* ac = ty.access(ast::AccessControl::kReadOnly, s); auto* a2 = ty.alias("a2", ac); Global(Source{{56, 78}}, "g", a2, ast::StorageClass::kStorage); @@ -74,7 +78,8 @@ TEST_F(ResolverHostShareableValidationTest, NestedStructures) { auto* i2 = Structure("I2", {Member(Source{{3, 4}}, "y", i1)}); auto* i3 = Structure("I3", {Member(Source{{5, 6}}, "z", i2)}); - auto* s = Structure("S", {Member(Source{{7, 8}}, "m", i3)}); + auto* s = Structure("S", {Member(Source{{7, 8}}, "m", i3)}, + {create()}); auto* a = ty.access(ast::AccessControl::kReadOnly, s); Global(Source{{9, 10}}, "g", a, ast::StorageClass::kStorage); @@ -109,7 +114,8 @@ TEST_F(ResolverHostShareableValidationTest, NoError) { Member(Source{{6, 1}}, "z3", ty.alias("a2", i2)), }); - auto* s = Structure("S", {Member(Source{{7, 8}}, "m", i3)}); + auto* s = Structure("S", {Member(Source{{7, 8}}, "m", i3)}, + {create()}); auto* a = ty.access(ast::AccessControl::kReadOnly, s); Global(Source{{9, 10}}, "g", a, ast::StorageClass::kStorage); diff --git a/src/resolver/resolver.cc b/src/resolver/resolver.cc index 556b55592f..783e997ceb 100644 --- a/src/resolver/resolver.cc +++ b/src/resolver/resolver.cc @@ -284,27 +284,6 @@ bool Resolver::GlobalVariable(ast::Variable* var) { return false; } - if (info->storage_class == ast::StorageClass::kStorage) { - // https://gpuweb.github.io/gpuweb/wgsl/#variable-declaration - // Variables in the storage storage class and variables with a storage - // texture type must have an access attribute applied to the store type. - - // https://gpuweb.github.io/gpuweb/wgsl/#module-scope-variables - // A variable in the storage storage class is a storage buffer variable. Its - // store type must be a host-shareable structure type with block attribute, - // satisfying the storage class constraints. - - auto* access = info->type->As(); - auto* str = access ? access->type()->As() : nullptr; - if (!str) { - diagnostics_.add_error( - "variables declared in the storage class must be of an " - "[[access]] qualified structure type", - var->source()); - return false; - } - } - for (auto* deco : var->decorations()) { Mark(deco); if (!(deco->Is() || @@ -325,6 +304,10 @@ bool Resolver::GlobalVariable(ast::Variable* var) { } } + if (!ValidateGlobalVariable(info)) { + return false; + } + if (!ApplyStorageClassUsageToType(var->declared_storage_class(), info->type, var->source())) { diagnostics_.add_note("while instantiating variable " + @@ -336,6 +319,43 @@ bool Resolver::GlobalVariable(ast::Variable* var) { return true; } +bool Resolver::ValidateGlobalVariable(const VariableInfo* info) { + if (info->storage_class == ast::StorageClass::kStorage) { + // https://gpuweb.github.io/gpuweb/wgsl/#variable-declaration + // Variables in the storage storage class and variables with a storage + // texture type must have an access attribute applied to the store type. + + // https://gpuweb.github.io/gpuweb/wgsl/#module-scope-variables + // A variable in the storage storage class is a storage buffer variable. Its + // store type must be a host-shareable structure type with block attribute, + // satisfying the storage class constraints. + + auto* access = info->type->As(); + auto* str = access ? access->type()->As() : nullptr; + if (!str) { + diagnostics_.add_error( + "variables declared in the storage class must be of an " + "[[access]] qualified structure type", + info->declaration->source()); + return false; + } + + if (!str->IsBlockDecorated()) { + diagnostics_.add_error( + "structure used as a storage buffer must be declared with the " + "[[block]] decoration", + str->impl()->source()); + if (info->declaration->source().range.begin.line) { + diagnostics_.add_note("structure used as storage buffer here", + info->declaration->source()); + } + return false; + } + } + + return true; +} + bool Resolver::ValidateVariable(const ast::Variable* var) { auto* type = variable_to_info_[var]->type; if (auto* r = type->UnwrapAll()->As()) { diff --git a/src/resolver/resolver.h b/src/resolver/resolver.h index 7bc29409e5..321d0aaa16 100644 --- a/src/resolver/resolver.h +++ b/src/resolver/resolver.h @@ -240,6 +240,7 @@ class Resolver { bool ValidateBinary(ast::BinaryExpression* expr); bool ValidateEntryPoint(const ast::Function* func); bool ValidateFunction(const ast::Function* func); + bool ValidateGlobalVariable(const VariableInfo* var); bool ValidateMatrixConstructor(const type::Matrix* matrix_type, const ast::ExpressionList& values); bool ValidateParameter(const ast::Variable* param); diff --git a/src/resolver/resolver_test.cc b/src/resolver/resolver_test.cc index d9f8ac7d3d..ffd7bb9b6c 100644 --- a/src/resolver/resolver_test.cc +++ b/src/resolver/resolver_test.cc @@ -28,6 +28,7 @@ #include "src/ast/loop_statement.h" #include "src/ast/return_statement.h" #include "src/ast/stage_decoration.h" +#include "src/ast/struct_block_decoration.h" #include "src/ast/switch_statement.h" #include "src/ast/unary_op_expression.h" #include "src/ast/variable_decl_statement.h" @@ -764,7 +765,8 @@ TEST_F(ResolverTest, Function_Parameters) { } TEST_F(ResolverTest, Function_RegisterInputOutputVariables) { - auto* s = Structure("S", {Member("m", ty.u32())}); + auto* s = Structure("S", {Member("m", ty.u32())}, + {create()}); auto* a = ty.access(ast::AccessControl::kReadOnly, s); auto* in_var = Global("in_var", ty.f32(), ast::StorageClass::kInput); @@ -799,7 +801,8 @@ TEST_F(ResolverTest, Function_RegisterInputOutputVariables) { } TEST_F(ResolverTest, Function_RegisterInputOutputVariables_SubFunction) { - auto* s = Structure("S", {Member("m", ty.u32())}); + auto* s = Structure("S", {Member("m", ty.u32())}, + {create()}); auto* a = ty.access(ast::AccessControl::kReadOnly, s); auto* in_var = Global("in_var", ty.f32(), ast::StorageClass::kInput); diff --git a/src/resolver/storage_class_validation_test.cc b/src/resolver/storage_class_validation_test.cc index 5f9324eac0..5dfdd1897e 100644 --- a/src/resolver/storage_class_validation_test.cc +++ b/src/resolver/storage_class_validation_test.cc @@ -15,6 +15,7 @@ #include "src/resolver/resolver.h" #include "gmock/gmock.h" +#include "src/ast/struct_block_decoration.h" #include "src/resolver/resolver_test_helper.h" #include "src/sem/struct.h" #include "src/type/access_control_type.h" @@ -34,7 +35,7 @@ TEST_F(ResolverStorageClassValidationTest, GlobalVariableNoStorageClass_Fail) { "12:34 error v-0022: global variables must have a storage class"); } -TEST_F(ResolverStorageClassValidationTest, Bool) { +TEST_F(ResolverStorageClassValidationTest, StorageBufferBool) { // var g : bool; Global(Source{{56, 78}}, "g", ty.bool_(), ast::StorageClass::kStorage); @@ -45,7 +46,7 @@ TEST_F(ResolverStorageClassValidationTest, Bool) { R"(56:78 error: variables declared in the storage class must be of an [[access]] qualified structure type)"); } -TEST_F(ResolverStorageClassValidationTest, Pointer) { +TEST_F(ResolverStorageClassValidationTest, StorageBufferPointer) { // var g : ptr; Global(Source{{56, 78}}, "g", ty.pointer(ast::StorageClass::kInput), ast::StorageClass::kStorage); @@ -57,7 +58,7 @@ TEST_F(ResolverStorageClassValidationTest, Pointer) { R"(56:78 error: variables declared in the storage class must be of an [[access]] qualified structure type)"); } -TEST_F(ResolverStorageClassValidationTest, Array) { +TEST_F(ResolverStorageClassValidationTest, StorageBufferArray) { // var g : [[access(read)]] array; auto* s = Structure("S", {Member("a", ty.f32())}); auto* a = ty.array(s, 3); @@ -71,7 +72,7 @@ TEST_F(ResolverStorageClassValidationTest, Array) { R"(56:78 error: variables declared in the storage class must be of an [[access]] qualified structure type)"); } -TEST_F(ResolverStorageClassValidationTest, BoolAlias) { +TEST_F(ResolverStorageClassValidationTest, StorageBufferBoolAlias) { // type a = bool; // var g : [[access(read)]] a; auto* a = ty.alias("a", ty.bool_()); @@ -84,7 +85,7 @@ TEST_F(ResolverStorageClassValidationTest, BoolAlias) { R"(56:78 error: variables declared in the storage class must be of an [[access]] qualified structure type)"); } -TEST_F(ResolverStorageClassValidationTest, NoAccessControl) { +TEST_F(ResolverStorageClassValidationTest, StorageBufferNoAccessControl) { // var g : S; auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())}); Global(Source{{56, 78}}, "g", s, ast::StorageClass::kStorage); @@ -96,20 +97,39 @@ TEST_F(ResolverStorageClassValidationTest, NoAccessControl) { R"(56:78 error: variables declared in the storage class must be of an [[access]] qualified structure type)"); } -TEST_F(ResolverStorageClassValidationTest, NoError_Basic) { +TEST_F(ResolverStorageClassValidationTest, StorageBufferNoBlockDecoration) { + // struct S { x : i32 }; // var g : [[access(read)]] S; - auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())}); + auto* s = Structure(Source{{12, 34}}, "S", {Member("x", ty.i32())}); + auto* a = ty.access(ast::AccessControl::kReadOnly, s); + Global(Source{{56, 78}}, "g", a, ast::StorageClass::kStorage); + + ASSERT_FALSE(r()->Resolve()); + + EXPECT_EQ( + r()->error(), + R"(12:34 error: structure used as a storage buffer must be declared with the [[block]] decoration +56:78 note: structure used as storage buffer here)"); +} + +TEST_F(ResolverStorageClassValidationTest, StorageBufferNoError_Basic) { + // [[block]] struct S { x : i32 }; + // var g : [[access(read)]] S; + auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())}, + {create()}); auto* a = ty.access(ast::AccessControl::kReadOnly, s); Global(Source{{56, 78}}, "g", a, ast::StorageClass::kStorage); ASSERT_TRUE(r()->Resolve()); } -TEST_F(ResolverStorageClassValidationTest, NoError_Aliases) { +TEST_F(ResolverStorageClassValidationTest, StorageBufferNoError_Aliases) { + // [[block]] struct S { x : i32 }; // type a1 = S; // type a2 = [[access(read)]] a1; // var g : a2; - auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())}); + auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())}, + {create()}); auto* a1 = ty.alias("a1", s); auto* ac = ty.access(ast::AccessControl::kReadOnly, a1); auto* a2 = ty.alias("a2", ac); diff --git a/src/resolver/struct_storage_class_use_test.cc b/src/resolver/struct_storage_class_use_test.cc index 0521200e30..475199277d 100644 --- a/src/resolver/struct_storage_class_use_test.cc +++ b/src/resolver/struct_storage_class_use_test.cc @@ -15,6 +15,7 @@ #include "src/resolver/resolver.h" #include "gmock/gmock.h" +#include "src/ast/struct_block_decoration.h" #include "src/resolver/resolver_test_helper.h" #include "src/sem/struct.h" @@ -167,7 +168,8 @@ TEST_F(ResolverStorageClassUseTest, StructReachableViaLocalArray) { } TEST_F(ResolverStorageClassUseTest, StructMultipleStorageClassUses) { - auto* s = Structure("S", {Member("a", ty.f32())}); + auto* s = Structure("S", {Member("a", ty.f32())}, + {create()}); auto* ac = ty.access(ast::AccessControl::kReadOnly, s); Global("x", s, ast::StorageClass::kUniform); Global("y", ac, ast::StorageClass::kStorage); diff --git a/src/writer/hlsl/generator_impl_function_test.cc b/src/writer/hlsl/generator_impl_function_test.cc index c3fa8c43e5..923ee9bf02 100644 --- a/src/writer/hlsl/generator_impl_function_test.cc +++ b/src/writer/hlsl/generator_impl_function_test.cc @@ -388,10 +388,12 @@ void frag_main() { TEST_F(HlslGeneratorImplTest_Function, Emit_Decoration_EntryPoint_With_RW_StorageBuffer_Read) { - auto* s = Structure("Data", { - Member("a", ty.i32()), - Member("b", ty.f32()), - }); + auto* s = Structure("Data", + { + Member("a", ty.i32()), + Member("b", ty.f32()), + }, + {create()}); type::AccessControl ac(ast::AccessControl::kReadWrite, s); @@ -432,10 +434,12 @@ void frag_main() { TEST_F(HlslGeneratorImplTest_Function, Emit_Decoration_EntryPoint_With_RO_StorageBuffer_Read) { - auto* s = Structure("Data", { - Member("a", ty.i32()), - Member("b", ty.f32()), - }); + auto* s = Structure("Data", + { + Member("a", ty.i32()), + Member("b", ty.f32()), + }, + {create()}); type::AccessControl ac(ast::AccessControl::kReadOnly, s); @@ -476,10 +480,12 @@ void frag_main() { TEST_F(HlslGeneratorImplTest_Function, Emit_Decoration_EntryPoint_With_WO_StorageBuffer_Store) { - auto* s = Structure("Data", { - Member("a", ty.i32()), - Member("b", ty.f32()), - }); + auto* s = Structure("Data", + { + Member("a", ty.i32()), + Member("b", ty.f32()), + }, + {create()}); type::AccessControl ac(ast::AccessControl::kWriteOnly, s); @@ -518,10 +524,12 @@ void frag_main() { TEST_F(HlslGeneratorImplTest_Function, Emit_Decoration_EntryPoint_With_StorageBuffer_Store) { - auto* s = Structure("Data", { - Member("a", ty.i32()), - Member("b", ty.f32()), - }); + auto* s = Structure("Data", + { + Member("a", ty.i32()), + Member("b", ty.f32()), + }, + {create()}); type::AccessControl ac(ast::AccessControl::kReadWrite, s); @@ -777,7 +785,8 @@ void frag_main() { TEST_F(HlslGeneratorImplTest_Function, Emit_Decoration_Called_By_EntryPoint_With_StorageBuffer) { - auto* s = Structure("S", {Member("x", ty.f32())}); + auto* s = Structure("S", {Member("x", ty.f32())}, + {create()}); auto* ac = ty.access(ast::AccessControl::kReadWrite, s); Global("coord", ac, ast::StorageClass::kStorage, nullptr, ast::DecorationList{ diff --git a/src/writer/hlsl/generator_impl_type_test.cc b/src/writer/hlsl/generator_impl_type_test.cc index e03a7b491b..d9bb495fcb 100644 --- a/src/writer/hlsl/generator_impl_type_test.cc +++ b/src/writer/hlsl/generator_impl_type_test.cc @@ -173,10 +173,12 @@ TEST_F(HlslGeneratorImplTest_Type, EmitType_StructDecl) { } TEST_F(HlslGeneratorImplTest_Type, EmitType_StructDecl_OmittedIfStorageBuffer) { - auto* s = Structure("S", { - Member("a", ty.i32()), - Member("b", ty.f32()), - }); + auto* s = Structure("S", + { + Member("a", ty.i32()), + Member("b", ty.f32()), + }, + {create()}); Global("g", ty.access(ast::AccessControl::kReadWrite, s), ast::StorageClass::kStorage); diff --git a/src/writer/msl/generator_impl_function_test.cc b/src/writer/msl/generator_impl_function_test.cc index e81540101a..8cf5908bf6 100644 --- a/src/writer/msl/generator_impl_function_test.cc +++ b/src/writer/msl/generator_impl_function_test.cc @@ -307,10 +307,12 @@ vertex tint_symbol_2 vert_main2() { TEST_F(MslGeneratorImplTest, Emit_FunctionDecoration_EntryPoint_With_RW_StorageBuffer) { - auto* s = Structure("Data", { - Member("a", ty.i32()), - Member("b", ty.f32()), - }); + auto* s = Structure("Data", + { + Member("a", ty.i32()), + Member("b", ty.f32()), + }, + {create()}); type::AccessControl ac(ast::AccessControl::kReadWrite, s); @@ -351,10 +353,12 @@ fragment void frag_main(device Data& coord [[buffer(0)]]) { TEST_F(MslGeneratorImplTest, Emit_FunctionDecoration_EntryPoint_With_RO_StorageBuffer) { - auto* s = Structure("Data", { - Member("a", ty.i32()), - Member("b", ty.f32()), - }); + auto* s = Structure("Data", + { + Member("a", ty.i32()), + Member("b", ty.f32()), + }, + {create()}); type::AccessControl ac(ast::AccessControl::kReadOnly, s); @@ -609,10 +613,12 @@ fragment void frag_main(constant float4& coord [[buffer(0)]]) { TEST_F(MslGeneratorImplTest, Emit_FunctionDecoration_Called_By_EntryPoint_With_RW_StorageBuffer) { - auto* s = Structure("Data", { - Member("a", ty.i32()), - Member("b", ty.f32()), - }); + auto* s = Structure("Data", + { + Member("a", ty.i32()), + Member("b", ty.f32()), + }, + {create()}); type::AccessControl ac(ast::AccessControl::kReadWrite, s); @@ -665,10 +671,12 @@ fragment void frag_main(device Data& coord [[buffer(0)]]) { TEST_F(MslGeneratorImplTest, Emit_FunctionDecoration_Called_By_EntryPoint_With_RO_StorageBuffer) { - auto* s = Structure("Data", { - Member("a", ty.i32()), - Member("b", ty.f32()), - }); + auto* s = Structure("Data", + { + Member("a", ty.i32()), + Member("b", ty.f32()), + }, + {create()}); type::AccessControl ac(ast::AccessControl::kReadOnly, s); diff --git a/src/writer/msl/generator_impl_type_test.cc b/src/writer/msl/generator_impl_type_test.cc index 69d36c9f8e..fbad91ae9a 100644 --- a/src/writer/msl/generator_impl_type_test.cc +++ b/src/writer/msl/generator_impl_type_test.cc @@ -201,35 +201,37 @@ TEST_F(MslGeneratorImplTest, EmitType_StructDecl) { } TEST_F(MslGeneratorImplTest, EmitType_Struct_Layout_NonComposites) { - auto* s = Structure( - "S", { - Member("a", ty.i32(), {MemberSize(32)}), - Member("b", ty.f32(), {MemberAlign(128), MemberSize(128)}), - Member("c", ty.vec2()), - Member("d", ty.u32()), - Member("e", ty.vec3()), - Member("f", ty.u32()), - Member("g", ty.vec4()), - Member("h", ty.u32()), - Member("i", ty.mat2x2()), - Member("j", ty.u32()), - Member("k", ty.mat2x3()), - Member("l", ty.u32()), - Member("m", ty.mat2x4()), - Member("n", ty.u32()), - Member("o", ty.mat3x2()), - Member("p", ty.u32()), - Member("q", ty.mat3x3()), - Member("r", ty.u32()), - Member("s", ty.mat3x4()), - Member("t", ty.u32()), - Member("u", ty.mat4x2()), - Member("v", ty.u32()), - Member("w", ty.mat4x3()), - Member("x", ty.u32()), - Member("y", ty.mat4x4()), - Member("z", ty.f32()), - }); + auto* s = + Structure("S", + { + Member("a", ty.i32(), {MemberSize(32)}), + Member("b", ty.f32(), {MemberAlign(128), MemberSize(128)}), + Member("c", ty.vec2()), + Member("d", ty.u32()), + Member("e", ty.vec3()), + Member("f", ty.u32()), + Member("g", ty.vec4()), + Member("h", ty.u32()), + Member("i", ty.mat2x2()), + Member("j", ty.u32()), + Member("k", ty.mat2x3()), + Member("l", ty.u32()), + Member("m", ty.mat2x4()), + Member("n", ty.u32()), + Member("o", ty.mat3x2()), + Member("p", ty.u32()), + Member("q", ty.mat3x3()), + Member("r", ty.u32()), + Member("s", ty.mat3x4()), + Member("t", ty.u32()), + Member("u", ty.mat4x2()), + Member("v", ty.u32()), + Member("w", ty.mat4x3()), + Member("x", ty.u32()), + Member("y", ty.mat4x4()), + Member("z", ty.f32()), + }, + {create()}); Global("G", ty.access(ast::AccessControl::kReadOnly, s), ast::StorageClass::kStorage); @@ -326,13 +328,15 @@ TEST_F(MslGeneratorImplTest, EmitType_Struct_Layout_Structures) { Member("b", ty.f32()), }); - auto* s = Structure("S", { - Member("a", ty.i32()), - Member("b", inner_x), - Member("c", ty.f32()), - Member("d", inner_y), - Member("e", ty.f32()), - }); + auto* s = Structure("S", + { + Member("a", ty.i32()), + Member("b", inner_x), + Member("c", ty.f32()), + Member("d", inner_y), + Member("e", ty.f32()), + }, + {create()}); Global("G", ty.access(ast::AccessControl::kReadOnly, s), ast::StorageClass::kStorage); @@ -523,7 +527,8 @@ TEST_F(MslGeneratorImplTest, AttemptTintPadSymbolCollision) { Member("tint_pad_28", ty.u32()), Member("tint_pad_4", ty.mat4x4()), Member("tint_pad_21", ty.f32()), - }); + }, + {create()}); Global("G", ty.access(ast::AccessControl::kReadOnly, s), ast::StorageClass::kStorage); diff --git a/src/writer/spirv/builder_global_variable_test.cc b/src/writer/spirv/builder_global_variable_test.cc index eb0ed7886f..6b541efdd9 100644 --- a/src/writer/spirv/builder_global_variable_test.cc +++ b/src/writer/spirv/builder_global_variable_test.cc @@ -14,6 +14,7 @@ #include "src/ast/constant_id_decoration.h" #include "src/ast/stage_decoration.h" +#include "src/ast/struct_block_decoration.h" #include "src/writer/spirv/spv_dump.h" #include "src/writer/spirv/test_helper.h" @@ -387,10 +388,12 @@ TEST_F(BuilderTest, GlobalVar_DeclReadOnly) { // }; // var b : [[access(read)]] A - auto* A = Structure("A", { - Member("a", ty.i32()), - Member("b", ty.i32()), - }); + auto* A = Structure("A", + { + Member("a", ty.i32()), + Member("b", ty.i32()), + }, + {create()}); auto* ac = create(ast::AccessControl::kReadOnly, A); auto* var = Global("b", ac, ast::StorageClass::kStorage); @@ -399,7 +402,8 @@ TEST_F(BuilderTest, GlobalVar_DeclReadOnly) { EXPECT_TRUE(b.GenerateGlobalVariable(var)) << b.error(); - EXPECT_EQ(DumpInstructions(b.annots()), R"(OpMemberDecorate %3 0 Offset 0 + EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %3 Block +OpMemberDecorate %3 0 Offset 0 OpMemberDecorate %3 0 NonWritable OpMemberDecorate %3 1 Offset 4 OpMemberDecorate %3 1 NonWritable @@ -423,7 +427,8 @@ TEST_F(BuilderTest, GlobalVar_TypeAliasDeclReadOnly) { // type B = A; // var b : [[access(read)]] B - auto* A = Structure("A", {Member("a", ty.i32())}); + auto* A = Structure("A", {Member("a", ty.i32())}, + {create()}); auto* B = ty.alias("B", A); auto* ac = create(ast::AccessControl::kReadOnly, B); auto* var = Global("b", ac, ast::StorageClass::kStorage); @@ -432,7 +437,8 @@ TEST_F(BuilderTest, GlobalVar_TypeAliasDeclReadOnly) { EXPECT_TRUE(b.GenerateGlobalVariable(var)) << b.error(); - EXPECT_EQ(DumpInstructions(b.annots()), R"(OpMemberDecorate %3 0 Offset 0 + EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %3 Block +OpMemberDecorate %3 0 Offset 0 OpMemberDecorate %3 0 NonWritable )"); EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %3 "A" @@ -453,7 +459,8 @@ TEST_F(BuilderTest, GlobalVar_TypeAliasAssignReadOnly) { // type B = [[access(read)]] A; // var b : B - auto* A = Structure("A", {Member("a", ty.i32())}); + auto* A = Structure("A", {Member("a", ty.i32())}, + {create()}); auto* ac = create(ast::AccessControl::kReadOnly, A); auto* B = ty.alias("B", ac); auto* var = Global("b", B, ast::StorageClass::kStorage); @@ -462,7 +469,8 @@ TEST_F(BuilderTest, GlobalVar_TypeAliasAssignReadOnly) { EXPECT_TRUE(b.GenerateGlobalVariable(var)) << b.error(); - EXPECT_EQ(DumpInstructions(b.annots()), R"(OpMemberDecorate %3 0 Offset 0 + EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %3 Block +OpMemberDecorate %3 0 Offset 0 OpMemberDecorate %3 0 NonWritable )"); EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %3 "A" @@ -483,7 +491,8 @@ TEST_F(BuilderTest, GlobalVar_TwoVarDeclReadOnly) { // var b : [[access(read)]] A // var c : [[access(read_write)]] A - auto* A = Structure("A", {Member("a", ty.i32())}); + auto* A = Structure("A", {Member("a", ty.i32())}, + {create()}); type::AccessControl read{ast::AccessControl::kReadOnly, A}; type::AccessControl rw{ast::AccessControl::kReadWrite, A}; @@ -496,8 +505,10 @@ TEST_F(BuilderTest, GlobalVar_TwoVarDeclReadOnly) { EXPECT_TRUE(b.GenerateGlobalVariable(var_c)) << b.error(); EXPECT_EQ(DumpInstructions(b.annots()), - R"(OpMemberDecorate %3 0 Offset 0 + R"(OpDecorate %3 Block +OpMemberDecorate %3 0 Offset 0 OpMemberDecorate %3 0 NonWritable +OpDecorate %7 Block OpMemberDecorate %7 0 Offset 0 )"); EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %3 "A"