Dynamic Buffer Offset : Validation

In a typical application, most draws will use different uniforms values for
things like the world position and orientation. In the current state of WebGPU
this means that a new bind group needs to be created for each draw to set the
right uniforms. Bind group creation is expected to be more expensive than
recording draws because they incur an allocation.

This feature is to reduce the number of bind groups that need to be
created.

The patch implemented dynamic buffer offset validation logics and adding unittests.

BUG=dawn:55
Change-Id: If6200a87bfedba825abcbfe60f336eab2e27226a
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/7100
Commit-Queue: Shaobo Yan <shaobo.yan@intel.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/common/Constants.h b/src/common/Constants.h
index 7aa9039..0a41dff 100644
--- a/src/common/Constants.h
+++ b/src/common/Constants.h
@@ -33,6 +33,8 @@
 static constexpr uint32_t kNumStages = 3;
 static constexpr uint32_t kMaxColorAttachments = 4u;
 static constexpr uint32_t kTextureRowPitchAlignment = 256u;
+// Dynamic buffer offsets require offset to be divisible by 256
+static constexpr uint64_t kMinDynamicBufferOffsetAlignment = 256u;
 
 // Non spec defined constants.
 static constexpr float kLodMin = 0.0;
diff --git a/src/dawn_native/BindGroup.cpp b/src/dawn_native/BindGroup.cpp
index 8bc56c5..93af9bf 100644
--- a/src/dawn_native/BindGroup.cpp
+++ b/src/dawn_native/BindGroup.cpp
@@ -126,9 +126,11 @@
             // Perform binding-type specific validation.
             switch (layoutInfo.types[bindingIndex]) {
                 case dawn::BindingType::UniformBuffer:
+                case dawn::BindingType::DynamicUniformBuffer:
                     DAWN_TRY(ValidateBufferBinding(device, binding, dawn::BufferUsageBit::Uniform));
                     break;
                 case dawn::BindingType::StorageBuffer:
+                case dawn::BindingType::DynamicStorageBuffer:
                     DAWN_TRY(ValidateBufferBinding(device, binding, dawn::BufferUsageBit::Storage));
                     break;
                 case dawn::BindingType::SampledTexture:
@@ -138,10 +140,6 @@
                 case dawn::BindingType::Sampler:
                     DAWN_TRY(ValidateSamplerBinding(device, binding));
                     break;
-                // TODO(shaobo.yan@intel.com): Implement dynamic buffer offset.
-                case dawn::BindingType::DynamicUniformBuffer:
-                case dawn::BindingType::DynamicStorageBuffer:
-                    return DAWN_VALIDATION_ERROR("Dawn doesn't support dynamic buffer yet");
             }
         }
 
@@ -209,7 +207,10 @@
         ASSERT(binding < kMaxBindingsPerGroup);
         ASSERT(mLayout->GetBindingInfo().mask[binding]);
         ASSERT(mLayout->GetBindingInfo().types[binding] == dawn::BindingType::UniformBuffer ||
-               mLayout->GetBindingInfo().types[binding] == dawn::BindingType::StorageBuffer);
+               mLayout->GetBindingInfo().types[binding] == dawn::BindingType::StorageBuffer ||
+               mLayout->GetBindingInfo().types[binding] ==
+                   dawn::BindingType::DynamicUniformBuffer ||
+               mLayout->GetBindingInfo().types[binding] == dawn::BindingType::DynamicStorageBuffer);
         BufferBase* buffer = static_cast<BufferBase*>(mBindings[binding].Get());
         return {buffer, mOffsets[binding], mSizes[binding]};
     }
diff --git a/src/dawn_native/BindGroupLayout.cpp b/src/dawn_native/BindGroupLayout.cpp
index e316917..4e94e9f 100644
--- a/src/dawn_native/BindGroupLayout.cpp
+++ b/src/dawn_native/BindGroupLayout.cpp
@@ -87,6 +87,11 @@
             mBindingInfo.visibilities[index] = binding.visibility;
             mBindingInfo.types[index] = binding.type;
 
