From a28d19b18ddf29951108878a0d1ad5609fa7a4dc Mon Sep 17 00:00:00 2001 From: Ryan Harrison <rharrison@chromium.org> Date: Tue, 3 Nov 2020 16:53:59 +0000 Subject: [PATCH] Add tests for MinBufferBindingSize BUG=tint:283 Change-Id: Id198175d232680ca89e04561b8b7510f41d982f7 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/31106 Commit-Queue: Ryan Harrison <rharrison@chromium.org> Reviewed-by: dan sinclair <dsinclair@chromium.org> --- src/ast/type/access_control_type.cc | 9 +- src/ast/type/access_control_type.h | 10 +- src/ast/type/access_control_type_test.cc | 108 ++++++ src/ast/type/alias_type.cc | 8 +- src/ast/type/alias_type.h | 8 +- src/ast/type/alias_type_test.cc | 110 ++++++ src/ast/type/array_type.cc | 38 +- src/ast/type/array_type.h | 8 +- src/ast/type/array_type_test.cc | 48 +++ src/ast/type/bool_type_test.cc | 5 + src/ast/type/depth_texture_type_test.cc | 5 + src/ast/type/f32_type.cc | 6 +- src/ast/type/f32_type.h | 8 +- src/ast/type/f32_type_test.cc | 10 + src/ast/type/i32_type.cc | 6 +- src/ast/type/i32_type.h | 8 +- src/ast/type/i32_type_test.cc | 10 + src/ast/type/matrix_type.cc | 15 +- src/ast/type/matrix_type.h | 8 +- src/ast/type/matrix_type_test.cc | 56 +++ .../type/multisampled_texture_type_test.cc | 6 + src/ast/type/pointer_type.cc | 4 - src/ast/type/pointer_type.h | 4 - src/ast/type/sampled_texture_type_test.cc | 6 + src/ast/type/sampler_type_test.cc | 5 + src/ast/type/storage_texture_type_test.cc | 6 + src/ast/type/struct_type.cc | 35 +- src/ast/type/struct_type.h | 10 +- src/ast/type/struct_type_test.cc | 332 ++++++++++++++++++ src/ast/type/type.cc | 6 +- src/ast/type/type.h | 11 +- src/ast/type/u32_type.cc | 6 +- src/ast/type/u32_type.h | 8 +- src/ast/type/u32_type_test.cc | 10 + src/ast/type/vector_type.cc | 15 +- src/ast/type/vector_type.h | 8 +- src/ast/type/vector_type_test.cc | 36 ++ src/inspector/inspector.cc | 7 +- src/inspector/inspector_test.cc | 16 +- 39 files changed, 966 insertions(+), 49 deletions(-) diff --git a/src/ast/type/access_control_type.cc b/src/ast/type/access_control_type.cc index 1abae24d70..7bce6fefd1 100644 --- a/src/ast/type/access_control_type.cc +++ b/src/ast/type/access_control_type.cc @@ -48,8 +48,13 @@ std::string AccessControlType::type_name() const { return name + subtype_->type_name(); } -uint64_t AccessControlType::MinBufferBindingSize() const { - return subtype_->MinBufferBindingSize(); +uint64_t AccessControlType::MinBufferBindingSize( + MemoryLayout mem_layout) const { + return subtype_->MinBufferBindingSize(mem_layout); +} + +uint64_t AccessControlType::BaseAlignment(MemoryLayout mem_layout) const { + return subtype_->BaseAlignment(mem_layout); } } // namespace type diff --git a/src/ast/type/access_control_type.h b/src/ast/type/access_control_type.h index 146a80aa77..35a2f96912 100644 --- a/src/ast/type/access_control_type.h +++ b/src/ast/type/access_control_type.h @@ -53,9 +53,15 @@ class AccessControlType : public Type { /// @returns the name for this type std::string type_name() const override; + /// @param mem_layout type of memory layout to use in calculation. /// @returns minimum size required for this type, in bytes. - /// 0 for non-host shareable types. - uint64_t MinBufferBindingSize() const override; + /// 0 for non-host shareable types. + uint64_t MinBufferBindingSize(MemoryLayout mem_layout) const override; + + /// @param mem_layout type of memory layout to use in calculation. + /// @returns base alignment for the type, in bytes. + /// 0 for non-host shareable types. + uint64_t BaseAlignment(MemoryLayout mem_layout) const override; private: AccessControl access_ = AccessControl::kReadOnly; diff --git a/src/ast/type/access_control_type_test.cc b/src/ast/type/access_control_type_test.cc index 0f31121d0b..cfb3eb0d62 100644 --- a/src/ast/type/access_control_type_test.cc +++ b/src/ast/type/access_control_type_test.cc @@ -16,8 +16,14 @@ #include "gtest/gtest.h" #include "src/ast/storage_class.h" +#include "src/ast/stride_decoration.h" +#include "src/ast/struct_member.h" +#include "src/ast/struct_member_decoration.h" +#include "src/ast/struct_member_offset_decoration.h" +#include "src/ast/type/array_type.h" #include "src/ast/type/i32_type.h" #include "src/ast/type/pointer_type.h" +#include "src/ast/type/struct_type.h" #include "src/ast/type/u32_type.h" namespace tint { @@ -83,6 +89,108 @@ TEST_F(AccessControlTypeTest, AccessReadWrite) { EXPECT_EQ(at.type_name(), "__access_control_read_write__i32"); } +TEST_F(AccessControlTypeTest, MinBufferBindingSizeU32) { + U32Type u32; + AccessControlType at{AccessControl::kReadOnly, &u32}; + EXPECT_EQ(4u, at.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); +} + +TEST_F(AccessControlTypeTest, MinBufferBindingSizeArray) { + U32Type u32; + ArrayType array(&u32, 4); + ArrayDecorationList decos; + decos.push_back(std::make_unique<StrideDecoration>(4)); + array.set_decorations(std::move(decos)); + AccessControlType at{AccessControl::kReadOnly, &array}; + EXPECT_EQ(16u, at.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); +} + +TEST_F(AccessControlTypeTest, MinBufferBindingSizeRuntimeArray) { + U32Type u32; + ArrayType array(&u32); + ArrayDecorationList decos; + decos.push_back(std::make_unique<StrideDecoration>(4)); + array.set_decorations(std::move(decos)); + AccessControlType at{AccessControl::kReadOnly, &array}; + EXPECT_EQ(4u, at.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); +} + +TEST_F(AccessControlTypeTest, MinBufferBindingSizeStruct) { + U32Type u32; + StructMemberList members; + + StructMemberDecorationList deco; + deco.push_back(std::make_unique<StructMemberOffsetDecoration>(0)); + members.push_back( + std::make_unique<StructMember>("foo", &u32, std::move(deco))); + + deco = StructMemberDecorationList(); + deco.push_back(std::make_unique<StructMemberOffsetDecoration>(4)); + members.push_back( + std::make_unique<StructMember>("bar", &u32, std::move(deco))); + + ast::StructDecorationList decos; + + auto str = + std::make_unique<ast::Struct>(std::move(decos), std::move(members)); + StructType struct_type("struct_type", std::move(str)); + AccessControlType at{AccessControl::kReadOnly, &struct_type}; + EXPECT_EQ(16u, at.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); + EXPECT_EQ(8u, at.MinBufferBindingSize(MemoryLayout::kStorageBuffer)); +} + +TEST_F(AccessControlTypeTest, BaseAlignmentU32) { + U32Type u32; + AccessControlType at{AccessControl::kReadOnly, &u32}; + EXPECT_EQ(4u, at.BaseAlignment(MemoryLayout::kUniformBuffer)); +} + +TEST_F(AccessControlTypeTest, BaseAlignmentArray) { + U32Type u32; + ArrayType array(&u32, 4); + ArrayDecorationList decos; + decos.push_back(std::make_unique<StrideDecoration>(4)); + array.set_decorations(std::move(decos)); + AccessControlType at{AccessControl::kReadOnly, &array}; + EXPECT_EQ(16u, at.BaseAlignment(MemoryLayout::kUniformBuffer)); +} + +TEST_F(AccessControlTypeTest, BaseAlignmentRuntimeArray) { + U32Type u32; + ArrayType array(&u32); + ArrayDecorationList decos; + decos.push_back(std::make_unique<StrideDecoration>(4)); + array.set_decorations(std::move(decos)); + AccessControlType at{AccessControl::kReadOnly, &array}; + EXPECT_EQ(16u, at.BaseAlignment(MemoryLayout::kUniformBuffer)); +} + +TEST_F(AccessControlTypeTest, BaseAlignmentStruct) { + U32Type u32; + StructMemberList members; + + { + StructMemberDecorationList deco; + deco.push_back(std::make_unique<StructMemberOffsetDecoration>(0)); + members.push_back( + std::make_unique<StructMember>("foo", &u32, std::move(deco))); + } + { + StructMemberDecorationList deco; + deco.push_back(std::make_unique<StructMemberOffsetDecoration>(4)); + members.push_back( + std::make_unique<StructMember>("bar", &u32, std::move(deco))); + } + ast::StructDecorationList decos; + + auto str = + std::make_unique<ast::Struct>(std::move(decos), std::move(members)); + StructType struct_type("struct_type", std::move(str)); + AccessControlType at{AccessControl::kReadOnly, &struct_type}; + EXPECT_EQ(16u, at.BaseAlignment(MemoryLayout::kUniformBuffer)); + EXPECT_EQ(4u, at.BaseAlignment(MemoryLayout::kStorageBuffer)); +} + } // namespace } // namespace type } // namespace ast diff --git a/src/ast/type/alias_type.cc b/src/ast/type/alias_type.cc index d8f67d9340..a4b6ac7947 100644 --- a/src/ast/type/alias_type.cc +++ b/src/ast/type/alias_type.cc @@ -35,8 +35,12 @@ std::string AliasType::type_name() const { return "__alias_" + name_ + subtype_->type_name(); } -uint64_t AliasType::MinBufferBindingSize() const { - return subtype_->MinBufferBindingSize(); +uint64_t AliasType::MinBufferBindingSize(MemoryLayout mem_layout) const { + return subtype_->MinBufferBindingSize(mem_layout); +} + +uint64_t AliasType::BaseAlignment(MemoryLayout mem_layout) const { + return subtype_->BaseAlignment(mem_layout); } } // namespace type diff --git a/src/ast/type/alias_type.h b/src/ast/type/alias_type.h index bc7505a745..38923baf47 100644 --- a/src/ast/type/alias_type.h +++ b/src/ast/type/alias_type.h @@ -45,9 +45,15 @@ class AliasType : public Type { /// @returns the name for this type std::string type_name() const override; + /// @param mem_layout type of memory layout to use in calculation. /// @returns minimum size required for this type, in bytes. /// 0 for non-host shareable types. - uint64_t MinBufferBindingSize() const override; + uint64_t MinBufferBindingSize(MemoryLayout mem_layout) const override; + + /// @param mem_layout type of memory layout to use in calculation. + /// @returns base alignment for the type, in bytes. + /// 0 for non-host shareable types. + uint64_t BaseAlignment(MemoryLayout mem_layout) const override; private: std::string name_; diff --git a/src/ast/type/alias_type_test.cc b/src/ast/type/alias_type_test.cc index 22b549ebe9..1b5dbc0a94 100644 --- a/src/ast/type/alias_type_test.cc +++ b/src/ast/type/alias_type_test.cc @@ -16,9 +16,15 @@ #include "gtest/gtest.h" #include "src/ast/storage_class.h" +#include "src/ast/stride_decoration.h" +#include "src/ast/struct_member.h" +#include "src/ast/struct_member_decoration.h" +#include "src/ast/struct_member_offset_decoration.h" #include "src/ast/type/access_control_type.h" +#include "src/ast/type/array_type.h" #include "src/ast/type/i32_type.h" #include "src/ast/type/pointer_type.h" +#include "src/ast/type/struct_type.h" #include "src/ast/type/u32_type.h" namespace tint { @@ -146,6 +152,110 @@ TEST_F(AliasTypeTest, UnwrapAll_PointerAccessControl) { EXPECT_EQ(u32.UnwrapAll(), &u32); } +TEST_F(AliasTypeTest, MinBufferBindingSizeU32) { + U32Type u32; + AliasType alias{"alias", &u32}; + EXPECT_EQ(4u, alias.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); +} + +TEST_F(AliasTypeTest, MinBufferBindingSizeArray) { + U32Type u32; + ArrayType array(&u32, 4); + ArrayDecorationList decos; + decos.push_back(std::make_unique<StrideDecoration>(4)); + array.set_decorations(std::move(decos)); + AliasType alias{"alias", &array}; + EXPECT_EQ(16u, alias.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); +} + +TEST_F(AliasTypeTest, MinBufferBindingSizeRuntimeArray) { + U32Type u32; + ArrayType array(&u32); + ArrayDecorationList decos; + decos.push_back(std::make_unique<StrideDecoration>(4)); + array.set_decorations(std::move(decos)); + AliasType alias{"alias", &array}; + EXPECT_EQ(4u, alias.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); +} + +TEST_F(AliasTypeTest, MinBufferBindingSizeStruct) { + U32Type u32; + StructMemberList members; + + { + StructMemberDecorationList deco; + deco.push_back(std::make_unique<StructMemberOffsetDecoration>(0)); + members.push_back( + std::make_unique<StructMember>("foo", &u32, std::move(deco))); + } + { + StructMemberDecorationList deco; + deco.push_back(std::make_unique<StructMemberOffsetDecoration>(4)); + members.push_back( + std::make_unique<StructMember>("bar", &u32, std::move(deco))); + } + ast::StructDecorationList decos; + + auto str = + std::make_unique<ast::Struct>(std::move(decos), std::move(members)); + StructType struct_type("struct_type", std::move(str)); + AliasType alias{"alias", &struct_type}; + EXPECT_EQ(16u, alias.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); + EXPECT_EQ(8u, alias.MinBufferBindingSize(MemoryLayout::kStorageBuffer)); +} + +TEST_F(AliasTypeTest, BaseAlignmentU32) { + U32Type u32; + AliasType alias{"alias", &u32}; + EXPECT_EQ(4u, alias.BaseAlignment(MemoryLayout::kUniformBuffer)); +} + +TEST_F(AliasTypeTest, BaseAlignmentArray) { + U32Type u32; + ArrayType array(&u32, 4); + ArrayDecorationList decos; + decos.push_back(std::make_unique<StrideDecoration>(4)); + array.set_decorations(std::move(decos)); + AliasType alias{"alias", &array}; + EXPECT_EQ(16u, alias.BaseAlignment(MemoryLayout::kUniformBuffer)); +} + +TEST_F(AliasTypeTest, BaseAlignmentRuntimeArray) { + U32Type u32; + ArrayType array(&u32); + ArrayDecorationList decos; + decos.push_back(std::make_unique<StrideDecoration>(4)); + array.set_decorations(std::move(decos)); + AliasType alias{"alias", &array}; + EXPECT_EQ(16u, alias.BaseAlignment(MemoryLayout::kUniformBuffer)); +} + +TEST_F(AliasTypeTest, BaseAlignmentStruct) { + U32Type u32; + StructMemberList members; + + { + StructMemberDecorationList deco; + deco.push_back(std::make_unique<StructMemberOffsetDecoration>(0)); + members.push_back( + std::make_unique<StructMember>("foo", &u32, std::move(deco))); + } + { + StructMemberDecorationList deco; + deco.push_back(std::make_unique<StructMemberOffsetDecoration>(4)); + members.push_back( + std::make_unique<StructMember>("bar", &u32, std::move(deco))); + } + ast::StructDecorationList decos; + + auto str = + std::make_unique<ast::Struct>(std::move(decos), std::move(members)); + StructType struct_type("struct_type", std::move(str)); + AliasType alias{"alias", &struct_type}; + EXPECT_EQ(16u, alias.BaseAlignment(MemoryLayout::kUniformBuffer)); + EXPECT_EQ(4u, alias.BaseAlignment(MemoryLayout::kStorageBuffer)); +} + } // namespace } // namespace type } // namespace ast diff --git a/src/ast/type/array_type.cc b/src/ast/type/array_type.cc index 776ac0764e..8a497d8985 100644 --- a/src/ast/type/array_type.cc +++ b/src/ast/type/array_type.cc @@ -14,7 +14,10 @@ #include "src/ast/type/array_type.h" +#include <cmath> + #include "src/ast/stride_decoration.h" +#include "src/ast/type/vector_type.h" namespace tint { namespace ast { @@ -33,16 +36,35 @@ bool ArrayType::IsArray() const { return true; } -uint64_t ArrayType::MinBufferBindingSize() const { - // RTAs have a size_ = 0, but the value that is wanted from this call is the - // minimum size, so assuming atleast 1 element in the RTA. - uint32_t size = IsRuntimeArray() ? 1 : size_; - - if (has_array_stride()) { - return size * array_stride(); +uint64_t ArrayType::MinBufferBindingSize(MemoryLayout mem_layout) const { + if (!has_array_stride()) { + // Arrays in buffers are required to have a stride. + return 0; } - return size * type()->MinBufferBindingSize(); + if (IsRuntimeArray()) { + // WebGPU spec 10.1.2: + // If the last field of the corresponding structure defined in the shader + // has an unbounded array type, then the value of minBufferBindingSize must + // be greater than or equal to the byte offset of that field plus the stride + // of the unbounded array + return array_stride(); + } else { + // Not including the padding for the last element + return (size_ - 1) * array_stride() + + subtype_->MinBufferBindingSize(mem_layout); + } +} + +uint64_t ArrayType::BaseAlignment(MemoryLayout mem_layout) const { + if (mem_layout == MemoryLayout::kUniformBuffer) { + float aligment = 16; // for a vec4 + float unaligned = subtype_->BaseAlignment(mem_layout); + return aligment * std::ceil(unaligned / aligment); + } else if (mem_layout == MemoryLayout::kStorageBuffer) { + return subtype_->BaseAlignment(mem_layout); + } + return 0; } uint32_t ArrayType::array_stride() const { diff --git a/src/ast/type/array_type.h b/src/ast/type/array_type.h index aaea0585ed..042ae8cb05 100644 --- a/src/ast/type/array_type.h +++ b/src/ast/type/array_type.h @@ -47,9 +47,15 @@ class ArrayType : public Type { /// i.e. the size is determined at runtime bool IsRuntimeArray() const { return size_ == 0; } + /// @param mem_layout type of memory layout to use in calculation. /// @returns minimum size required for this type, in bytes. /// 0 for non-host shareable types. - uint64_t MinBufferBindingSize() const override; + uint64_t MinBufferBindingSize(MemoryLayout mem_layout) const override; + + /// @param mem_layout type of memory layout to use in calculation. + /// @returns base alignment for the type, in bytes. + /// 0 for non-host shareable types. + uint64_t BaseAlignment(MemoryLayout mem_layout) const override; /// Sets the array decorations /// @param decos the decorations to set diff --git a/src/ast/type/array_type_test.cc b/src/ast/type/array_type_test.cc index 2f128a4a75..33cd00e740 100644 --- a/src/ast/type/array_type_test.cc +++ b/src/ast/type/array_type_test.cc @@ -88,6 +88,54 @@ TEST_F(ArrayTypeTest, TypeName_WithStride) { EXPECT_EQ(arr.type_name(), "__array__i32_3_stride_16"); } +TEST_F(ArrayTypeTest, MinBufferBindingSizeNoStride) { + U32Type u32; + ArrayType arr(&u32, 4); + EXPECT_EQ(0u, arr.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); +} + +TEST_F(ArrayTypeTest, MinBufferBindingSizeArray) { + U32Type u32; + ArrayDecorationList decos; + decos.push_back(std::make_unique<StrideDecoration>(4)); + + ArrayType arr(&u32, 4); + arr.set_decorations(std::move(decos)); + EXPECT_EQ(16u, arr.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); +} + +TEST_F(ArrayTypeTest, MinBufferBindingSizeRuntimeArray) { + U32Type u32; + ArrayDecorationList decos; + decos.push_back(std::make_unique<StrideDecoration>(4)); + + ArrayType arr(&u32); + arr.set_decorations(std::move(decos)); + EXPECT_EQ(4u, arr.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); +} + +TEST_F(ArrayTypeTest, BaseAlignmentArray) { + U32Type u32; + ArrayDecorationList decos; + decos.push_back(std::make_unique<StrideDecoration>(4)); + + ArrayType arr(&u32, 4); + arr.set_decorations(std::move(decos)); + EXPECT_EQ(16u, arr.BaseAlignment(MemoryLayout::kUniformBuffer)); + EXPECT_EQ(4u, arr.BaseAlignment(MemoryLayout::kStorageBuffer)); +} + +TEST_F(ArrayTypeTest, BaseAlignmentRuntimeArray) { + U32Type u32; + ArrayDecorationList decos; + decos.push_back(std::make_unique<StrideDecoration>(4)); + + ArrayType arr(&u32); + arr.set_decorations(std::move(decos)); + EXPECT_EQ(16u, arr.BaseAlignment(MemoryLayout::kUniformBuffer)); + EXPECT_EQ(4u, arr.BaseAlignment(MemoryLayout::kStorageBuffer)); +} + } // namespace } // namespace type } // namespace ast diff --git a/src/ast/type/bool_type_test.cc b/src/ast/type/bool_type_test.cc index 4bcf4821da..f069db871d 100644 --- a/src/ast/type/bool_type_test.cc +++ b/src/ast/type/bool_type_test.cc @@ -45,6 +45,11 @@ TEST_F(BoolTypeTest, TypeName) { EXPECT_EQ(b.type_name(), "__bool"); } +TEST_F(BoolTypeTest, MinBufferBindingSize) { + BoolType b; + EXPECT_EQ(0u, b.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); +} + } // namespace } // namespace type } // namespace ast diff --git a/src/ast/type/depth_texture_type_test.cc b/src/ast/type/depth_texture_type_test.cc index e8b5d59937..d0cd6cfb3a 100644 --- a/src/ast/type/depth_texture_type_test.cc +++ b/src/ast/type/depth_texture_type_test.cc @@ -57,6 +57,11 @@ TEST_F(DepthTextureTypeTest, TypeName) { EXPECT_EQ(d.type_name(), "__depth_texture_cube"); } +TEST_F(DepthTextureTypeTest, MinBufferBindingSize) { + DepthTextureType d(TextureDimension::kCube); + EXPECT_EQ(0u, d.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); +} + } // namespace } // namespace type } // namespace ast diff --git a/src/ast/type/f32_type.cc b/src/ast/type/f32_type.cc index 2306bd03bf..df487324a9 100644 --- a/src/ast/type/f32_type.cc +++ b/src/ast/type/f32_type.cc @@ -30,7 +30,11 @@ std::string F32Type::type_name() const { return "__f32"; } -uint64_t F32Type::MinBufferBindingSize() const { +uint64_t F32Type::MinBufferBindingSize(MemoryLayout) const { + return 4; +} + +uint64_t F32Type::BaseAlignment(MemoryLayout) const { return 4; } diff --git a/src/ast/type/f32_type.h b/src/ast/type/f32_type.h index 8a0a5b64e7..0da82ddcd4 100644 --- a/src/ast/type/f32_type.h +++ b/src/ast/type/f32_type.h @@ -38,9 +38,15 @@ class F32Type : public Type { /// @returns the name for this type std::string type_name() const override; + /// @param mem_layout type of memory layout to use in calculation. /// @returns minimum size required for this type, in bytes. /// 0 for non-host shareable types. - uint64_t MinBufferBindingSize() const override; + uint64_t MinBufferBindingSize(MemoryLayout mem_layout) const override; + + /// @param mem_layout type of memory layout to use in calculation. + /// @returns base alignment for the type, in bytes. + /// 0 for non-host shareable types. + uint64_t BaseAlignment(MemoryLayout mem_layout) const override; }; } // namespace type diff --git a/src/ast/type/f32_type_test.cc b/src/ast/type/f32_type_test.cc index 88bef620c4..42325dbc5c 100644 --- a/src/ast/type/f32_type_test.cc +++ b/src/ast/type/f32_type_test.cc @@ -45,6 +45,16 @@ TEST_F(F32TypeTest, TypeName) { EXPECT_EQ(f.type_name(), "__f32"); } +TEST_F(F32TypeTest, MinBufferBindingSize) { + F32Type f; + EXPECT_EQ(4u, f.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); +} + +TEST_F(F32TypeTest, BaseAlignment) { + F32Type f; + EXPECT_EQ(4u, f.BaseAlignment(MemoryLayout::kUniformBuffer)); +} + } // namespace } // namespace type } // namespace ast diff --git a/src/ast/type/i32_type.cc b/src/ast/type/i32_type.cc index 772f5cf7ef..ec1b26eb35 100644 --- a/src/ast/type/i32_type.cc +++ b/src/ast/type/i32_type.cc @@ -30,7 +30,11 @@ std::string I32Type::type_name() const { return "__i32"; } -uint64_t I32Type::MinBufferBindingSize() const { +uint64_t I32Type::MinBufferBindingSize(MemoryLayout mem_layout) const { + return 4; +} + +uint64_t I32Type::BaseAlignment(MemoryLayout mem_layout) const { return 4; } diff --git a/src/ast/type/i32_type.h b/src/ast/type/i32_type.h index 6c6b625b72..a302cdaedd 100644 --- a/src/ast/type/i32_type.h +++ b/src/ast/type/i32_type.h @@ -38,9 +38,15 @@ class I32Type : public Type { /// @returns the name for this type std::string type_name() const override; + /// @param mem_layout type of memory layout to use in calculation. /// @returns minimum size required for this type, in bytes. /// 0 for non-host shareable types. - uint64_t MinBufferBindingSize() const override; + uint64_t MinBufferBindingSize(MemoryLayout mem_layout) const override; + + /// @param mem_layout type of memory layout to use in calculation. + /// @returns base alignment for the type, in bytes. + /// 0 for non-host shareable types. + uint64_t BaseAlignment(MemoryLayout mem_layout) const override; }; } // namespace type diff --git a/src/ast/type/i32_type_test.cc b/src/ast/type/i32_type_test.cc index 12e0795f2d..7a01fbfb2e 100644 --- a/src/ast/type/i32_type_test.cc +++ b/src/ast/type/i32_type_test.cc @@ -45,6 +45,16 @@ TEST_F(I32TypeTest, TypeName) { EXPECT_EQ(i.type_name(), "__i32"); } +TEST_F(I32TypeTest, MinBufferBindingSize) { + I32Type i; + EXPECT_EQ(4u, i.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); +} + +TEST_F(I32TypeTest, BaseAlignment) { + I32Type i; + EXPECT_EQ(4u, i.BaseAlignment(MemoryLayout::kUniformBuffer)); +} + } // namespace } // namespace type } // namespace ast diff --git a/src/ast/type/matrix_type.cc b/src/ast/type/matrix_type.cc index bd60c6c98c..8f0f09291e 100644 --- a/src/ast/type/matrix_type.cc +++ b/src/ast/type/matrix_type.cc @@ -16,6 +16,9 @@ #include <assert.h> +#include "src/ast/type/array_type.h" +#include "src/ast/type/vector_type.h" + namespace tint { namespace ast { namespace type { @@ -39,8 +42,16 @@ std::string MatrixType::type_name() const { subtype_->type_name(); } -uint64_t MatrixType::MinBufferBindingSize() const { - return rows_ * columns_ * subtype_->MinBufferBindingSize(); +uint64_t MatrixType::MinBufferBindingSize(MemoryLayout mem_layout) const { + VectorType vec(subtype_, rows_); + return (columns_ - 1) * vec.BaseAlignment(mem_layout) + + vec.MinBufferBindingSize(mem_layout); +} + +uint64_t MatrixType::BaseAlignment(MemoryLayout mem_layout) const { + VectorType vec(subtype_, rows_); + ArrayType arr(&vec, columns_); + return arr.BaseAlignment(mem_layout); } } // namespace type diff --git a/src/ast/type/matrix_type.h b/src/ast/type/matrix_type.h index 6a42d3527c..458c1aaf54 100644 --- a/src/ast/type/matrix_type.h +++ b/src/ast/type/matrix_type.h @@ -48,9 +48,15 @@ class MatrixType : public Type { /// @returns the name for this type std::string type_name() const override; + /// @param mem_layout type of memory layout to use in calculation. /// @returns minimum size required for this type, in bytes. /// 0 for non-host shareable types. - uint64_t MinBufferBindingSize() const override; + uint64_t MinBufferBindingSize(MemoryLayout mem_layout) const override; + + /// @param mem_layout type of memory layout to use in calculation. + /// @returns base alignment for the type, in bytes. + /// 0 for non-host shareable types. + uint64_t BaseAlignment(MemoryLayout mem_layout) const override; private: Type* subtype_ = nullptr; diff --git a/src/ast/type/matrix_type_test.cc b/src/ast/type/matrix_type_test.cc index 0ab07aca83..38e57ac4af 100644 --- a/src/ast/type/matrix_type_test.cc +++ b/src/ast/type/matrix_type_test.cc @@ -56,6 +56,62 @@ TEST_F(MatrixTypeTest, TypeName) { EXPECT_EQ(m.type_name(), "__mat_2_3__i32"); } +TEST_F(MatrixTypeTest, MinBufferBindingSize4x2) { + I32Type i32; + MatrixType m{&i32, 4, 2}; + EXPECT_EQ(32u, m.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); + EXPECT_EQ(32u, m.MinBufferBindingSize(MemoryLayout::kStorageBuffer)); +} + +TEST_F(MatrixTypeTest, MinBufferBindingSize3x2) { + I32Type i32; + MatrixType m{&i32, 3, 2}; + EXPECT_EQ(28u, m.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); + EXPECT_EQ(28u, m.MinBufferBindingSize(MemoryLayout::kStorageBuffer)); +} + +TEST_F(MatrixTypeTest, MinBufferBindingSize2x3) { + I32Type i32; + MatrixType m{&i32, 2, 3}; + EXPECT_EQ(24u, m.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); + EXPECT_EQ(24u, m.MinBufferBindingSize(MemoryLayout::kStorageBuffer)); +} + +TEST_F(MatrixTypeTest, MinBufferBindingSize2x2) { + I32Type i32; + MatrixType m{&i32, 2, 2}; + EXPECT_EQ(16u, m.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); + EXPECT_EQ(16u, m.MinBufferBindingSize(MemoryLayout::kStorageBuffer)); +} + +TEST_F(MatrixTypeTest, BaseAlignment4x2) { + I32Type i32; + MatrixType m{&i32, 4, 2}; + EXPECT_EQ(16u, m.BaseAlignment(MemoryLayout::kUniformBuffer)); + EXPECT_EQ(16u, m.BaseAlignment(MemoryLayout::kStorageBuffer)); +} + +TEST_F(MatrixTypeTest, BaseAlignment3x2) { + I32Type i32; + MatrixType m{&i32, 3, 2}; + EXPECT_EQ(16u, m.BaseAlignment(MemoryLayout::kUniformBuffer)); + EXPECT_EQ(16u, m.BaseAlignment(MemoryLayout::kStorageBuffer)); +} + +TEST_F(MatrixTypeTest, BaseAlignment2x3) { + I32Type i32; + MatrixType m{&i32, 2, 3}; + EXPECT_EQ(16u, m.BaseAlignment(MemoryLayout::kUniformBuffer)); + EXPECT_EQ(8u, m.BaseAlignment(MemoryLayout::kStorageBuffer)); +} + +TEST_F(MatrixTypeTest, BaseAlignment2x2) { + I32Type i32; + MatrixType m{&i32, 2, 2}; + EXPECT_EQ(16u, m.BaseAlignment(MemoryLayout::kUniformBuffer)); + EXPECT_EQ(8u, m.BaseAlignment(MemoryLayout::kStorageBuffer)); +} + } // namespace } // namespace type } // namespace ast diff --git a/src/ast/type/multisampled_texture_type_test.cc b/src/ast/type/multisampled_texture_type_test.cc index 4d3d059603..d75e4f17a4 100644 --- a/src/ast/type/multisampled_texture_type_test.cc +++ b/src/ast/type/multisampled_texture_type_test.cc @@ -69,6 +69,12 @@ TEST_F(MultisampledTextureTypeTest, TypeName) { EXPECT_EQ(s.type_name(), "__multisampled_texture_3d__f32"); } +TEST_F(MultisampledTextureTypeTest, MinBufferBindingSize) { + F32Type f32; + MultisampledTextureType s(TextureDimension::k3d, &f32); + EXPECT_EQ(0u, s.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); +} + } // namespace } // namespace type } // namespace ast diff --git a/src/ast/type/pointer_type.cc b/src/ast/type/pointer_type.cc index 507dbedb0f..e6083c811b 100644 --- a/src/ast/type/pointer_type.cc +++ b/src/ast/type/pointer_type.cc @@ -25,10 +25,6 @@ bool PointerType::IsPointer() const { return true; } -uint64_t PointerType::MinBufferBindingSize() const { - return 4; -} - std::string PointerType::type_name() const { std::ostringstream out; out << "__ptr_" << storage_class_ << subtype_->type_name(); diff --git a/src/ast/type/pointer_type.h b/src/ast/type/pointer_type.h index dbf7e5c842..fa9797551c 100644 --- a/src/ast/type/pointer_type.h +++ b/src/ast/type/pointer_type.h @@ -39,10 +39,6 @@ class PointerType : public Type { /// @returns true if the type is a pointer type bool IsPointer() const override; - /// @returns minimum size required for this type, in bytes. - /// 0 for non-host shareable types. - uint64_t MinBufferBindingSize() const override; - /// @returns the pointee type Type* type() const { return subtype_; } /// @returns the storage class of the pointer diff --git a/src/ast/type/sampled_texture_type_test.cc b/src/ast/type/sampled_texture_type_test.cc index a464459be0..370f616725 100644 --- a/src/ast/type/sampled_texture_type_test.cc +++ b/src/ast/type/sampled_texture_type_test.cc @@ -68,6 +68,12 @@ TEST_F(SampledTextureTypeTest, TypeName) { EXPECT_EQ(s.type_name(), "__sampled_texture_3d__f32"); } +TEST_F(SampledTextureTypeTest, MinBufferBindingSize) { + F32Type f32; + SampledTextureType s(TextureDimension::kCube, &f32); + EXPECT_EQ(0u, s.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); +} + } // namespace } // namespace type } // namespace ast diff --git a/src/ast/type/sampler_type_test.cc b/src/ast/type/sampler_type_test.cc index 34f835cec7..aa3b653068 100644 --- a/src/ast/type/sampler_type_test.cc +++ b/src/ast/type/sampler_type_test.cc @@ -61,6 +61,11 @@ TEST_F(SamplerTypeTest, TypeName_Comparison) { EXPECT_EQ(s.type_name(), "__sampler_comparison"); } +TEST_F(SamplerTypeTest, MinBufferBindingSize) { + SamplerType s{SamplerKind::kSampler}; + EXPECT_EQ(0u, s.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); +} + } // namespace } // namespace type } // namespace ast diff --git a/src/ast/type/storage_texture_type_test.cc b/src/ast/type/storage_texture_type_test.cc index cc0679f424..d65bb3f537 100644 --- a/src/ast/type/storage_texture_type_test.cc +++ b/src/ast/type/storage_texture_type_test.cc @@ -119,6 +119,12 @@ TEST_F(StorageTextureTypeTest, I32Type) { EXPECT_TRUE(s->AsTexture()->AsStorage()->type()->IsI32()); } +TEST_F(StorageTextureTypeTest, MinBufferBindingSize) { + StorageTextureType s(TextureDimension::k2dArray, AccessControl::kReadOnly, + ImageFormat::kRgba32Sint); + EXPECT_EQ(0u, s.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); +} + } // namespace } // namespace type } // namespace ast diff --git a/src/ast/type/struct_type.cc b/src/ast/type/struct_type.cc index c52fdb50a9..ba7a9b3e04 100644 --- a/src/ast/type/struct_type.cc +++ b/src/ast/type/struct_type.cc @@ -14,8 +14,14 @@ #include "src/ast/type/struct_type.h" +#include <cmath> #include <utility> +#include "src/ast/type/alias_type.h" +#include "src/ast/type/array_type.h" +#include "src/ast/type/matrix_type.h" +#include "src/ast/type/vector_type.h" + namespace tint { namespace ast { namespace type { @@ -35,7 +41,7 @@ std::string StructType::type_name() const { return "__struct_" + name_; } -uint64_t StructType::MinBufferBindingSize() const { +uint64_t StructType::MinBufferBindingSize(MemoryLayout mem_layout) const { if (!struct_->members().size()) { return 0; } @@ -48,7 +54,32 @@ uint64_t StructType::MinBufferBindingSize() const { return 0; } - return last_member->offset() + last_member->type()->MinBufferBindingSize(); + uint64_t size = last_member->type()->MinBufferBindingSize(mem_layout); + if (!size) { + return 0; + } + + float unaligned = last_member->offset() + size; + float alignment = BaseAlignment(mem_layout); + + return alignment * std::ceil(unaligned / alignment); +} + +uint64_t StructType::BaseAlignment(MemoryLayout mem_layout) const { + uint64_t max = 0; + for (const auto& member : struct_->members()) { + if (member->type()->BaseAlignment(mem_layout) > max) { + max = member->type()->BaseAlignment(mem_layout); + } + } + + if (mem_layout == MemoryLayout::kUniformBuffer) { + // Round up to a vec4. + return 16 * std::ceil(static_cast<float>(max) / 16.0f); + } else if (mem_layout == MemoryLayout::kStorageBuffer) { + return max; + } + return 0; } } // namespace type diff --git a/src/ast/type/struct_type.h b/src/ast/type/struct_type.h index ec5a7a11c4..50c1565b68 100644 --- a/src/ast/type/struct_type.h +++ b/src/ast/type/struct_type.h @@ -51,13 +51,21 @@ class StructType : public Type { /// @returns the name for the type std::string type_name() const override; + /// @param mem_layout type of memory layout to use in calculation. /// @returns minimum size required for this type, in bytes. /// 0 for non-host shareable types. - uint64_t MinBufferBindingSize() const override; + uint64_t MinBufferBindingSize(MemoryLayout mem_layout) const override; + + /// @param mem_layout type of memory layout to use in calculation. + /// @returns base alignment for the type, in bytes. + /// 0 for non-host shareable types. + uint64_t BaseAlignment(MemoryLayout mem_layout) const override; private: std::string name_; std::unique_ptr<Struct> struct_; + + uint64_t LargestMemberBaseAlignment(MemoryLayout mem_layout) const; }; } // namespace type diff --git a/src/ast/type/struct_type_test.cc b/src/ast/type/struct_type_test.cc index 775c9e95c2..9d98edca7e 100644 --- a/src/ast/type/struct_type_test.cc +++ b/src/ast/type/struct_type_test.cc @@ -17,7 +17,14 @@ #include <utility> #include "gtest/gtest.h" +#include "src/ast/stride_decoration.h" +#include "src/ast/struct_member.h" +#include "src/ast/struct_member_decoration.h" +#include "src/ast/struct_member_offset_decoration.h" +#include "src/ast/type/array_type.h" #include "src/ast/type/i32_type.h" +#include "src/ast/type/u32_type.h" +#include "src/ast/type/vector_type.h" namespace tint { namespace ast { @@ -57,6 +64,331 @@ TEST_F(StructTypeTest, TypeName) { EXPECT_EQ(s.type_name(), "__struct_my_struct"); } +TEST_F(StructTypeTest, MinBufferBindingSize) { + U32Type u32; + StructMemberList members; + + { + StructMemberDecorationList deco; + deco.push_back(std::make_unique<StructMemberOffsetDecoration>(0)); + members.push_back( + std::make_unique<StructMember>("foo", &u32, std::move(deco))); + } + { + StructMemberDecorationList deco; + deco.push_back(std::make_unique<StructMemberOffsetDecoration>(4)); + members.push_back( + std::make_unique<StructMember>("bar", &u32, std::move(deco))); + } + ast::StructDecorationList decos; + + auto str = + std::make_unique<ast::Struct>(std::move(decos), std::move(members)); + StructType struct_type("struct_type", std::move(str)); + EXPECT_EQ(16u, + struct_type.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); + EXPECT_EQ(8u, struct_type.MinBufferBindingSize(MemoryLayout::kStorageBuffer)); +} + +TEST_F(StructTypeTest, MinBufferBindingSizeArray) { + U32Type u32; + ArrayType arr(&u32, 4); + { + ArrayDecorationList decos; + decos.push_back(std::make_unique<StrideDecoration>(4)); + arr.set_decorations(std::move(decos)); + } + + StructMemberList members; + { + StructMemberDecorationList deco; + deco.push_back(std::make_unique<StructMemberOffsetDecoration>(0)); + members.push_back( + std::make_unique<StructMember>("foo", &u32, std::move(deco))); + } + { + StructMemberDecorationList deco; + deco.push_back(std::make_unique<StructMemberOffsetDecoration>(4)); + members.push_back( + std::make_unique<StructMember>("bar", &u32, std::move(deco))); + } + { + StructMemberDecorationList deco; + deco.push_back(std::make_unique<StructMemberOffsetDecoration>(8)); + members.push_back( + std::make_unique<StructMember>("bar", &arr, std::move(deco))); + } + ast::StructDecorationList decos; + + auto str = + std::make_unique<ast::Struct>(std::move(decos), std::move(members)); + StructType struct_type("struct_type", std::move(str)); + EXPECT_EQ(32u, + struct_type.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); + EXPECT_EQ(24u, + struct_type.MinBufferBindingSize(MemoryLayout::kStorageBuffer)); +} + +TEST_F(StructTypeTest, MinBufferBindingSizeRuntimeArray) { + U32Type u32; + ArrayType arr(&u32); + { + ArrayDecorationList decos; + decos.push_back(std::make_unique<StrideDecoration>(4)); + arr.set_decorations(std::move(decos)); + } + + StructMemberList members; + { + StructMemberDecorationList deco; + deco.push_back(std::make_unique<StructMemberOffsetDecoration>(0)); + members.push_back( + std::make_unique<StructMember>("foo", &u32, std::move(deco))); + } + { + StructMemberDecorationList deco; + deco.push_back(std::make_unique<StructMemberOffsetDecoration>(4)); + members.push_back( + std::make_unique<StructMember>("bar", &u32, std::move(deco))); + } + { + StructMemberDecorationList deco; + deco.push_back(std::make_unique<StructMemberOffsetDecoration>(8)); + members.push_back( + std::make_unique<StructMember>("bar", &u32, std::move(deco))); + } + ast::StructDecorationList decos; + + auto str = + std::make_unique<ast::Struct>(std::move(decos), std::move(members)); + StructType struct_type("struct_type", std::move(str)); + EXPECT_EQ(12u, + struct_type.MinBufferBindingSize(MemoryLayout::kStorageBuffer)); +} + +TEST_F(StructTypeTest, MinBufferBindingSizeVec2) { + U32Type u32; + VectorType vec2(&u32, 2); + + StructMemberList members; + { + StructMemberDecorationList deco; + deco.push_back(std::make_unique<StructMemberOffsetDecoration>(0)); + members.push_back( + std::make_unique<StructMember>("foo", &vec2, std::move(deco))); + } + ast::StructDecorationList decos; + + auto str = + std::make_unique<ast::Struct>(std::move(decos), std::move(members)); + StructType struct_type("struct_type", std::move(str)); + EXPECT_EQ(16u, + struct_type.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); + EXPECT_EQ(8u, struct_type.MinBufferBindingSize(MemoryLayout::kStorageBuffer)); +} + +TEST_F(StructTypeTest, MinBufferBindingSizeVec3) { + U32Type u32; + VectorType vec3(&u32, 3); + + StructMemberList members; + { + StructMemberDecorationList deco; + deco.push_back(std::make_unique<StructMemberOffsetDecoration>(0)); + members.push_back( + std::make_unique<StructMember>("foo", &vec3, std::move(deco))); + } + ast::StructDecorationList decos; + + auto str = + std::make_unique<ast::Struct>(std::move(decos), std::move(members)); + StructType struct_type("struct_type", std::move(str)); + EXPECT_EQ(16u, + struct_type.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); + EXPECT_EQ(16u, + struct_type.MinBufferBindingSize(MemoryLayout::kStorageBuffer)); +} + +TEST_F(StructTypeTest, MinBufferBindingSizeVec4) { + U32Type u32; + VectorType vec4(&u32, 4); + + StructMemberList members; + { + StructMemberDecorationList deco; + deco.push_back(std::make_unique<StructMemberOffsetDecoration>(0)); + members.push_back( + std::make_unique<StructMember>("foo", &vec4, std::move(deco))); + } + ast::StructDecorationList decos; + + auto str = + std::make_unique<ast::Struct>(std::move(decos), std::move(members)); + StructType struct_type("struct_type", std::move(str)); + EXPECT_EQ(16u, + struct_type.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); + EXPECT_EQ(16u, + struct_type.MinBufferBindingSize(MemoryLayout::kStorageBuffer)); +} + +TEST_F(StructTypeTest, BaseAlignment) { + U32Type u32; + StructMemberList members; + + { + StructMemberDecorationList deco; + deco.push_back(std::make_unique<StructMemberOffsetDecoration>(0)); + members.push_back( + std::make_unique<StructMember>("foo", &u32, std::move(deco))); + } + { + StructMemberDecorationList deco; + deco.push_back(std::make_unique<StructMemberOffsetDecoration>(4)); + members.push_back( + std::make_unique<StructMember>("bar", &u32, std::move(deco))); + } + ast::StructDecorationList decos; + + auto str = + std::make_unique<ast::Struct>(std::move(decos), std::move(members)); + StructType struct_type("struct_type", std::move(str)); + EXPECT_EQ(16u, struct_type.BaseAlignment(MemoryLayout::kUniformBuffer)); + EXPECT_EQ(4u, struct_type.BaseAlignment(MemoryLayout::kStorageBuffer)); +} + +TEST_F(StructTypeTest, BaseAlignmentArray) { + U32Type u32; + ArrayType arr(&u32, 4); + { + ArrayDecorationList decos; + decos.push_back(std::make_unique<StrideDecoration>(4)); + arr.set_decorations(std::move(decos)); + } + + StructMemberList members; + { + StructMemberDecorationList deco; + deco.push_back(std::make_unique<StructMemberOffsetDecoration>(0)); + members.push_back( + std::make_unique<StructMember>("foo", &u32, std::move(deco))); + } + { + StructMemberDecorationList deco; + deco.push_back(std::make_unique<StructMemberOffsetDecoration>(4)); + members.push_back( + std::make_unique<StructMember>("bar", &u32, std::move(deco))); + } + { + StructMemberDecorationList deco; + deco.push_back(std::make_unique<StructMemberOffsetDecoration>(8)); + members.push_back( + std::make_unique<StructMember>("bar", &arr, std::move(deco))); + } + ast::StructDecorationList decos; + + auto str = + std::make_unique<ast::Struct>(std::move(decos), std::move(members)); + StructType struct_type("struct_type", std::move(str)); + EXPECT_EQ(16u, struct_type.BaseAlignment(MemoryLayout::kUniformBuffer)); + EXPECT_EQ(4u, struct_type.BaseAlignment(MemoryLayout::kStorageBuffer)); +} + +TEST_F(StructTypeTest, BaseAlignmentRuntimeArray) { + U32Type u32; + ArrayType arr(&u32); + { + ArrayDecorationList decos; + decos.push_back(std::make_unique<StrideDecoration>(4)); + arr.set_decorations(std::move(decos)); + } + + StructMemberList members; + { + StructMemberDecorationList deco; + deco.push_back(std::make_unique<StructMemberOffsetDecoration>(0)); + members.push_back( + std::make_unique<StructMember>("foo", &u32, std::move(deco))); + } + { + StructMemberDecorationList deco; + deco.push_back(std::make_unique<StructMemberOffsetDecoration>(4)); + members.push_back( + std::make_unique<StructMember>("bar", &u32, std::move(deco))); + } + { + StructMemberDecorationList deco; + deco.push_back(std::make_unique<StructMemberOffsetDecoration>(8)); + members.push_back( + std::make_unique<StructMember>("bar", &u32, std::move(deco))); + } + ast::StructDecorationList decos; + + auto str = + std::make_unique<ast::Struct>(std::move(decos), std::move(members)); + StructType struct_type("struct_type", std::move(str)); + EXPECT_EQ(4u, struct_type.BaseAlignment(MemoryLayout::kStorageBuffer)); +} + +TEST_F(StructTypeTest, BaseAlignmentVec2) { + U32Type u32; + VectorType vec2(&u32, 2); + + StructMemberList members; + { + StructMemberDecorationList deco; + deco.push_back(std::make_unique<StructMemberOffsetDecoration>(0)); + members.push_back( + std::make_unique<StructMember>("foo", &vec2, std::move(deco))); + } + ast::StructDecorationList decos; + + auto str = + std::make_unique<ast::Struct>(std::move(decos), std::move(members)); + StructType struct_type("struct_type", std::move(str)); + EXPECT_EQ(16u, struct_type.BaseAlignment(MemoryLayout::kUniformBuffer)); + EXPECT_EQ(8u, struct_type.BaseAlignment(MemoryLayout::kStorageBuffer)); +} + +TEST_F(StructTypeTest, BaseAlignmentVec3) { + U32Type u32; + VectorType vec3(&u32, 3); + + StructMemberList members; + { + StructMemberDecorationList deco; + deco.push_back(std::make_unique<StructMemberOffsetDecoration>(0)); + members.push_back( + std::make_unique<StructMember>("foo", &vec3, std::move(deco))); + } + ast::StructDecorationList decos; + + auto str = + std::make_unique<ast::Struct>(std::move(decos), std::move(members)); + StructType struct_type("struct_type", std::move(str)); + EXPECT_EQ(16u, struct_type.BaseAlignment(MemoryLayout::kUniformBuffer)); + EXPECT_EQ(16u, struct_type.BaseAlignment(MemoryLayout::kStorageBuffer)); +} + +TEST_F(StructTypeTest, BaseAlignmentVec4) { + U32Type u32; + VectorType vec4(&u32, 4); + + StructMemberList members; + { + StructMemberDecorationList deco; + deco.push_back(std::make_unique<StructMemberOffsetDecoration>(0)); + members.push_back( + std::make_unique<StructMember>("foo", &vec4, std::move(deco))); + } + ast::StructDecorationList decos; + + auto str = + std::make_unique<ast::Struct>(std::move(decos), std::move(members)); + StructType struct_type("struct_type", std::move(str)); + EXPECT_EQ(16u, struct_type.BaseAlignment(MemoryLayout::kUniformBuffer)); + EXPECT_EQ(16u, struct_type.BaseAlignment(MemoryLayout::kStorageBuffer)); +} + } // namespace } // namespace type } // namespace ast diff --git a/src/ast/type/type.cc b/src/ast/type/type.cc index b7d065d827..a0f29b77c1 100644 --- a/src/ast/type/type.cc +++ b/src/ast/type/type.cc @@ -120,7 +120,11 @@ bool Type::IsVoid() const { return false; } -uint64_t Type::MinBufferBindingSize() const { +uint64_t Type::MinBufferBindingSize(MemoryLayout) const { + return 0; +} + +uint64_t Type::BaseAlignment(MemoryLayout) const { return 0; } diff --git a/src/ast/type/type.h b/src/ast/type/type.h index ca4b208642..54903ff349 100644 --- a/src/ast/type/type.h +++ b/src/ast/type/type.h @@ -36,6 +36,9 @@ class U32Type; class VectorType; class VoidType; +/// Supported memory layouts for calculating sizes +enum class MemoryLayout { kUniformBuffer, kStorageBuffer }; + /// Base class for a type in the system class Type { public: @@ -75,9 +78,15 @@ class Type { /// @returns the name for this type. The |type_name| is unique over all types. virtual std::string type_name() const = 0; + /// @param mem_layout type of memory layout to use in calculation. /// @returns minimum size required for this type, in bytes. /// 0 for non-host shareable types. - virtual uint64_t MinBufferBindingSize() const; + virtual uint64_t MinBufferBindingSize(MemoryLayout mem_layout) const; + + /// @param mem_layout type of memory layout to use in calculation. + /// @returns base alignment for the type, in bytes. + /// 0 for non-host shareable types. + virtual uint64_t BaseAlignment(MemoryLayout mem_layout) const; /// @returns the pointee type if this is a pointer, |this| otherwise Type* UnwrapPtrIfNeeded(); diff --git a/src/ast/type/u32_type.cc b/src/ast/type/u32_type.cc index ebc65f506d..a699eb37f0 100644 --- a/src/ast/type/u32_type.cc +++ b/src/ast/type/u32_type.cc @@ -32,7 +32,11 @@ std::string U32Type::type_name() const { return "__u32"; } -uint64_t U32Type::MinBufferBindingSize() const { +uint64_t U32Type::MinBufferBindingSize(MemoryLayout) const { + return 4; +} + +uint64_t U32Type::BaseAlignment(MemoryLayout) const { return 4; } diff --git a/src/ast/type/u32_type.h b/src/ast/type/u32_type.h index 27b40dbf6b..64bc641823 100644 --- a/src/ast/type/u32_type.h +++ b/src/ast/type/u32_type.h @@ -38,9 +38,15 @@ class U32Type : public Type { /// @returns the name for th type std::string type_name() const override; + /// @param mem_layout type of memory layout to use in calculation. /// @returns minimum size required for this type, in bytes. /// 0 for non-host shareable types. - uint64_t MinBufferBindingSize() const override; + uint64_t MinBufferBindingSize(MemoryLayout mem_layout) const override; + + /// @param mem_layout type of memory layout to use in calculation. + /// @returns base alignment for the type, in bytes. + /// 0 for non-host shareable types. + uint64_t BaseAlignment(MemoryLayout mem_layout) const override; }; } // namespace type diff --git a/src/ast/type/u32_type_test.cc b/src/ast/type/u32_type_test.cc index ef0ff20656..9794cab47e 100644 --- a/src/ast/type/u32_type_test.cc +++ b/src/ast/type/u32_type_test.cc @@ -45,6 +45,16 @@ TEST_F(U32TypeTest, TypeName) { EXPECT_EQ(u.type_name(), "__u32"); } +TEST_F(U32TypeTest, MinBufferBindingSize) { + U32Type u; + EXPECT_EQ(4u, u.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); +} + +TEST_F(U32TypeTest, BaseAlignment) { + U32Type u; + EXPECT_EQ(4u, u.BaseAlignment(MemoryLayout::kUniformBuffer)); +} + } // namespace } // namespace type } // namespace ast diff --git a/src/ast/type/vector_type.cc b/src/ast/type/vector_type.cc index 645ac2f576..973471fcbc 100644 --- a/src/ast/type/vector_type.cc +++ b/src/ast/type/vector_type.cc @@ -15,6 +15,7 @@ #include "src/ast/type/vector_type.h" #include <assert.h> +#include <cmath> namespace tint { namespace ast { @@ -38,8 +39,18 @@ std::string VectorType::type_name() const { return "__vec_" + std::to_string(size_) + subtype_->type_name(); } -uint64_t VectorType::MinBufferBindingSize() const { - return size_ * subtype_->MinBufferBindingSize(); +uint64_t VectorType::MinBufferBindingSize(MemoryLayout mem_layout) const { + return size_ * subtype_->MinBufferBindingSize(mem_layout); +} + +uint64_t VectorType::BaseAlignment(MemoryLayout mem_layout) const { + if (size_ == 2) { + return 2 * subtype_->BaseAlignment(mem_layout); + } else if (size_ == 3 || size_ == 4) { + return 4 * subtype_->BaseAlignment(mem_layout); + } + + return 0; // vectors are only supposed to have 2, 3, or 4 elements. } } // namespace type diff --git a/src/ast/type/vector_type.h b/src/ast/type/vector_type.h index 3862274f0f..3d6313d5fe 100644 --- a/src/ast/type/vector_type.h +++ b/src/ast/type/vector_type.h @@ -45,9 +45,15 @@ class VectorType : public Type { /// @returns the name for th type std::string type_name() const override; + /// @param mem_layout type of memory layout to use in calculation. /// @returns minimum size required for this type, in bytes. /// 0 for non-host shareable types. - uint64_t MinBufferBindingSize() const override; + uint64_t MinBufferBindingSize(MemoryLayout mem_layout) const override; + + /// @param mem_layout type of memory layout to use in calculation. + /// @returns base alignment for the type, in bytes. + /// 0 for non-host shareable types. + uint64_t BaseAlignment(MemoryLayout mem_layout) const override; private: Type* subtype_ = nullptr; diff --git a/src/ast/type/vector_type_test.cc b/src/ast/type/vector_type_test.cc index 1cc53901c4..791e4d4f88 100644 --- a/src/ast/type/vector_type_test.cc +++ b/src/ast/type/vector_type_test.cc @@ -55,6 +55,42 @@ TEST_F(VectorTypeTest, TypeName) { EXPECT_EQ(v.type_name(), "__vec_3__i32"); } +TEST_F(VectorTypeTest, MinBufferBindingSizeVec2) { + I32Type i32; + VectorType v{&i32, 2}; + EXPECT_EQ(8u, v.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); +} + +TEST_F(VectorTypeTest, MinBufferBindingSizeVec3) { + I32Type i32; + VectorType v{&i32, 3}; + EXPECT_EQ(12u, v.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); +} + +TEST_F(VectorTypeTest, MinBufferBindingSizeVec4) { + I32Type i32; + VectorType v{&i32, 4}; + EXPECT_EQ(16u, v.MinBufferBindingSize(MemoryLayout::kUniformBuffer)); +} + +TEST_F(VectorTypeTest, BaseAlignmentVec2) { + I32Type i32; + VectorType v{&i32, 2}; + EXPECT_EQ(8u, v.BaseAlignment(MemoryLayout::kUniformBuffer)); +} + +TEST_F(VectorTypeTest, BaseAlignmentVec3) { + I32Type i32; + VectorType v{&i32, 3}; + EXPECT_EQ(16u, v.BaseAlignment(MemoryLayout::kUniformBuffer)); +} + +TEST_F(VectorTypeTest, BaseAlignmentVec4) { + I32Type i32; + VectorType v{&i32, 4}; + EXPECT_EQ(16u, v.BaseAlignment(MemoryLayout::kUniformBuffer)); +} + } // namespace } // namespace type } // namespace ast diff --git a/src/inspector/inspector.cc b/src/inspector/inspector.cc index 39b9e5963c..b6bdc4a5bb 100644 --- a/src/inspector/inspector.cc +++ b/src/inspector/inspector.cc @@ -27,6 +27,7 @@ #include "src/ast/scalar_constructor_expression.h" #include "src/ast/sint_literal.h" #include "src/ast/type/struct_type.h" +#include "src/ast/type/type.h" #include "src/ast/uint_literal.h" namespace tint { @@ -160,7 +161,8 @@ std::vector<ResourceBinding> Inspector::GetUniformBufferResourceBindings( entry.bind_group = binding_info.set->value(); entry.binding = binding_info.binding->value(); - entry.min_buffer_binding_size = var->type()->MinBufferBindingSize(); + entry.min_buffer_binding_size = var->type()->MinBufferBindingSize( + ast::type::MemoryLayout::kUniformBuffer); result.push_back(std::move(entry)); } @@ -187,7 +189,8 @@ std::vector<ResourceBinding> Inspector::GetStorageBufferResourceBindings( entry.bind_group = binding_info.set->value(); entry.binding = binding_info.binding->value(); - entry.min_buffer_binding_size = var->type()->MinBufferBindingSize(); + entry.min_buffer_binding_size = var->type()->MinBufferBindingSize( + ast::type::MemoryLayout::kStorageBuffer); result.push_back(std::move(entry)); } diff --git a/src/inspector/inspector_test.cc b/src/inspector/inspector_test.cc index e6215576e4..f3665e7798 100644 --- a/src/inspector/inspector_test.cc +++ b/src/inspector/inspector_test.cc @@ -33,6 +33,7 @@ #include "src/ast/scalar_constructor_expression.h" #include "src/ast/sint_literal.h" #include "src/ast/stage_decoration.h" +#include "src/ast/stride_decoration.h" #include "src/ast/struct_decoration.h" #include "src/ast/struct_member.h" #include "src/ast/struct_member_decoration.h" @@ -398,6 +399,9 @@ class InspectorHelper { if (array_type_memo_.find(count) == array_type_memo_.end()) { array_type_memo_[count] = std::make_unique<ast::type::ArrayType>(u32_type(), count); + ast::ArrayDecorationList decos; + decos.push_back(std::make_unique<ast::StrideDecoration>(4)); + array_type_memo_[count]->set_decorations(std::move(decos)); } return array_type_memo_[count].get(); } @@ -914,7 +918,7 @@ TEST_F(InspectorGetUniformBufferResourceBindings, Simple) { EXPECT_EQ(0u, result[0].bind_group); EXPECT_EQ(0u, result[0].binding); - EXPECT_EQ(4u, result[0].min_buffer_binding_size); + EXPECT_EQ(16u, result[0].min_buffer_binding_size); } TEST_F(InspectorGetUniformBufferResourceBindings, MultipleMembers) { @@ -939,7 +943,7 @@ TEST_F(InspectorGetUniformBufferResourceBindings, MultipleMembers) { EXPECT_EQ(0u, result[0].bind_group); EXPECT_EQ(0u, result[0].binding); - EXPECT_EQ(12u, result[0].min_buffer_binding_size); + EXPECT_EQ(16u, result[0].min_buffer_binding_size); } TEST_F(InspectorGetUniformBufferResourceBindings, MultipleUniformBuffers) { @@ -989,15 +993,15 @@ TEST_F(InspectorGetUniformBufferResourceBindings, MultipleUniformBuffers) { EXPECT_EQ(0u, result[0].bind_group); EXPECT_EQ(0u, result[0].binding); - EXPECT_EQ(12u, result[0].min_buffer_binding_size); + EXPECT_EQ(16u, result[0].min_buffer_binding_size); EXPECT_EQ(0u, result[1].bind_group); EXPECT_EQ(1u, result[1].binding); - EXPECT_EQ(12u, result[1].min_buffer_binding_size); + EXPECT_EQ(16u, result[1].min_buffer_binding_size); EXPECT_EQ(2u, result[2].bind_group); EXPECT_EQ(0u, result[2].binding); - EXPECT_EQ(12u, result[2].min_buffer_binding_size); + EXPECT_EQ(16u, result[2].min_buffer_binding_size); } TEST_F(InspectorGetUniformBufferResourceBindings, ContainingArray) { @@ -1022,7 +1026,7 @@ TEST_F(InspectorGetUniformBufferResourceBindings, ContainingArray) { EXPECT_EQ(0u, result[0].bind_group); EXPECT_EQ(0u, result[0].binding); - EXPECT_EQ(20u, result[0].min_buffer_binding_size); + EXPECT_EQ(32u, result[0].min_buffer_binding_size); } TEST_F(InspectorGetStorageBufferResourceBindings, Simple) {