Include padding bytes in minBindingSize validation

The "minimum buffer binding size" for a buffer binding variable with
type `T` is `SizeOf(T)`, which includes trailing padding bytes for
structures.

Update several tests that were not creating large enough buffers. Add
a new test for validating the size of a buffer with a non-struct vec3
type, which should still be 12 bytes.

Fixed: tint:1377
Change-Id: Iddbc22c561a67b6aa6659d7ddf78b1b12b230930
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/111582
Reviewed-by: Austin Eng <enga@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: James Price <jrprice@google.com>
diff --git a/src/dawn/native/ShaderModule.cpp b/src/dawn/native/ShaderModule.cpp
index 5bf019c..d473d89 100644
--- a/src/dawn/native/ShaderModule.cpp
+++ b/src/dawn/native/ShaderModule.cpp
@@ -696,7 +696,7 @@
 
         switch (info.bindingType) {
             case BindingInfoType::Buffer:
-                info.buffer.minBindingSize = resource.size_no_padding;
+                info.buffer.minBindingSize = resource.size;
                 DAWN_TRY_ASSIGN(info.buffer.type,
                                 TintResourceTypeToBufferBindingType(resource.resource_type));
                 break;
diff --git a/src/dawn/tests/end2end/BindGroupTests.cpp b/src/dawn/tests/end2end/BindGroupTests.cpp
index fc78112..1bc8871 100644
--- a/src/dawn/tests/end2end/BindGroupTests.cpp
+++ b/src/dawn/tests/end2end/BindGroupTests.cpp
@@ -1045,17 +1045,13 @@
             value : u32
         }
 
-        struct OutputBuffer {
-            value : vec3<u32>
-        }
-
         @group(0) @binding(2) var<uniform> buffer2 : Buffer;
         @group(0) @binding(3) var<storage, read> buffer3 : Buffer;
         @group(0) @binding(0) var<storage, read> buffer0 : Buffer;
-        @group(0) @binding(4) var<storage, read_write> outputBuffer : OutputBuffer;
+        @group(0) @binding(4) var<storage, read_write> outputBuffer : vec3<u32>;
 
         @compute @workgroup_size(1) fn main() {
-            outputBuffer.value = vec3<u32>(buffer0.value, buffer2.value, buffer3.value);
+            outputBuffer = vec3<u32>(buffer0.value, buffer2.value, buffer3.value);
         })");
     pipelineDescriptor.compute.entryPoint = "main";
     pipelineDescriptor.layout = utils::MakeBasicPipelineLayout(device, &bgl);
diff --git a/src/dawn/tests/end2end/ComputeDispatchTests.cpp b/src/dawn/tests/end2end/ComputeDispatchTests.cpp
index 1cdf759..472d54d 100644
--- a/src/dawn/tests/end2end/ComputeDispatchTests.cpp
+++ b/src/dawn/tests/end2end/ComputeDispatchTests.cpp
@@ -28,22 +28,18 @@
         // Write workgroup number into the output buffer if we saw the biggest dispatch
         // To make sure the dispatch was not called, write maximum u32 value for 0 dispatches
         wgpu::ShaderModule module = utils::CreateShaderModule(device, R"(
-            struct OutputBuf {
-                workGroups : vec3<u32>
-            }
-
-            @group(0) @binding(0) var<storage, read_write> output : OutputBuf;
+            @group(0) @binding(0) var<storage, read_write> output : vec3<u32>;
 
             @compute @workgroup_size(1, 1, 1)
             fn main(@builtin(global_invocation_id) GlobalInvocationID : vec3<u32>,
                     @builtin(num_workgroups) dispatch : vec3<u32>) {
                 if (dispatch.x == 0u || dispatch.y == 0u || dispatch.z == 0u) {
-                    output.workGroups = vec3<u32>(0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu);
+                    output = vec3<u32>(0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu);
                     return;
                 }
 
                 if (all(GlobalInvocationID == dispatch - vec3<u32>(1u, 1u, 1u))) {
-                    output.workGroups = dispatch;
+                    output = dispatch;
                 }
             })");
 
@@ -54,27 +50,20 @@
 
         // Test the use of the compute pipelines without using @num_workgroups
         wgpu::ShaderModule moduleWithoutNumWorkgroups = utils::CreateShaderModule(device, R"(
-            struct InputBuf {
-                expectedDispatch : vec3<u32>
-            }
-            struct OutputBuf {
-                workGroups : vec3<u32>
-            }
-
-            @group(0) @binding(0) var<uniform> input : InputBuf;
-            @group(0) @binding(1) var<storage, read_write> output : OutputBuf;
+            @group(0) @binding(0) var<uniform> input : vec3<u32>;
+            @group(0) @binding(1) var<storage, read_write> output : vec3<u32>;
 
             @compute @workgroup_size(1, 1, 1)
             fn main(@builtin(global_invocation_id) GlobalInvocationID : vec3<u32>) {
-                let dispatch : vec3<u32> = input.expectedDispatch;
+                let dispatch : vec3<u32> = input;
 
                 if (dispatch.x == 0u || dispatch.y == 0u || dispatch.z == 0u) {
-                    output.workGroups = vec3<u32>(0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu);
+                    output = vec3<u32>(0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu);
                     return;
                 }
 
                 if (all(GlobalInvocationID == dispatch - vec3<u32>(1u, 1u, 1u))) {
-                    output.workGroups = dispatch;
+                    output = dispatch;
                 }
             })");
         csDesc.compute.module = moduleWithoutNumWorkgroups;
