| // Copyright 2020 The Tint Authors. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include <array> |
| |
| #include "gmock/gmock.h" |
| |
| #include "src/ast/struct_block_decoration.h" |
| #include "src/sem/depth_multisampled_texture_type.h" |
| #include "src/sem/depth_texture_type.h" |
| #include "src/sem/multisampled_texture_type.h" |
| #include "src/sem/sampled_texture_type.h" |
| #include "src/sem/sampler_type.h" |
| #include "src/sem/storage_texture_type.h" |
| #include "src/writer/msl/test_helper.h" |
| |
| namespace tint { |
| namespace writer { |
| namespace msl { |
| namespace { |
| |
| using ::testing::HasSubstr; |
| |
| #define CHECK_TYPE_SIZE_AND_ALIGN(TYPE, SIZE, ALIGN) \ |
| static_assert(sizeof(TYPE) == SIZE, "Bad type size"); \ |
| static_assert(alignof(TYPE) == ALIGN, "Bad type alignment") |
| |
| // Declare C++ types that match the size and alignment of the types of the same |
| // name in MSL. |
| #define DECLARE_TYPE(NAME, SIZE, ALIGN) \ |
| struct alignas(ALIGN) NAME { \ |
| uint8_t _[SIZE]; \ |
| }; \ |
| CHECK_TYPE_SIZE_AND_ALIGN(NAME, SIZE, ALIGN) |
| |
| // Size and alignments taken from the MSL spec: |
| // https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf |
| DECLARE_TYPE(float2, 8, 8); |
| DECLARE_TYPE(packed_float3, 12, 4); |
| DECLARE_TYPE(float4, 16, 16); |
| DECLARE_TYPE(float2x2, 16, 8); |
| DECLARE_TYPE(float2x3, 32, 16); |
| DECLARE_TYPE(float2x4, 32, 16); |
| DECLARE_TYPE(float3x2, 24, 8); |
| DECLARE_TYPE(float3x3, 48, 16); |
| DECLARE_TYPE(float3x4, 48, 16); |
| DECLARE_TYPE(float4x2, 32, 8); |
| DECLARE_TYPE(float4x3, 64, 16); |
| DECLARE_TYPE(float4x4, 64, 16); |
| using uint = unsigned int; |
| |
| using MslGeneratorImplTest = TestHelper; |
| |
| TEST_F(MslGeneratorImplTest, EmitType_Array) { |
| auto* arr = ty.array<bool, 4>(); |
| Global("G", arr, ast::StorageClass::kPrivate); |
| |
| GeneratorImpl& gen = Build(); |
| |
| std::stringstream out; |
| ASSERT_TRUE(gen.EmitType(out, program->TypeOf(arr), "ary")) << gen.error(); |
| EXPECT_EQ(out.str(), "bool ary[4]"); |
| } |
| |
| TEST_F(MslGeneratorImplTest, EmitType_ArrayOfArray) { |
| auto* a = ty.array<bool, 4>(); |
| auto* b = ty.array(a, 5); |
| Global("G", b, ast::StorageClass::kPrivate); |
| |
| GeneratorImpl& gen = Build(); |
| |
| std::stringstream out; |
| ASSERT_TRUE(gen.EmitType(out, program->TypeOf(b), "ary")) << gen.error(); |
| EXPECT_EQ(out.str(), "bool ary[5][4]"); |
| } |
| |
| TEST_F(MslGeneratorImplTest, EmitType_ArrayOfArrayOfArray) { |
| auto* a = ty.array<bool, 4>(); |
| auto* b = ty.array(a, 5); |
| auto* c = ty.array(b, 6); |
| Global("G", c, ast::StorageClass::kPrivate); |
| |
| GeneratorImpl& gen = Build(); |
| |
| std::stringstream out; |
| ASSERT_TRUE(gen.EmitType(out, program->TypeOf(c), "ary")) << gen.error(); |
| EXPECT_EQ(out.str(), "bool ary[6][5][4]"); |
| } |
| |
| TEST_F(MslGeneratorImplTest, EmitType_Array_WithoutName) { |
| auto* arr = ty.array<bool, 4>(); |
| Global("G", arr, ast::StorageClass::kPrivate); |
| |
| GeneratorImpl& gen = Build(); |
| |
| std::stringstream out; |
| ASSERT_TRUE(gen.EmitType(out, program->TypeOf(arr), "")) << gen.error(); |
| EXPECT_EQ(out.str(), "bool[4]"); |
| } |
| |
| TEST_F(MslGeneratorImplTest, EmitType_RuntimeArray) { |
| auto* arr = ty.array<bool, 1>(); |
| Global("G", arr, ast::StorageClass::kPrivate); |
| |
| GeneratorImpl& gen = Build(); |
| |
| std::stringstream out; |
| ASSERT_TRUE(gen.EmitType(out, program->TypeOf(arr), "ary")) << gen.error(); |
| EXPECT_EQ(out.str(), "bool ary[1]"); |
| } |
| |
| TEST_F(MslGeneratorImplTest, EmitType_ArrayWithStride) { |
| auto* s = Structure("s", {Member("arr", ty.array<f32, 4>(64))}, |
| {create<ast::StructBlockDecoration>()}); |
| auto* ubo = Global("ubo", ty.Of(s), ast::StorageClass::kUniform, |
| ast::DecorationList{ |
| create<ast::GroupDecoration>(0), |
| create<ast::BindingDecoration>(1), |
| }); |
| WrapInFunction(MemberAccessor(ubo, "arr")); |
| |
| GeneratorImpl& gen = SanitizeAndBuild(); |
| |
| ASSERT_TRUE(gen.Generate()) << gen.error(); |
| EXPECT_THAT(gen.result(), HasSubstr(R"(struct tint_padded_array_element { |
| /* 0x0000 */ float el; |
| /* 0x0004 */ int8_t tint_pad[60]; |
| };)")); |
| EXPECT_THAT(gen.result(), HasSubstr(R"(struct tint_array_wrapper { |
| /* 0x0000 */ tint_padded_array_element arr[4]; |
| };)")); |
| EXPECT_THAT(gen.result(), HasSubstr(R"(struct s { |
| /* 0x0000 */ tint_array_wrapper arr; |
| };)")); |
| } |
| |
| TEST_F(MslGeneratorImplTest, EmitType_Bool) { |
| auto* bool_ = create<sem::Bool>(); |
| |
| GeneratorImpl& gen = Build(); |
| |
| std::stringstream out; |
| ASSERT_TRUE(gen.EmitType(out, bool_, "")) << gen.error(); |
| EXPECT_EQ(out.str(), "bool"); |
| } |
| |
| TEST_F(MslGeneratorImplTest, EmitType_F32) { |
| auto* f32 = create<sem::F32>(); |
| |
| GeneratorImpl& gen = Build(); |
| |
| std::stringstream out; |
| ASSERT_TRUE(gen.EmitType(out, f32, "")) << gen.error(); |
| EXPECT_EQ(out.str(), "float"); |
| } |
| |
| TEST_F(MslGeneratorImplTest, EmitType_I32) { |
| auto* i32 = create<sem::I32>(); |
| |
| GeneratorImpl& gen = Build(); |
| |
| std::stringstream out; |
| ASSERT_TRUE(gen.EmitType(out, i32, "")) << gen.error(); |
| EXPECT_EQ(out.str(), "int"); |
| } |
| |
| TEST_F(MslGeneratorImplTest, EmitType_Matrix) { |
| auto* f32 = create<sem::F32>(); |
| auto* vec3 = create<sem::Vector>(f32, 3); |
| auto* mat2x3 = create<sem::Matrix>(vec3, 2); |
| |
| GeneratorImpl& gen = Build(); |
| |
| std::stringstream out; |
| ASSERT_TRUE(gen.EmitType(out, mat2x3, "")) << gen.error(); |
| EXPECT_EQ(out.str(), "float2x3"); |
| } |
| |
| TEST_F(MslGeneratorImplTest, EmitType_Pointer) { |
| auto* f32 = create<sem::F32>(); |
| auto* p = create<sem::Pointer>(f32, ast::StorageClass::kWorkgroup, |
| ast::Access::kReadWrite); |
| |
| GeneratorImpl& gen = Build(); |
| |
| std::stringstream out; |
| ASSERT_TRUE(gen.EmitType(out, p, "")) << gen.error(); |
| EXPECT_EQ(out.str(), "threadgroup float* "); |
| } |
| |
| TEST_F(MslGeneratorImplTest, EmitType_Struct) { |
| auto* s = Structure("S", { |
| Member("a", ty.i32()), |
| Member("b", ty.f32()), |
| }); |
| |
| GeneratorImpl& gen = Build(); |
| |
| std::stringstream out; |
| ASSERT_TRUE(gen.EmitType(out, program->TypeOf(s), "")) << gen.error(); |
| EXPECT_EQ(out.str(), "S"); |
| } |
| |
| TEST_F(MslGeneratorImplTest, EmitType_StructDecl) { |
| auto* s = Structure("S", { |
| Member("a", ty.i32()), |
| Member("b", ty.f32()), |
| }); |
| |
| GeneratorImpl& gen = Build(); |
| |
| TextGenerator::TextBuffer buf; |
| auto* sem_s = program->TypeOf(s)->As<sem::Struct>(); |
| ASSERT_TRUE(gen.EmitStructType(&buf, sem_s)) << gen.error(); |
| EXPECT_EQ(buf.String(), R"(struct S { |
| int a; |
| float b; |
| }; |
| )"); |
| } |
| |
| 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<f32>()), |
| Member("d", ty.u32()), |
| Member("e", ty.vec3<f32>()), |
| Member("f", ty.u32()), |
| Member("g", ty.vec4<f32>()), |
| Member("h", ty.u32()), |
| Member("i", ty.mat2x2<f32>()), |
| Member("j", ty.u32()), |
| Member("k", ty.mat2x3<f32>()), |
| Member("l", ty.u32()), |
| Member("m", ty.mat2x4<f32>()), |
| Member("n", ty.u32()), |
| Member("o", ty.mat3x2<f32>()), |
| Member("p", ty.u32()), |
| Member("q", ty.mat3x3<f32>()), |
| Member("r", ty.u32()), |
| Member("s", ty.mat3x4<f32>()), |
| Member("t", ty.u32()), |
| Member("u", ty.mat4x2<f32>()), |
| Member("v", ty.u32()), |
| Member("w", ty.mat4x3<f32>()), |
| Member("x", ty.u32()), |
| Member("y", ty.mat4x4<f32>()), |
| Member("z", ty.f32()), |
| }, |
| {create<ast::StructBlockDecoration>()}); |
| |
| Global("G", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead, |
| ast::DecorationList{ |
| create<ast::BindingDecoration>(0), |
| create<ast::GroupDecoration>(0), |
| }); |
| |
| GeneratorImpl& gen = Build(); |
| |
| TextGenerator::TextBuffer buf; |
| auto* sem_s = program->TypeOf(s)->As<sem::Struct>(); |
| ASSERT_TRUE(gen.EmitStructType(&buf, sem_s)) << gen.error(); |
| |
| // ALL_FIELDS() calls the macro FIELD(ADDR, TYPE, NAME, SUFFIX) |
| // for each field of the structure s. |
| #define ALL_FIELDS() \ |
| FIELD(0x0000, int, a, /*NO SUFFIX*/) \ |
| FIELD(0x0004, int8_t, tint_pad, [124]) \ |
| FIELD(0x0080, float, b, /*NO SUFFIX*/) \ |
| FIELD(0x0084, int8_t, tint_pad_1, [124]) \ |
| FIELD(0x0100, float2, c, /*NO SUFFIX*/) \ |
| FIELD(0x0108, uint, d, /*NO SUFFIX*/) \ |
| FIELD(0x010c, int8_t, tint_pad_2, [4]) \ |
| FIELD(0x0110, packed_float3, e, /*NO SUFFIX*/) \ |
| FIELD(0x011c, uint, f, /*NO SUFFIX*/) \ |
| FIELD(0x0120, float4, g, /*NO SUFFIX*/) \ |
| FIELD(0x0130, uint, h, /*NO SUFFIX*/) \ |
| FIELD(0x0134, int8_t, tint_pad_3, [4]) \ |
| FIELD(0x0138, float2x2, i, /*NO SUFFIX*/) \ |
| FIELD(0x0148, uint, j, /*NO SUFFIX*/) \ |
| FIELD(0x014c, int8_t, tint_pad_4, [4]) \ |
| FIELD(0x0150, float2x3, k, /*NO SUFFIX*/) \ |
| FIELD(0x0170, uint, l, /*NO SUFFIX*/) \ |
| FIELD(0x0174, int8_t, tint_pad_5, [12]) \ |
| FIELD(0x0180, float2x4, m, /*NO SUFFIX*/) \ |
| FIELD(0x01a0, uint, n, /*NO SUFFIX*/) \ |
| FIELD(0x01a4, int8_t, tint_pad_6, [4]) \ |
| FIELD(0x01a8, float3x2, o, /*NO SUFFIX*/) \ |
| FIELD(0x01c0, uint, p, /*NO SUFFIX*/) \ |
| FIELD(0x01c4, int8_t, tint_pad_7, [12]) \ |
| FIELD(0x01d0, float3x3, q, /*NO SUFFIX*/) \ |
| FIELD(0x0200, uint, r, /*NO SUFFIX*/) \ |
| FIELD(0x0204, int8_t, tint_pad_8, [12]) \ |
| FIELD(0x0210, float3x4, s, /*NO SUFFIX*/) \ |
| FIELD(0x0240, uint, t, /*NO SUFFIX*/) \ |
| FIELD(0x0244, int8_t, tint_pad_9, [4]) \ |
| FIELD(0x0248, float4x2, u, /*NO SUFFIX*/) \ |
| FIELD(0x0268, uint, v, /*NO SUFFIX*/) \ |
| FIELD(0x026c, int8_t, tint_pad_10, [4]) \ |
| FIELD(0x0270, float4x3, w, /*NO SUFFIX*/) \ |
| FIELD(0x02b0, uint, x, /*NO SUFFIX*/) \ |
| FIELD(0x02b4, int8_t, tint_pad_11, [12]) \ |
| FIELD(0x02c0, float4x4, y, /*NO SUFFIX*/) \ |
| FIELD(0x0300, float, z, /*NO SUFFIX*/) \ |
| FIELD(0x0304, int8_t, tint_pad_12, [124]) |
| |
| // Check that the generated string is as expected. |
| #define FIELD(ADDR, TYPE, NAME, SUFFIX) \ |
| " /* " #ADDR " */ " #TYPE " " #NAME #SUFFIX ";\n" |
| auto* expect = "struct S {\n" ALL_FIELDS() "};\n"; |
| #undef FIELD |
| EXPECT_EQ(buf.String(), expect); |
| |
| // 1.4 Metal and C++14 |
| // The Metal programming language is a C++14-based Specification with |
| // extensions and restrictions. Refer to the C++14 Specification (also known |
| // as the ISO/IEC JTC1/SC22/WG21 N4431 Language Specification) for a detailed |
| // description of the language grammar. |
| // |
| // Tint is written in C++14, so use the compiler to verify the generated |
| // layout is as expected for C++14 / MSL. |
| { |
| struct S { |
| #define FIELD(ADDR, TYPE, NAME, SUFFIX) TYPE NAME SUFFIX; |
| ALL_FIELDS() |
| #undef FIELD |
| }; |
| |
| #define FIELD(ADDR, TYPE, NAME, SUFFIX) \ |
| EXPECT_EQ(ADDR, static_cast<int>(offsetof(S, NAME))) << "Field " << #NAME; |
| ALL_FIELDS() |
| #undef FIELD |
| } |
| #undef ALL_FIELDS |
| } |
| |
| TEST_F(MslGeneratorImplTest, EmitType_Struct_Layout_Structures) { |
| // inner_x: size(1024), align(512) |
| auto* inner_x = |
| Structure("inner_x", { |
| Member("a", ty.i32()), |
| Member("b", ty.f32(), {MemberAlign(512)}), |
| }); |
| |
| // inner_y: size(516), align(4) |
| auto* inner_y = |
| Structure("inner_y", { |
| Member("a", ty.i32(), {MemberSize(512)}), |
| Member("b", ty.f32()), |
| }); |
| |
| auto* s = Structure("S", |
| { |
| Member("a", ty.i32()), |
| Member("b", ty.Of(inner_x)), |
| Member("c", ty.f32()), |
| Member("d", ty.Of(inner_y)), |
| Member("e", ty.f32()), |
| }, |
| {create<ast::StructBlockDecoration>()}); |
| |
| Global("G", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead, |
| ast::DecorationList{ |
| create<ast::BindingDecoration>(0), |
| create<ast::GroupDecoration>(0), |
| }); |
| |
| GeneratorImpl& gen = Build(); |
| |
| TextGenerator::TextBuffer buf; |
| auto* sem_s = program->TypeOf(s)->As<sem::Struct>(); |
| ASSERT_TRUE(gen.EmitStructType(&buf, sem_s)) << gen.error(); |
| |
| // ALL_FIELDS() calls the macro FIELD(ADDR, TYPE, NAME, SUFFIX) |
| // for each field of the structure s. |
| #define ALL_FIELDS() \ |
| FIELD(0x0000, int, a, /*NO SUFFIX*/) \ |
| FIELD(0x0004, int8_t, tint_pad, [508]) \ |
| FIELD(0x0200, inner_x, b, /*NO SUFFIX*/) \ |
| FIELD(0x0600, float, c, /*NO SUFFIX*/) \ |
| FIELD(0x0604, inner_y, d, /*NO SUFFIX*/) \ |
| FIELD(0x0808, float, e, /*NO SUFFIX*/) \ |
| FIELD(0x080c, int8_t, tint_pad_1, [500]) |
| |
| // Check that the generated string is as expected. |
| #define FIELD(ADDR, TYPE, NAME, SUFFIX) \ |
| " /* " #ADDR " */ " #TYPE " " #NAME #SUFFIX ";\n" |
| auto* expect = "struct S {\n" ALL_FIELDS() "};\n"; |
| #undef FIELD |
| EXPECT_EQ(buf.String(), expect); |
| |
| // 1.4 Metal and C++14 |
| // The Metal programming language is a C++14-based Specification with |
| // extensions and restrictions. Refer to the C++14 Specification (also known |
| // as the ISO/IEC JTC1/SC22/WG21 N4431 Language Specification) for a detailed |
| // description of the language grammar. |
| // |
| // Tint is written in C++14, so use the compiler to verify the generated |
| // layout is as expected for C++14 / MSL. |
| { |
| struct inner_x { |
| uint32_t a; |
| alignas(512) float b; |
| }; |
| CHECK_TYPE_SIZE_AND_ALIGN(inner_x, 1024, 512); |
| |
| struct inner_y { |
| uint32_t a[128]; |
| float b; |
| }; |
| CHECK_TYPE_SIZE_AND_ALIGN(inner_y, 516, 4); |
| |
| struct S { |
| #define FIELD(ADDR, TYPE, NAME, SUFFIX) TYPE NAME SUFFIX; |
| ALL_FIELDS() |
| #undef FIELD |
| }; |
| |
| #define FIELD(ADDR, TYPE, NAME, SUFFIX) \ |
| EXPECT_EQ(ADDR, static_cast<int>(offsetof(S, NAME))) << "Field " << #NAME; |
| ALL_FIELDS() |
| #undef FIELD |
| } |
| |
| #undef ALL_FIELDS |
| } |
| |
| TEST_F(MslGeneratorImplTest, EmitType_Struct_Layout_ArrayDefaultStride) { |
| // inner: size(1024), align(512) |
| auto* inner = |
| Structure("inner", { |
| Member("a", ty.i32()), |
| Member("b", ty.f32(), {MemberAlign(512)}), |
| }); |
| |
| // array_x: size(28), align(4) |
| auto* array_x = ty.array<f32, 7>(); |
| |
| // array_y: size(4096), align(512) |
| auto* array_y = ty.array(ty.Of(inner), 4); |
| |
| // array_z: size(4), align(4) |
| auto* array_z = ty.array<f32>(); |
| |
| auto* s = |
| Structure("S", |
| { |
| Member("a", ty.i32()), |
| Member("b", array_x), |
| Member("c", ty.f32()), |
| Member("d", array_y), |
| Member("e", ty.f32()), |
| Member("f", array_z), |
| }, |
| ast::DecorationList{create<ast::StructBlockDecoration>()}); |
| |
| Global("G", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead, |
| ast::DecorationList{ |
| create<ast::BindingDecoration>(0), |
| create<ast::GroupDecoration>(0), |
| }); |
| |
| GeneratorImpl& gen = Build(); |
| |
| TextGenerator::TextBuffer buf; |
| auto* sem_s = program->TypeOf(s)->As<sem::Struct>(); |
| ASSERT_TRUE(gen.EmitStructType(&buf, sem_s)) << gen.error(); |
| |
| // ALL_FIELDS() calls the macro FIELD(ADDR, TYPE, NAME, SUFFIX) |
| // for each field of the structure s. |
| #define ALL_FIELDS() \ |
| FIELD(0x0000, int, a, /*NO SUFFIX*/) \ |
| FIELD(0x0004, float, b, [7]) \ |
| FIELD(0x0020, float, c, /*NO SUFFIX*/) \ |
| FIELD(0x0024, int8_t, tint_pad, [476]) \ |
| FIELD(0x0200, inner, d, [4]) \ |
| FIELD(0x1200, float, e, /*NO SUFFIX*/) \ |
| FIELD(0x1204, float, f, [1]) \ |
| FIELD(0x1208, int8_t, tint_pad_1, [504]) |
| |
| // Check that the generated string is as expected. |
| #define FIELD(ADDR, TYPE, NAME, SUFFIX) \ |
| " /* " #ADDR " */ " #TYPE " " #NAME #SUFFIX ";\n" |
| auto* expect = "struct S {\n" ALL_FIELDS() "};\n"; |
| #undef FIELD |
| EXPECT_EQ(buf.String(), expect); |
| |
| // 1.4 Metal and C++14 |
| // The Metal programming language is a C++14-based Specification with |
| // extensions and restrictions. Refer to the C++14 Specification (also known |
| // as the ISO/IEC JTC1/SC22/WG21 N4431 Language Specification) for a detailed |
| // description of the language grammar. |
| // |
| // Tint is written in C++14, so use the compiler to verify the generated |
| // layout is as expected for C++14 / MSL. |
| { |
| struct inner { |
| uint32_t a; |
| alignas(512) float b; |
| }; |
| CHECK_TYPE_SIZE_AND_ALIGN(inner, 1024, 512); |
| |
| // array_x: size(28), align(4) |
| using array_x = std::array<float, 7>; |
| CHECK_TYPE_SIZE_AND_ALIGN(array_x, 28, 4); |
| |
| // array_y: size(4096), align(512) |
| using array_y = std::array<inner, 4>; |
| CHECK_TYPE_SIZE_AND_ALIGN(array_y, 4096, 512); |
| |
| // array_z: size(4), align(4) |
| using array_z = std::array<float, 1>; |
| CHECK_TYPE_SIZE_AND_ALIGN(array_z, 4, 4); |
| |
| struct S { |
| #define FIELD(ADDR, TYPE, NAME, SUFFIX) TYPE NAME SUFFIX; |
| ALL_FIELDS() |
| #undef FIELD |
| }; |
| |
| #define FIELD(ADDR, TYPE, NAME, SUFFIX) \ |
| EXPECT_EQ(ADDR, static_cast<int>(offsetof(S, NAME))) << "Field " << #NAME; |
| ALL_FIELDS() |
| #undef FIELD |
| } |
| |
| #undef ALL_FIELDS |
| } |
| |
| TEST_F(MslGeneratorImplTest, EmitType_Struct_Layout_ArrayVec3DefaultStride) { |
| // array: size(64), align(16) |
| auto* array = ty.array(ty.vec3<f32>(), 4); |
| |
| auto* s = |
| Structure("S", |
| { |
| Member("a", ty.i32()), |
| Member("b", array), |
| Member("c", ty.i32()), |
| }, |
| ast::DecorationList{create<ast::StructBlockDecoration>()}); |
| |
| Global("G", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead, |
| ast::DecorationList{ |
| create<ast::BindingDecoration>(0), |
| create<ast::GroupDecoration>(0), |
| }); |
| |
| GeneratorImpl& gen = Build(); |
| |
| TextGenerator::TextBuffer buf; |
| auto* sem_s = program->TypeOf(s)->As<sem::Struct>(); |
| ASSERT_TRUE(gen.EmitStructType(&buf, sem_s)) << gen.error(); |
| |
| // ALL_FIELDS() calls the macro FIELD(ADDR, TYPE, NAME, SUFFIX) |
| // for each field of the structure s. |
| #define ALL_FIELDS() \ |
| FIELD(0x0000, int, a, /*NO SUFFIX*/) \ |
| FIELD(0x0004, int8_t, tint_pad, [12]) \ |
| FIELD(0x0010, float3, b, [4]) \ |
| FIELD(0x0050, int, c, /*NO SUFFIX*/) \ |
| FIELD(0x0054, int8_t, tint_pad_1, [12]) |
| |
| // Check that the generated string is as expected. |
| #define FIELD(ADDR, TYPE, NAME, SUFFIX) \ |
| " /* " #ADDR " */ " #TYPE " " #NAME #SUFFIX ";\n" |
| auto* expect = "struct S {\n" ALL_FIELDS() "};\n"; |
| #undef FIELD |
| EXPECT_EQ(buf.String(), expect); |
| } |
| |
| TEST_F(MslGeneratorImplTest, AttemptTintPadSymbolCollision) { |
| auto* s = Structure( |
| "S", |
| { |
| // uses symbols tint_pad_[0..9] and tint_pad_[20..35] |
| Member("tint_pad_2", ty.i32(), {MemberSize(32)}), |
| Member("tint_pad_20", ty.f32(), {MemberAlign(128), MemberSize(128)}), |
| Member("tint_pad_33", ty.vec2<f32>()), |
| Member("tint_pad_1", ty.u32()), |
| Member("tint_pad_3", ty.vec3<f32>()), |
| Member("tint_pad_7", ty.u32()), |
| Member("tint_pad_25", ty.vec4<f32>()), |
| Member("tint_pad_5", ty.u32()), |
| Member("tint_pad_27", ty.mat2x2<f32>()), |
| Member("tint_pad_24", ty.u32()), |
| Member("tint_pad_23", ty.mat2x3<f32>()), |
| Member("tint_pad", ty.u32()), |
| Member("tint_pad_8", ty.mat2x4<f32>()), |
| Member("tint_pad_26", ty.u32()), |
| Member("tint_pad_29", ty.mat3x2<f32>()), |
| Member("tint_pad_6", ty.u32()), |
| Member("tint_pad_22", ty.mat3x3<f32>()), |
| Member("tint_pad_32", ty.u32()), |
| Member("tint_pad_34", ty.mat3x4<f32>()), |
| Member("tint_pad_35", ty.u32()), |
| Member("tint_pad_30", ty.mat4x2<f32>()), |
| Member("tint_pad_9", ty.u32()), |
| Member("tint_pad_31", ty.mat4x3<f32>()), |
| Member("tint_pad_28", ty.u32()), |
| Member("tint_pad_4", ty.mat4x4<f32>()), |
| Member("tint_pad_21", ty.f32()), |
| }, |
| {create<ast::StructBlockDecoration>()}); |
| |
| Global("G", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead, |
| ast::DecorationList{ |
| create<ast::BindingDecoration>(0), |
| create<ast::GroupDecoration>(0), |
| }); |
| |
| GeneratorImpl& gen = Build(); |
| |
| TextGenerator::TextBuffer buf; |
| auto* sem_s = program->TypeOf(s)->As<sem::Struct>(); |
| ASSERT_TRUE(gen.EmitStructType(&buf, sem_s)) << gen.error(); |
| EXPECT_EQ(buf.String(), R"(struct S { |
| /* 0x0000 */ int tint_pad_2; |
| /* 0x0004 */ int8_t tint_pad_10[124]; |
| /* 0x0080 */ float tint_pad_20; |
| /* 0x0084 */ int8_t tint_pad_11[124]; |
| /* 0x0100 */ float2 tint_pad_33; |
| /* 0x0108 */ uint tint_pad_1; |
| /* 0x010c */ int8_t tint_pad_12[4]; |
| /* 0x0110 */ packed_float3 tint_pad_3; |
| /* 0x011c */ uint tint_pad_7; |
| /* 0x0120 */ float4 tint_pad_25; |
| /* 0x0130 */ uint tint_pad_5; |
| /* 0x0134 */ int8_t tint_pad_13[4]; |
| /* 0x0138 */ float2x2 tint_pad_27; |
| /* 0x0148 */ uint tint_pad_24; |
| /* 0x014c */ int8_t tint_pad_14[4]; |
| /* 0x0150 */ float2x3 tint_pad_23; |
| /* 0x0170 */ uint tint_pad; |
| /* 0x0174 */ int8_t tint_pad_15[12]; |
| /* 0x0180 */ float2x4 tint_pad_8; |
| /* 0x01a0 */ uint tint_pad_26; |
| /* 0x01a4 */ int8_t tint_pad_16[4]; |
| /* 0x01a8 */ float3x2 tint_pad_29; |
| /* 0x01c0 */ uint tint_pad_6; |
| /* 0x01c4 */ int8_t tint_pad_17[12]; |
| /* 0x01d0 */ float3x3 tint_pad_22; |
| /* 0x0200 */ uint tint_pad_32; |
| /* 0x0204 */ int8_t tint_pad_18[12]; |
| /* 0x0210 */ float3x4 tint_pad_34; |
| /* 0x0240 */ uint tint_pad_35; |
| /* 0x0244 */ int8_t tint_pad_19[4]; |
| /* 0x0248 */ float4x2 tint_pad_30; |
| /* 0x0268 */ uint tint_pad_9; |
| /* 0x026c */ int8_t tint_pad_36[4]; |
| /* 0x0270 */ float4x3 tint_pad_31; |
| /* 0x02b0 */ uint tint_pad_28; |
| /* 0x02b4 */ int8_t tint_pad_37[12]; |
| /* 0x02c0 */ float4x4 tint_pad_4; |
| /* 0x0300 */ float tint_pad_21; |
| /* 0x0304 */ int8_t tint_pad_38[124]; |
| }; |
| )"); |
| } |
| |
| // TODO(dsinclair): How to translate [[block]] |
| TEST_F(MslGeneratorImplTest, DISABLED_EmitType_Struct_WithDecoration) { |
| auto* s = Structure("S", |
| { |
| Member("a", ty.i32()), |
| Member("b", ty.f32()), |
| }, |
| {create<ast::StructBlockDecoration>()}); |
| |
| Global("G", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead, |
| ast::DecorationList{ |
| create<ast::BindingDecoration>(0), |
| create<ast::GroupDecoration>(0), |
| }); |
| |
| GeneratorImpl& gen = Build(); |
| |
| std::stringstream out; |
| ASSERT_TRUE(gen.EmitType(out, program->TypeOf(s), "")) << gen.error(); |
| EXPECT_EQ(out.str(), R"(struct { |
| /* 0x0000 */ int a; |
| /* 0x0004 */ float b; |
| })"); |
| } |
| |
| TEST_F(MslGeneratorImplTest, EmitType_U32) { |
| auto* u32 = create<sem::U32>(); |
| |
| GeneratorImpl& gen = Build(); |
| |
| std::stringstream out; |
| ASSERT_TRUE(gen.EmitType(out, u32, "")) << gen.error(); |
| EXPECT_EQ(out.str(), "uint"); |
| } |
| |
| TEST_F(MslGeneratorImplTest, EmitType_Vector) { |
| auto* f32 = create<sem::F32>(); |
| auto* vec3 = create<sem::Vector>(f32, 3); |
| |
| GeneratorImpl& gen = Build(); |
| |
| std::stringstream out; |
| ASSERT_TRUE(gen.EmitType(out, vec3, "")) << gen.error(); |
| EXPECT_EQ(out.str(), "float3"); |
| } |
| |
| TEST_F(MslGeneratorImplTest, EmitType_Void) { |
| auto* void_ = create<sem::Void>(); |
| |
| GeneratorImpl& gen = Build(); |
| |
| std::stringstream out; |
| ASSERT_TRUE(gen.EmitType(out, void_, "")) << gen.error(); |
| EXPECT_EQ(out.str(), "void"); |
| } |
| |
| TEST_F(MslGeneratorImplTest, EmitType_Sampler) { |
| auto* sampler = create<sem::Sampler>(ast::SamplerKind::kSampler); |
| |
| GeneratorImpl& gen = Build(); |
| |
| std::stringstream out; |
| ASSERT_TRUE(gen.EmitType(out, sampler, "")) << gen.error(); |
| EXPECT_EQ(out.str(), "sampler"); |
| } |
| |
| TEST_F(MslGeneratorImplTest, EmitType_SamplerComparison) { |
| auto* sampler = create<sem::Sampler>(ast::SamplerKind::kComparisonSampler); |
| |
| GeneratorImpl& gen = Build(); |
| |
| std::stringstream out; |
| ASSERT_TRUE(gen.EmitType(out, sampler, "")) << gen.error(); |
| EXPECT_EQ(out.str(), "sampler"); |
| } |
| |
| struct MslDepthTextureData { |
| ast::TextureDimension dim; |
| std::string result; |
| }; |
| inline std::ostream& operator<<(std::ostream& out, MslDepthTextureData data) { |
| out << data.dim; |
| return out; |
| } |
| using MslDepthTexturesTest = TestParamHelper<MslDepthTextureData>; |
| TEST_P(MslDepthTexturesTest, Emit) { |
| auto params = GetParam(); |
| |
| sem::DepthTexture s(params.dim); |
| |
| GeneratorImpl& gen = Build(); |
| |
| std::stringstream out; |
| ASSERT_TRUE(gen.EmitType(out, &s, "")) << gen.error(); |
| EXPECT_EQ(out.str(), params.result); |
| } |
| INSTANTIATE_TEST_SUITE_P( |
| MslGeneratorImplTest, |
| MslDepthTexturesTest, |
| testing::Values(MslDepthTextureData{ast::TextureDimension::k2d, |
| "depth2d<float, access::sample>"}, |
| MslDepthTextureData{ast::TextureDimension::k2dArray, |
| "depth2d_array<float, access::sample>"}, |
| MslDepthTextureData{ast::TextureDimension::kCube, |
| "depthcube<float, access::sample>"}, |
| MslDepthTextureData{ |
| ast::TextureDimension::kCubeArray, |
| "depthcube_array<float, access::sample>"})); |
| |
| using MslDepthMultisampledTexturesTest = TestHelper; |
| TEST_F(MslDepthMultisampledTexturesTest, Emit) { |
| sem::DepthMultisampledTexture s(ast::TextureDimension::k2d); |
| |
| GeneratorImpl& gen = Build(); |
| |
| std::stringstream out; |
| ASSERT_TRUE(gen.EmitType(out, &s, "")) << gen.error(); |
| EXPECT_EQ(out.str(), "depth2d_ms<float, access::read>"); |
| } |
| |
| struct MslTextureData { |
| ast::TextureDimension dim; |
| std::string result; |
| }; |
| inline std::ostream& operator<<(std::ostream& out, MslTextureData data) { |
| out << data.dim; |
| return out; |
| } |
| using MslSampledtexturesTest = TestParamHelper<MslTextureData>; |
| TEST_P(MslSampledtexturesTest, Emit) { |
| auto params = GetParam(); |
| |
| auto* f32 = create<sem::F32>(); |
| auto* s = create<sem::SampledTexture>(params.dim, f32); |
| |
| GeneratorImpl& gen = Build(); |
| |
| std::stringstream out; |
| ASSERT_TRUE(gen.EmitType(out, s, "")) << gen.error(); |
| EXPECT_EQ(out.str(), params.result); |
| } |
| INSTANTIATE_TEST_SUITE_P( |
| MslGeneratorImplTest, |
| MslSampledtexturesTest, |
| testing::Values(MslTextureData{ast::TextureDimension::k1d, |
| "texture1d<float, access::sample>"}, |
| MslTextureData{ast::TextureDimension::k2d, |
| "texture2d<float, access::sample>"}, |
| MslTextureData{ast::TextureDimension::k2dArray, |
| "texture2d_array<float, access::sample>"}, |
| MslTextureData{ast::TextureDimension::k3d, |
| "texture3d<float, access::sample>"}, |
| MslTextureData{ast::TextureDimension::kCube, |
| "texturecube<float, access::sample>"}, |
| MslTextureData{ |
| ast::TextureDimension::kCubeArray, |
| "texturecube_array<float, access::sample>"})); |
| |
| TEST_F(MslGeneratorImplTest, Emit_TypeMultisampledTexture) { |
| auto* u32 = create<sem::U32>(); |
| auto* ms = create<sem::MultisampledTexture>(ast::TextureDimension::k2d, u32); |
| |
| GeneratorImpl& gen = Build(); |
| |
| std::stringstream out; |
| ASSERT_TRUE(gen.EmitType(out, ms, "")) << gen.error(); |
| EXPECT_EQ(out.str(), "texture2d_ms<uint, access::read>"); |
| } |
| |
| struct MslStorageTextureData { |
| ast::TextureDimension dim; |
| std::string result; |
| }; |
| inline std::ostream& operator<<(std::ostream& out, MslStorageTextureData data) { |
| return out << data.dim; |
| } |
| using MslStorageTexturesTest = TestParamHelper<MslStorageTextureData>; |
| TEST_P(MslStorageTexturesTest, Emit) { |
| auto params = GetParam(); |
| |
| auto* s = ty.storage_texture(params.dim, ast::TexelFormat::kR32Float, |
| ast::Access::kWrite); |
| Global("test_var", s, |
| ast::DecorationList{ |
| create<ast::BindingDecoration>(0), |
| create<ast::GroupDecoration>(0), |
| }); |
| |
| GeneratorImpl& gen = Build(); |
| |
| std::stringstream out; |
| ASSERT_TRUE(gen.EmitType(out, program->TypeOf(s), "")) << gen.error(); |
| EXPECT_EQ(out.str(), params.result); |
| } |
| INSTANTIATE_TEST_SUITE_P( |
| MslGeneratorImplTest, |
| MslStorageTexturesTest, |
| testing::Values(MslStorageTextureData{ast::TextureDimension::k1d, |
| "texture1d<float, access::write>"}, |
| MslStorageTextureData{ast::TextureDimension::k2d, |
| "texture2d<float, access::write>"}, |
| MslStorageTextureData{ |
| ast::TextureDimension::k2dArray, |
| "texture2d_array<float, access::write>"}, |
| MslStorageTextureData{ast::TextureDimension::k3d, |
| "texture3d<float, access::write>"})); |
| |
| } // namespace |
| } // namespace msl |
| } // namespace writer |
| } // namespace tint |