+            if (binding.type == dawn::BindingType::DynamicUniformBuffer ||
+                binding.type == dawn::BindingType::DynamicStorageBuffer) {
+                mDynamicBufferCount++;
+            }
+
             ASSERT(!mBindingInfo.mask[index]);
             mBindingInfo.mask.set(index);
         }
@@ -122,4 +127,8 @@
         return a->mBindingInfo == b->mBindingInfo;
     }
 
+    uint32_t BindGroupLayoutBase::GetDynamicBufferCount() const {
+        return mDynamicBufferCount;
+    }
+
 }  // namespace dawn_native
diff --git a/src/dawn_native/BindGroupLayout.h b/src/dawn_native/BindGroupLayout.h
index 047228f..decc68f 100644
--- a/src/dawn_native/BindGroupLayout.h
+++ b/src/dawn_native/BindGroupLayout.h
@@ -54,11 +54,14 @@
             bool operator()(const BindGroupLayoutBase* a, const BindGroupLayoutBase* b) const;
         };
 
+        uint32_t GetDynamicBufferCount() const;
+
       private:
         BindGroupLayoutBase(DeviceBase* device, ObjectBase::ErrorTag tag);
 
         LayoutBindingInfo mBindingInfo;
         bool mIsBlueprint = false;
+        uint32_t mDynamicBufferCount = 0;
     };
 
 }  // namespace dawn_native
diff --git a/src/dawn_native/CommandEncoder.cpp b/src/dawn_native/CommandEncoder.cpp
index cc13ec2..58f22ee 100644
--- a/src/dawn_native/CommandEncoder.cpp
+++ b/src/dawn_native/CommandEncoder.cpp
@@ -543,12 +543,14 @@
                 dawn::BindingType type = layoutInfo.types[i];
 
                 switch (type) {
-                    case dawn::BindingType::UniformBuffer: {
+                    case dawn::BindingType::UniformBuffer:
+                    case dawn::BindingType::DynamicUniformBuffer: {
                         BufferBase* buffer = group->GetBindingAsBufferBinding(i).buffer;
                         tracker->BufferUsedAs(buffer, dawn::BufferUsageBit::Uniform);
                     } break;
 
-                    case dawn::BindingType::StorageBuffer: {
+                    case dawn::BindingType::StorageBuffer:
+                    case dawn::BindingType::DynamicStorageBuffer: {
                         BufferBase* buffer = group->GetBindingAsBufferBinding(i).buffer;
                         tracker->BufferUsedAs(buffer, dawn::BufferUsageBit::Storage);
                     } break;
@@ -560,12 +562,6 @@
 
                     case dawn::BindingType::Sampler:
                         break;
-
-                    // TODO(shaobo.yan@intel.com): Implement dynamic buffer offset
-                    case dawn::BindingType::DynamicUniformBuffer:
-                    case dawn::BindingType::DynamicStorageBuffer:
-                        UNREACHABLE();
-                        break;
                 }
             }
         }