diff --git a/src/dawn/tests/end2end/ComputeLayoutMemoryBufferTests.cpp b/src/dawn/tests/end2end/ComputeLayoutMemoryBufferTests.cpp
index 79cacd9..bf9ccdc 100644
--- a/src/dawn/tests/end2end/ComputeLayoutMemoryBufferTests.cpp
+++ b/src/dawn/tests/end2end/ComputeLayoutMemoryBufferTests.cpp
@@ -640,7 +640,7 @@
 
     MemoryDataBuilder expectedDataBuilder;  // The expected data to be copied by the shader
     expectedDataBuilder.AddSubBuilder(field.GetDataBuilder());
-    expectedDataBuilder.AlignTo(4);  // Storage buffer size must be a multiple of 4
+    expectedDataBuilder.AlignTo(std::max<size_t>(field.GetAlign(), 4u));
 
     // Expectation and input buffer have identical data bytes but different padding bytes.
     // Initializes the dst buffer with data bytes different from input and expectation, and padding
diff --git a/src/dawn/tests/end2end/MultisampledRenderingTests.cpp b/src/dawn/tests/end2end/MultisampledRenderingTests.cpp
index 407ae7c..88bb571 100644
--- a/src/dawn/tests/end2end/MultisampledRenderingTests.cpp
+++ b/src/dawn/tests/end2end/MultisampledRenderingTests.cpp
@@ -329,7 +329,7 @@
         utils::ComboRenderPassDescriptor renderPass =
             CreateComboRenderPassDescriptorForTest({mMultisampledColorView}, {mResolveView},
                                                    wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, true);
-        std::array<float, 5> kUniformData = {kGreen.r, kGreen.g, kGreen.b, kGreen.a,  // Color
+        std::array<float, 8> kUniformData = {kGreen.r, kGreen.g, kGreen.b, kGreen.a,  // Color
                                              0.2f};                                   // depth
         constexpr uint32_t kSize = sizeof(kUniformData);
         EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kUniformData.data(), kSize);
@@ -343,7 +343,7 @@
             {mMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Load, wgpu::LoadOp::Load,
             kTestDepth);
 
-        std::array<float, 5> kUniformData = {kRed.r, kRed.g, kRed.b, kRed.a,  // color
+        std::array<float, 8> kUniformData = {kRed.r, kRed.g, kRed.b, kRed.a,  // color
                                              0.5f};                           // depth
         constexpr uint32_t kSize = sizeof(kUniformData);
         EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kUniformData.data(), kSize);
@@ -714,7 +714,7 @@
         utils::ComboRenderPassDescriptor renderPass =
             CreateComboRenderPassDescriptorForTest({mMultisampledColorView}, {mResolveView},
                                                    wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, true);
