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) {