@@ -1034,6 +1030,9 @@
 
                 case Command::SetBindGroup: {
                     SetBindGroupCmd* cmd = mIterator.NextCommand<SetBindGroupCmd>();
+                    if (cmd->dynamicOffsetCount > 0) {
+                        mIterator.NextData<uint64_t>(cmd->dynamicOffsetCount);
+                    }
 
                     TrackBindGroupResourceUsage(cmd->group.Get(), &usageTracker);
                     persistentState.SetBindGroup(cmd->index, cmd->group.Get());
@@ -1149,6 +1148,9 @@
 
                 case Command::SetBindGroup: {
                     SetBindGroupCmd* cmd = mIterator.NextCommand<SetBindGroupCmd>();
+                    if (cmd->dynamicOffsetCount > 0) {
+                        mIterator.NextData<uint64_t>(cmd->dynamicOffsetCount);
+                    }
 
                     TrackBindGroupResourceUsage(cmd->group.Get(), &usageTracker);
                     persistentState.SetBindGroup(cmd->index, cmd->group.Get());
diff --git a/src/dawn_native/Commands.cpp b/src/dawn_native/Commands.cpp
index 1bae002..98a978b 100644
--- a/src/dawn_native/Commands.cpp
+++ b/src/dawn_native/Commands.cpp
@@ -115,6 +115,9 @@
                 } break;
                 case Command::SetBindGroup: {
                     SetBindGroupCmd* cmd = commands->NextCommand<SetBindGroupCmd>();
+                    if (cmd->dynamicOffsetCount > 0) {
+                        commands->NextData<uint64_t>(cmd->dynamicOffsetCount);
+                    }
                     cmd->~SetBindGroupCmd();
                 } break;
                 case Command::SetIndexBuffer: {
diff --git a/src/dawn_native/Commands.h b/src/dawn_native/Commands.h
index 26e0b68..6dc25e2 100644
--- a/src/dawn_native/Commands.h
+++ b/src/dawn_native/Commands.h
@@ -190,6 +190,7 @@
     struct SetBindGroupCmd {
         uint32_t index;
         Ref<BindGroupBase> group;
+        uint32_t dynamicOffsetCount;
     };
 
     struct SetIndexBufferCmd {
diff --git a/src/dawn_native/ProgrammablePassEncoder.cpp b/src/dawn_native/ProgrammablePassEncoder.cpp
index bf9c451..3ae7b11 100644
--- a/src/dawn_native/ProgrammablePassEncoder.cpp
+++ b/src/dawn_native/ProgrammablePassEncoder.cpp
@@ -83,6 +83,8 @@
                                                BindGroupBase* group,
                                                uint32_t dynamicOffsetCount,
                                                const uint64_t* dynamicOffsets) {
+        const BindGroupLayoutBase* layout = group->GetLayout();
+
         if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands()) ||
             mTopLevelEncoder->ConsumedError(GetDevice()->ValidateObject(group))) {
             return;
@@ -93,15 +95,33 @@
             return;
         }
 
-        // TODO(shaobo.yan@intel.com): Implement dynamic buffer offset.
-        if (dynamicOffsetCount != 0) {
-            mTopLevelEncoder->HandleError("Dynamic Buffer Offset not supported yet");
-            return;
+        // Dynamic offsets count must match the number required by the layout perfectly.
+        if (layout->GetDynamicBufferCount() != dynamicOffsetCount) {
+            mTopLevelEncoder->HandleError("dynamicOffset count mismatch");
+        }
+
+        for (uint32_t i = 0; i < dynamicOffsetCount; ++i) {
+            if (dynamicOffsets[i] % kMinDynamicBufferOffsetAlignment != 0) {
+                mTopLevelEncoder->HandleError("Dynamic Buffer Offset need to be aligned");
+                return;
+            }
+
+            BufferBinding bufferBinding = group->GetBindingAsBufferBinding(i);
+
+            if (dynamicOffsets[i] >= bufferBinding.size - bufferBinding.offset) {
+                mTopLevelEncoder->HandleError("dynamic offset out of bounds");
+                return;
+            }
         }
 
         SetBindGroupCmd* cmd = mAllocator->Allocate<SetBindGroupCmd>(Command::SetBindGroup);
         cmd->index = groupIndex;
         cmd->group = group;
+        cmd->dynamicOffsetCount = dynamicOffsetCount;
+        if (dynamicOffsetCount > 0) {
+            uint64_t* offsets = mAllocator->AllocateData<uint64_t>(cmd->dynamicOffsetCount);
+            memcpy(offsets, dynamicOffsets, dynamicOffsetCount * sizeof(uint64_t));
+        }
     }
 
     void ProgrammablePassEncoder::SetPushConstants(dawn::ShaderStageBit stages,
diff --git a/src/dawn_native/ShaderModule.cpp b/src/dawn_native/ShaderModule.cpp
index 613eb35..598d4f6 100644
--- a/src/dawn_native/ShaderModule.cpp
+++ b/src/dawn_native/ShaderModule.cpp
@@ -66,6 +66,17 @@
         return {};
     }
 
+    dawn::BindingType NonDynamicBindingType(dawn::BindingType type) {
+        switch (type) {
+            case dawn::BindingType::DynamicUniformBuffer:
+                return dawn::BindingType::UniformBuffer;
+            case dawn::BindingType::DynamicStorageBuffer:
+                return dawn::BindingType::StorageBuffer;
+            default:
+                return type;
+        }
+    }
+
     // ShaderModuleBase
 
     ShaderModuleBase::ShaderModuleBase(DeviceBase* device,
@@ -283,14 +294,18 @@
         const auto& layoutInfo = layout->GetBindingInfo();
         for (size_t i = 0; i < kMaxBindingsPerGroup; ++i) {
             const auto& moduleInfo = mBindingInfo[group][i];
+            const auto& layoutBindingType = layoutInfo.types[i];
 
             if (!moduleInfo.used) {
                 continue;
             }
 
-            if (moduleInfo.type != layoutInfo.types[i]) {
+            // DynamicUniformBuffer and DynamicStorageBuffer are uniform buffer and
+            // storage buffer in shader. Need to translate them.
+            if (NonDynamicBindingType(layoutBindingType) != moduleInfo.type) {
                 return false;
             }
+
             if ((layoutInfo.visibilities[i] & StageBit(mExecutionModel)) == 0) {
                 return false;
             }
diff --git a/src/tests/unittests/validation/BindGroupValidationTests.cpp b/src/tests/unittests/validation/BindGroupValidationTests.cpp
index cd4f6c8..843d176 100644
--- a/src/tests/unittests/validation/BindGroupValidationTests.cpp
+++ b/src/tests/unittests/validation/BindGroupValidationTests.cpp
@@ -15,6 +15,7 @@
 #include "tests/unittests/validation/ValidationTest.h"
 
 #include "common/Constants.h"
+#include "utils/ComboRenderPipelineDescriptor.h"
 #include "utils/DawnHelpers.h"
 
 class BindGroupValidationTest : public ValidationTest {
@@ -446,3 +447,260 @@
     // Caching should cause these to be the same.
     ASSERT_EQ(layout1.Get(), layout2.Get());
 }
+
+constexpr uint32_t kBufferElementsCount = kMinDynamicBufferOffsetAlignment / sizeof(uint32_t) + 2;
+constexpr uint32_t kBufferSize = kBufferElementsCount * sizeof(uint32_t);
+
+class SetBindGroupValidationTest : public ValidationTest {
+  public:
+    void SetUp() override {
+        std::array<float, kBufferElementsCount> uniformData = {0};
+
+        uniformData[0] = 1.0;
+        uniformData[1] = 2.0;
+        uniformData[uniformData.size() - 2] = 5.0;
+        uniformData[uniformData.size() - 1] = 6.0;
+
+        dawn::BufferDescriptor bufferDescriptor;
+        bufferDescriptor.size = kBufferSize;
+        bufferDescriptor.usage = dawn::BufferUsageBit::Storage;
+
+        mUniformBuffer = utils::CreateBufferFromData(device, uniformData.data(), kBufferSize,
+                                                     dawn::BufferUsageBit::Uniform);
+        mStorageBuffer = device.CreateBuffer(&bufferDescriptor);
+
+        mBindGroupLayout = utils::MakeBindGroupLayout(
+            device, {{0, dawn::ShaderStageBit::Compute | dawn::ShaderStageBit::Fragment,
+                      dawn::BindingType::DynamicUniformBuffer},
+                     {1, dawn::ShaderStageBit::Compute | dawn::ShaderStageBit::Fragment,
+                      dawn::BindingType::DynamicStorageBuffer}});
+
+        mBindGroup = utils::MakeBindGroup(
+            device, mBindGroupLayout,
+            {{0, mUniformBuffer, 0, kBufferSize}, {1, mStorageBuffer, 0, kBufferSize}});
+    }
+    // Create objects to use as resources inside test bind groups.
+
+    dawn::BindGroup mBindGroup;
+    dawn::BindGroupLayout mBindGroupLayout;
+    dawn::Buffer mUniformBuffer;
+    dawn::Buffer mStorageBuffer;
+
+    dawn::RenderPipeline CreateRenderPipeline() {
+        dawn::ShaderModule vsModule =
+            utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, R"(
+                #version 450
+                void main() {
+                })");
+
+        dawn::ShaderModule fsModule =
+            utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"(
+                #version 450
+                layout(std140, set = 0, binding = 0) uniform uBuffer {
+                     vec2 value1;
+                };
+                layout(std140, set = 0, binding = 1) buffer SBuffer {
+                     vec2 value2;
+                } sBuffer;
+                layout(location = 0) out uvec4 fragColor;
+                void main() {
+                })");
+
+        utils::ComboRenderPipelineDescriptor pipelineDescriptor(device);
+        pipelineDescriptor.cVertexStage.module = vsModule;
+        pipelineDescriptor.cFragmentStage.module = fsModule;
+        dawn::PipelineLayout pipelineLayout =
+            utils::MakeBasicPipelineLayout(device, &mBindGroupLayout);
+        pipelineDescriptor.layout = pipelineLayout;
+        return device.CreateRenderPipeline(&pipelineDescriptor);
+    }
+
+    dawn::ComputePipeline CreateComputePipeline() {
+        dawn::ShaderModule csModule =
+            utils::CreateShaderModule(device, dawn::ShaderStage::Compute, R"(
+                #version 450
+                const uint kTileSize = 4;
+                const uint kInstances = 11;
+
+                layout(local_size_x = kTileSize, local_size_y = kTileSize, local_size_z = 1) in;
+                layout(std140, set = 0, binding = 0) uniform UniformBuffer {
+                    float value1;
+                };
+                layout(std140, set = 0, binding = 1) buffer SBuffer {
+                    float value2;
+                } dst;
+
+        void main() {
+        })");
+
+        dawn::ComputePipelineDescriptor csDesc;
+        dawn::PipelineLayout pipelineLayout =
+            utils::MakeBasicPipelineLayout(device, &mBindGroupLayout);
+        csDesc.layout = pipelineLayout;
+
+        dawn::PipelineStageDescriptor computeStage;
+        computeStage.module = csModule;
+        computeStage.entryPoint = "main";
+        csDesc.computeStage = &computeStage;
+
+        return device.CreateComputePipeline(&csDesc);
+    }
+};
+
+// This is the test case that should work.
+TEST_F(SetBindGroupValidationTest, Basic) {
+    std::array<uint64_t, 2> offsets = {256, 0};
+
+    // RenderPipeline SetBindGroup
+    {
+        dawn::RenderPipeline renderPipeline = CreateRenderPipeline();
+        DummyRenderPass renderPass(device);
+
+        dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
+        dawn::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass);
+        renderPassEncoder.SetPipeline(renderPipeline);
+        renderPassEncoder.SetBindGroup(0, mBindGroup, 2, offsets.data());
+        renderPassEncoder.Draw(3, 1, 0, 0);
+        renderPassEncoder.EndPass();
+        commandEncoder.Finish();
+    }
+
+    {
+        dawn::ComputePipeline computePipeline = CreateComputePipeline();
+
+        dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
+        dawn::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass();
+        computePassEncoder.SetPipeline(computePipeline);
+        computePassEncoder.SetBindGroup(0, mBindGroup, 2, offsets.data());
+        computePassEncoder.Dispatch(1, 1, 1);
+        computePassEncoder.EndPass();
+        commandEncoder.Finish();
+    }
+}
+
+// Test cases that test dynamic offsets count mismatch with bind group layout.
+TEST_F(SetBindGroupValidationTest, DynamicOffsetsMismatch) {
+    std::array<uint64_t, 1> mismatchOffsets = {0};
+
+    // RenderPipeline SetBindGroup
+    {
+        dawn::RenderPipeline pipeline = CreateRenderPipeline();
+        DummyRenderPass renderPass(device);
+
+        dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
+        dawn::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass);
+        renderPassEncoder.SetPipeline(pipeline);
+        renderPassEncoder.SetBindGroup(0, mBindGroup, 1, mismatchOffsets.data());
+        renderPassEncoder.Draw(3, 1, 0, 0);
+        renderPassEncoder.EndPass();
+        ASSERT_DEVICE_ERROR(commandEncoder.Finish());
+    }
+
+    {
+        dawn::ComputePipeline computePipeline = CreateComputePipeline();
+
+        dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
+        dawn::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass();
+        computePassEncoder.SetPipeline(computePipeline);
+        computePassEncoder.SetBindGroup(0, mBindGroup, 1, mismatchOffsets.data());
+        computePassEncoder.Dispatch(1, 1, 1);
+        computePassEncoder.EndPass();
+        ASSERT_DEVICE_ERROR(commandEncoder.Finish());
+    }
+}
+
+// Test cases that test dynamic offsets not aligned
+TEST_F(SetBindGroupValidationTest, DynamicOffsetsNotAligned) {
+    std::array<uint64_t, 2> notAlignedOffsets = {1, 2};
+
+    // RenderPipeline SetBindGroup
+    {
+        dawn::RenderPipeline pipeline = CreateRenderPipeline();
+        DummyRenderPass renderPass(device);
+
+        dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
+        dawn::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass);
+        renderPassEncoder.SetPipeline(pipeline);
+        renderPassEncoder.SetBindGroup(0, mBindGroup, 2, notAlignedOffsets.data());
+        renderPassEncoder.Draw(3, 1, 0, 0);
+        renderPassEncoder.EndPass();
+        ASSERT_DEVICE_ERROR(commandEncoder.Finish());
+    }
+
+    // ComputePipeline SetBindGroup
+    {
+        dawn::ComputePipeline pipeline = CreateComputePipeline();
+
+        dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
+        dawn::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass();
+        computePassEncoder.SetPipeline(pipeline);
+        computePassEncoder.SetBindGroup(0, mBindGroup, 2, notAlignedOffsets.data());
+        computePassEncoder.Dispatch(1, 1, 1);
+        computePassEncoder.EndPass();
+        ASSERT_DEVICE_ERROR(commandEncoder.Finish());
+    }
+}
+
+// Test cases that test dynamic uniform buffer out of bound situation.
+TEST_F(SetBindGroupValidationTest, OutOfBoundDynamicUniformBuffer) {
+    std::array<uint64_t, 2> overFlowOffsets = {512, 0};
+
+    // RenderPipeline SetBindGroup
+    {
+        dawn::RenderPipeline pipeline = CreateRenderPipeline();
+        DummyRenderPass renderPass(device);
+
+        dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
+        dawn::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass);
+        renderPassEncoder.SetPipeline(pipeline);
+        renderPassEncoder.SetBindGroup(0, mBindGroup, 2, overFlowOffsets.data());
+        renderPassEncoder.Draw(3, 1, 0, 0);
+        renderPassEncoder.EndPass();
+        ASSERT_DEVICE_ERROR(commandEncoder.Finish());
+    }
+
+    // ComputePipeline SetBindGroup
+    {
+        dawn::ComputePipeline pipeline = CreateComputePipeline();
+
+        dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
+        dawn::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass();
+        computePassEncoder.SetPipeline(pipeline);
+        computePassEncoder.SetBindGroup(0, mBindGroup, 2, overFlowOffsets.data());
+        computePassEncoder.Dispatch(1, 1, 1);
+        computePassEncoder.EndPass();
+        ASSERT_DEVICE_ERROR(commandEncoder.Finish());
+    }
+}
+
+// Test cases that test dynamic storage buffer out of bound situation.
+TEST_F(SetBindGroupValidationTest, OutOfBoundDynamicStorageBuffer) {
+    std::array<uint64_t, 2> overFlowOffsets = {0, 512};
+
+    // RenderPipeline SetBindGroup
+    {
+        dawn::RenderPipeline pipeline = CreateRenderPipeline();
+        DummyRenderPass renderPass(device);
+
+        dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
+        dawn::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass);
+        renderPassEncoder.SetPipeline(pipeline);
+        renderPassEncoder.SetBindGroup(0, mBindGroup, 2, overFlowOffsets.data());
+        renderPassEncoder.Draw(3, 1, 0, 0);
+        renderPassEncoder.EndPass();
+        ASSERT_DEVICE_ERROR(commandEncoder.Finish());
+    }
+
+    // ComputePipeline SetBindGroup
+    {
+        dawn::ComputePipeline pipeline = CreateComputePipeline();
+
+        dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
+        dawn::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass();
+        computePassEncoder.SetPipeline(pipeline);
+        computePassEncoder.SetBindGroup(0, mBindGroup, 2, overFlowOffsets.data());
+        computePassEncoder.Dispatch(1, 1, 1);
+        computePassEncoder.EndPass();
+        ASSERT_DEVICE_ERROR(commandEncoder.Finish());
+    }
+}