-        std::array<float, 5> kUniformData = {kGreen.r, kGreen.g, kGreen.b, kGreen.a,  // Color
+        std::array<float, 8> kUniformData = {kGreen.r, kGreen.g, kGreen.b, kGreen.a,  // Color
                                              0.2f};                                   // depth
         constexpr uint32_t kSize = sizeof(kUniformData);
         EncodeRenderPassForTest(commandEncoder, renderPass, pipelineGreen, kUniformData.data(),
@@ -729,7 +729,7 @@
             {mMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Load, wgpu::LoadOp::Load,
             kTestDepth);
 
-        std::array<float, 5> kUniformData = {kRed.r, kRed.g, kRed.b, kRed.a,  // color
+        std::array<float, 8> kUniformData = {kRed.r, kRed.g, kRed.b, kRed.a,  // color
                                              0.5f};                           // depth
         constexpr uint32_t kSize = sizeof(kUniformData);
         EncodeRenderPassForTest(commandEncoder, renderPass, pipelineRed, kUniformData.data(),
@@ -1003,7 +1003,7 @@
         utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest(
             {mMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Clear, wgpu::LoadOp::Clear,
             kTestDepth);
-        std::array<float, 5> kUniformData = {kGreen.r, kGreen.g, kGreen.b, kGreen.a,  // Color
+        std::array<float, 8> kUniformData = {kGreen.r, kGreen.g, kGreen.b, kGreen.a,  // Color
                                              0.2f};                                   // depth
         constexpr uint32_t kSize = sizeof(kUniformData);
         EncodeRenderPassForTest(commandEncoder, renderPass, pipelineGreen, kUniformData.data(),
@@ -1018,7 +1018,7 @@
             {mMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Load, wgpu::LoadOp::Load,
             kTestDepth);
 
-        std::array<float, 5> kUniformData = {kRed.r, kRed.g, kRed.b, kRed.a,  // color
+        std::array<float, 8> kUniformData = {kRed.r, kRed.g, kRed.b, kRed.a,  // color
                                              0.5f};                           // depth
         constexpr uint32_t kSize = sizeof(kUniformData);
         EncodeRenderPassForTest(commandEncoder, renderPass, pipelineRed, kUniformData.data(),
diff --git a/src/dawn/tests/perf_tests/DrawCallPerf.cpp b/src/dawn/tests/perf_tests/DrawCallPerf.cpp
index 335f73b..4dd9715 100644
--- a/src/dawn/tests/perf_tests/DrawCallPerf.cpp
+++ b/src/dawn/tests/perf_tests/DrawCallPerf.cpp
@@ -41,26 +41,17 @@
         })";
 
 constexpr char kFragmentShaderA[] = R"(
-        struct Uniforms {
-            color : vec3<f32>
-        }
-        @group(0) @binding(0) var<uniform> uniforms : Uniforms;
+        @group(0) @binding(0) var<uniform> color : vec3<f32>;
         @fragment fn main() -> @location(0) vec4<f32> {
-            return vec4<f32>(uniforms.color * (1.0 / 5000.0), 1.0);
+            return vec4<f32>(color * (1.0 / 5000.0), 1.0);
         })";
 
 constexpr char kFragmentShaderB[] = R"(
-        struct Constants {
-            color : vec3<f32>
-        }
-        struct Uniforms {
-            color : vec3<f32>
-        }
-        @group(0) @binding(0) var<uniform> constants : Constants;
-        @group(1) @binding(0) var<uniform> uniforms : Uniforms;
+        @group(0) @binding(0) var<uniform> constant_color : vec3<f32>;
+        @group(1) @binding(0) var<uniform> uniform_color : vec3<f32>;
 
         @fragment fn main() -> @location(0) vec4<f32> {
-            return vec4<f32>((constants.color + uniforms.color) * (1.0 / 5000.0), 1.0);
+            return vec4<f32>((constant_color + uniform_color) * (1.0 / 5000.0), 1.0);
         })";
 
 enum class Pipeline {
diff --git a/src/dawn/tests/unittests/validation/MinimumBufferSizeValidationTests.cpp b/src/dawn/tests/unittests/validation/MinimumBufferSizeValidationTests.cpp
index dbd07c2..b09d0c9 100644
--- a/src/dawn/tests/unittests/validation/MinimumBufferSizeValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/MinimumBufferSizeValidationTests.cpp
@@ -571,7 +571,7 @@
 TEST_F(MinBufferSizeDefaultLayoutTests, NonDefaultLayout) {
     CheckShaderBindingSizeReflection(
         {{{0, 0, "@size(256) a : u32, b : u32,", "u32", "a", 260},
-          {0, 1, "c : u32, @align(16) d : u32,", "u32", "c", 20},
+          {0, 1, "c : u32, @align(16) d : u32,", "u32", "c", 32},
           {0, 2, "d : array<array<u32, 10>, 3>,", "u32", "d[0][0]", 120},
           {0, 3, "e : array<array<u32, 10>>,", "u32", "e[0][0]", 40}}});
 }
@@ -593,3 +593,31 @@
 
     CheckLayoutBindingSizeValidation(renderLayout, {{0, 0, "", "", "", 8}, {0, 1, "", "", "", 16}});
 }
+
+// Make sure that buffers with non-struct vec3 types do not include padding in the min buffer size.
+TEST_F(MinBufferSizePipelineCreationTests, NonStructVec3) {
+    std::vector<BindingDescriptor> bindings = {{0, 0, "", "", "", 12}, {0, 1, "", "", "", 12}};
+
+    auto MakeShader = [](const char* stageAttributes) {
+        std::ostringstream ostream;
+        ostream << "@group(0) @binding(0) var<storage, read_write> buffer : vec3<u32>;\n";
+        ostream << stageAttributes << " fn main() { buffer = vec3(42, 0, 7); }\n";
+        return ostream.str();
+    };
+    std::string computeShader = MakeShader("@compute @workgroup_size(1)");
+    std::string fragShader = MakeShader("@fragment");
+    std::string vertexShader = CreateVertexShaderWithBindings({});
+
+    CheckSizeBounds({12}, [&](const std::vector<uint64_t>& sizes, bool expectation) {
+        wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout(
+            device, {{0, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment,
+                      wgpu::BufferBindingType::Storage, false, sizes[0]}});
+        if (expectation) {
+            CreateRenderPipeline({layout}, vertexShader, fragShader);
+            CreateComputePipeline({layout}, computeShader);
+        } else {
+            ASSERT_DEVICE_ERROR(CreateRenderPipeline({layout}, vertexShader, fragShader));
+            ASSERT_DEVICE_ERROR(CreateComputePipeline({layout}, computeShader));
+        }
+    });
+}