diff --git a/src/dawn_native/d3d12/CommandBufferD3D12.cpp b/src/dawn_native/d3d12/CommandBufferD3D12.cpp index e041f8c774..dc9d5f0ab2 100644 --- a/src/dawn_native/d3d12/CommandBufferD3D12.cpp +++ b/src/dawn_native/d3d12/CommandBufferD3D12.cpp @@ -154,21 +154,24 @@ namespace dawn_native { namespace d3d12 { uint32_t dynamicOffsetCount, uint64_t* dynamicOffsets, bool force = false) { - if (mBindGroups[index] != group || force) { - mBindGroups[index] = group; - uint32_t currentDynamicBufferIndex = 0; - + // Usually, the application won't set the same offsets many times, + // so always try to apply dynamic offsets even if the offsets stay the same + if (dynamicOffsetCount) { + // Update dynamic offsets const BindGroupLayout::LayoutBindingInfo& layout = group->GetLayout()->GetBindingInfo(); + uint32_t currentDynamicBufferIndex = 0; + for (uint32_t bindingIndex : IterateBitSet(layout.dynamic)) { ASSERT(dynamicOffsetCount > 0); uint32_t parameterIndex = pipelineLayout->GetDynamicRootParameterIndex(index, bindingIndex); BufferBinding binding = group->GetBindingAsBufferBinding(bindingIndex); - // Calculate buffer locations that root descriptors links to. The location is - // (base buffer location + initial offset + dynamic offset) - uint64_t offset = dynamicOffsets[currentDynamicBufferIndex++] + binding.offset; + // Calculate buffer locations that root descriptors links to. The location + // is (base buffer location + initial offset + dynamic offset) + uint64_t dynamicOffset = dynamicOffsets[currentDynamicBufferIndex]; + uint64_t offset = binding.offset + dynamicOffset; D3D12_GPU_VIRTUAL_ADDRESS bufferLocation = ToBackend(binding.buffer)->GetVA() + offset; @@ -198,7 +201,15 @@ namespace dawn_native { namespace d3d12 { UNREACHABLE(); break; } + + // Record current dynamic offsets for inheriting + mLastDynamicOffsets[index][currentDynamicBufferIndex] = dynamicOffset; + ++currentDynamicBufferIndex; } + } + + if (mBindGroups[index] != group || force) { + mBindGroups[index] = group; uint32_t cbvUavSrvCount = ToBackend(group->GetLayout())->GetCbvUavSrvDescriptorCount(); uint32_t samplerCount = ToBackend(group->GetLayout())->GetSamplerDescriptorCount(); @@ -244,12 +255,11 @@ namespace dawn_native { namespace d3d12 { for (uint32_t i = 0; i < inheritUntil; ++i) { const BindGroupLayout* layout = ToBackend(mBindGroups[i]->GetLayout()); const uint32_t dynamicBufferCount = layout->GetDynamicBufferCount(); - // TODO(shaobo.yan@intel.com) : Need to handle dynamic resources inherited with last - // dynamic offsets. + + // Inherit dynamic offsets if (dynamicBufferCount > 0) { - std::vector zeroOffsets(dynamicBufferCount, 0); SetBindGroup(commandList, newLayout, mBindGroups[i], i, dynamicBufferCount, - zeroOffsets.data(), true); + mLastDynamicOffsets[i].data(), true); } else { SetBindGroup(commandList, newLayout, mBindGroups[i], i, 0, nullptr, true); } @@ -280,6 +290,8 @@ namespace dawn_native { namespace d3d12 { uint32_t mSamplerDescriptorHeapSize = 0; std::array mBindGroups = {}; std::deque mBindGroupsList = {}; + std::array, kMaxBindGroups> + mLastDynamicOffsets; bool mInCompute = false; DescriptorHeapHandle mCbvSrvUavGPUDescriptorHeap = {}; @@ -805,9 +817,11 @@ namespace dawn_native { namespace d3d12 { SetBindGroupCmd* cmd = mCommands.NextCommand(); BindGroup* group = ToBackend(cmd->group.Get()); uint64_t* dynamicOffsets = nullptr; + if (cmd->dynamicOffsetCount > 0) { dynamicOffsets = mCommands.NextData(cmd->dynamicOffsetCount); } + bindingTracker->SetBindGroup(commandList, lastLayout, group, cmd->index, cmd->dynamicOffsetCount, dynamicOffsets); } break; @@ -1104,6 +1118,7 @@ namespace dawn_native { namespace d3d12 { SetBindGroupCmd* cmd = mCommands.NextCommand(); BindGroup* group = ToBackend(cmd->group.Get()); uint64_t* dynamicOffsets = nullptr; + if (cmd->dynamicOffsetCount > 0) { dynamicOffsets = mCommands.NextData(cmd->dynamicOffsetCount); } diff --git a/src/dawn_native/d3d12/PipelineLayoutD3D12.cpp b/src/dawn_native/d3d12/PipelineLayoutD3D12.cpp index f5ae7c72ef..6cbb3085c9 100644 --- a/src/dawn_native/d3d12/PipelineLayoutD3D12.cpp +++ b/src/dawn_native/d3d12/PipelineLayoutD3D12.cpp @@ -165,7 +165,7 @@ namespace dawn_native { namespace d3d12 { return mSamplerRootParameterInfo[group]; } - ComPtr PipelineLayout::GetRootSignature() { + ComPtr PipelineLayout::GetRootSignature() const { return mRootSignature; } diff --git a/src/dawn_native/d3d12/PipelineLayoutD3D12.h b/src/dawn_native/d3d12/PipelineLayoutD3D12.h index 2b8358caee..b2ee9e6bbd 100644 --- a/src/dawn_native/d3d12/PipelineLayoutD3D12.h +++ b/src/dawn_native/d3d12/PipelineLayoutD3D12.h @@ -33,7 +33,7 @@ namespace dawn_native { namespace d3d12 { // Returns the index of the root parameter reserved for a dynamic buffer binding uint32_t GetDynamicRootParameterIndex(uint32_t group, uint32_t binding) const; - ComPtr GetRootSignature(); + ComPtr GetRootSignature() const; private: std::array mCbvUavSrvRootParameterInfo; diff --git a/src/tests/end2end/DynamicBufferOffsetTests.cpp b/src/tests/end2end/DynamicBufferOffsetTests.cpp index f55b9e5b94..46c888e35d 100644 --- a/src/tests/end2end/DynamicBufferOffsetTests.cpp +++ b/src/tests/end2end/DynamicBufferOffsetTests.cpp @@ -52,7 +52,7 @@ class DynamicBufferOffsetTests : public DawnTest { mDynamicStorageBuffer = device.CreateBuffer(&storageBufferDescriptor); - mBindGroupLayout = utils::MakeBindGroupLayout( + mDefaultBindGroupLayout = utils::MakeBindGroupLayout( device, {{0, dawn::ShaderStageBit::Compute | dawn::ShaderStageBit::Fragment, dawn::BindingType::UniformBuffer}, {1, dawn::ShaderStageBit::Compute | dawn::ShaderStageBit::Fragment, @@ -62,7 +62,9 @@ class DynamicBufferOffsetTests : public DawnTest { {4, dawn::ShaderStageBit::Compute | dawn::ShaderStageBit::Fragment, dawn::BindingType::StorageBuffer, true}}); - mBindGroup = utils::MakeBindGroup(device, mBindGroupLayout, + mDefaultPipelineLayout = utils::MakeBasicPipelineLayout(device, &mDefaultBindGroupLayout); + + mBindGroup = utils::MakeBindGroup(device, mDefaultBindGroupLayout, {{0, mUniformBuffer, 0, kBindingSize}, {1, mStorageBuffer, 0, kBindingSize}, {3, mDynamicUniformBuffer, 0, kBindingSize}, @@ -71,58 +73,16 @@ class DynamicBufferOffsetTests : public DawnTest { // Create objects to use as resources inside test bind groups. dawn::BindGroup mBindGroup; - dawn::BindGroupLayout mBindGroupLayout; + dawn::BindGroupLayout mDefaultBindGroupLayout; + dawn::PipelineLayout mDefaultPipelineLayout; dawn::Buffer mUniformBuffer; dawn::Buffer mStorageBuffer; dawn::Buffer mDynamicUniformBuffer; dawn::Buffer mDynamicStorageBuffer; dawn::Texture mColorAttachment; - dawn::RenderPipeline CreateRenderPipeline() { - dawn::ShaderModule vsModule = - utils::CreateShaderModule(device, utils::ShaderStage::Vertex, R"( - #version 450 - void main() { - const vec2 pos[3] = vec2[3](vec2(-1.0f, 0.0f), vec2(-1.0f, -1.0f), vec2(0.0f, -1.0f)); - gl_Position = vec4(pos[gl_VertexIndex], 0.0, 1.0); - })"); - - dawn::ShaderModule fsModule = - utils::CreateShaderModule(device, utils::ShaderStage::Fragment, R"( - #version 450 - layout(std140, set = 0, binding = 0) uniform uBufferNotDynamic { - uvec2 notDynamicValue; - }; - layout(std140, set = 0, binding = 1) buffer sBufferNotDynamic { - uvec2 notDynamicResult; - } mid; - layout(std140, set = 0, binding = 3) uniform uBuffer { - uvec2 value; - }; - layout(std140, set = 0, binding = 4) buffer SBuffer { - uvec2 result; - } sBuffer; - layout(location = 0) out vec4 fragColor; - void main() { - mid.notDynamicResult.xy = notDynamicValue.xy; - sBuffer.result.xy = value.xy + mid.notDynamicResult.xy; - fragColor = vec4(value.x / 255.0f, value.y / 255.0f, 1.0f, 1.0f); - })"); - - utils::ComboRenderPipelineDescriptor pipelineDescriptor(device); - pipelineDescriptor.cVertexStage.module = vsModule; - pipelineDescriptor.cFragmentStage.module = fsModule; - pipelineDescriptor.cColorStates[0]->format = dawn::TextureFormat::RGBA8Unorm; - dawn::PipelineLayout pipelineLayout = - utils::MakeBasicPipelineLayout(device, &mBindGroupLayout); - pipelineDescriptor.layout = pipelineLayout; - - return device.CreateRenderPipeline(&pipelineDescriptor); - } - - dawn::ComputePipeline CreateComputePipeline() { - dawn::ShaderModule csModule = - utils::CreateShaderModule(device, utils::ShaderStage::Compute, R"( + dawn::ShaderModule CreateDefaultCsModule() { + return utils::CreateShaderModule(device, utils::ShaderStage::Compute, R"( #version 450 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; layout(std140, set = 0, binding = 0) uniform uBufferNotDynamic { @@ -142,24 +102,135 @@ class DynamicBufferOffsetTests : public DawnTest { mid.notDynamicResult.xy = notDynamicValue.xy; sBuffer.result.xy = value.xy + mid.notDynamicResult.xy; })"); + } + dawn::ShaderModule CreateDefaultVsModule() { + return utils::CreateShaderModule(device, utils::ShaderStage::Vertex, R"( + #version 450 + void main() { + const vec2 pos[3] = vec2[3](vec2(-1.0f, 0.0f), vec2(-1.0f, -1.0f), vec2(0.0f, -1.0f)); + gl_Position = vec4(pos[gl_VertexIndex], 0.0, 1.0); + })"); + } + + dawn::ShaderModule CreateDefaultFsModule() { + return utils::CreateShaderModule(device, utils::ShaderStage::Fragment, R"( + #version 450 + layout(std140, set = 0, binding = 0) uniform uBufferNotDynamic { + uvec2 notDynamicValue; + }; + layout(std140, set = 0, binding = 1) buffer sBufferNotDynamic { + uvec2 notDynamicResult; + } mid; + layout(std140, set = 0, binding = 3) uniform uBuffer { + uvec2 value; + }; + layout(std140, set = 0, binding = 4) buffer SBuffer { + uvec2 result; + } sBuffer; + layout(location = 0) out vec4 fragColor; + void main() { + mid.notDynamicResult.xy = notDynamicValue.xy; + sBuffer.result.xy = value.xy + mid.notDynamicResult.xy; + fragColor = vec4(value.x / 255.0f, value.y / 255.0f, 1.0f, 1.0f); + })"); + } + + dawn::ShaderModule CreateInheritCsModule() { + return utils::CreateShaderModule(device, utils::ShaderStage::Compute, R"( + #version 450 + layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + layout(std140, set = 0, binding = 0) uniform uBufferNotDynamic { + uvec2 notDynamicValue; + }; + layout(std140, set = 0, binding = 1) buffer sBufferNotDynamic { + uvec2 notDynamicResult; + } mid; + layout(std140, set = 0, binding = 3) uniform uBuffer { + uvec2 value; + }; + layout(std140, set = 0, binding = 4) buffer SBuffer { + uvec2 result; + } sBuffer; + layout(std140, set = 1, binding = 0) uniform paddingBlock { + uvec2 padding; + }; + + void main() { + mid.notDynamicResult.xy = notDynamicValue.xy; + sBuffer.result.xy = 2 * (value.xy + mid.notDynamicResult.xy); + })"); + } + + dawn::ShaderModule CreateInheritFsModule() { + return utils::CreateShaderModule(device, utils::ShaderStage::Fragment, R"( + #version 450 + layout(std140, set = 0, binding = 0) uniform uBufferNotDynamic { + uvec2 notDynamicValue; + }; + layout(std140, set = 0, binding = 1) buffer sBufferNotDynamic { + uvec2 notDynamicResult; + } mid; + layout(std140, set = 0, binding = 3) uniform uBuffer { + uvec2 value; + }; + layout(std140, set = 0, binding = 4) buffer SBuffer { + uvec2 result; + } sBuffer; + layout(std140, set = 1, binding = 0) uniform paddingBlock { + uvec2 padding; + }; + layout(location = 0) out vec4 fragColor; + + void main() { + mid.notDynamicResult.xy = notDynamicValue.xy; + sBuffer.result.xy = 2 * (value.xy + mid.notDynamicResult.xy); + fragColor = vec4(value.x / 255.0f, value.y / 255.0f, 1.0f, 1.0f); + })"); + } + + dawn::RenderPipeline CreateRenderPipeline(dawn::PipelineLayout layout, + dawn::ShaderModule vs, + dawn::ShaderModule fs) { + utils::ComboRenderPipelineDescriptor pipelineDescriptor(device); + pipelineDescriptor.cVertexStage.module = vs; + pipelineDescriptor.cFragmentStage.module = fs; + pipelineDescriptor.cColorStates[0]->format = dawn::TextureFormat::RGBA8Unorm; + pipelineDescriptor.layout = layout; + + return device.CreateRenderPipeline(&pipelineDescriptor); + } + + dawn::ComputePipeline CreateComputePipeline(dawn::PipelineLayout layout, + dawn::ShaderModule cs) { dawn::ComputePipelineDescriptor csDesc; - dawn::PipelineLayout pipelineLayout = - utils::MakeBasicPipelineLayout(device, &mBindGroupLayout); - csDesc.layout = pipelineLayout; + csDesc.layout = layout; dawn::PipelineStageDescriptor computeStage; - computeStage.module = csModule; + computeStage.module = cs; computeStage.entryPoint = "main"; csDesc.computeStage = &computeStage; return device.CreateComputePipeline(&csDesc); } + + dawn::RenderPipeline CreateDefaultRenderPipeline() { + dawn::ShaderModule vs = this->CreateDefaultVsModule(); + dawn::ShaderModule fs = this->CreateDefaultFsModule(); + + return this->CreateRenderPipeline(mDefaultPipelineLayout, vs, fs); + } + + dawn::ComputePipeline CreateDefaultComputePipeline() { + dawn::ShaderModule cs = this->CreateDefaultCsModule(); + + return this->CreateComputePipeline(mDefaultPipelineLayout, cs); + } }; // Dynamic offsets are all zero and no effect to result. TEST_P(DynamicBufferOffsetTests, BasicRenderPipeline) { - dawn::RenderPipeline pipeline = CreateRenderPipeline(); + dawn::RenderPipeline pipeline = CreateDefaultRenderPipeline(); utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder(); @@ -180,7 +251,7 @@ TEST_P(DynamicBufferOffsetTests, BasicRenderPipeline) { // Have non-zero dynamic offsets. TEST_P(DynamicBufferOffsetTests, SetDynamicOffestsRenderPipeline) { - dawn::RenderPipeline pipeline = CreateRenderPipeline(); + dawn::RenderPipeline pipeline = CreateDefaultRenderPipeline(); utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder(); @@ -203,7 +274,7 @@ TEST_P(DynamicBufferOffsetTests, SetDynamicOffestsRenderPipeline) { // Dynamic offsets are all zero and no effect to result. TEST_P(DynamicBufferOffsetTests, BasicComputePipeline) { - dawn::ComputePipeline pipeline = CreateComputePipeline(); + dawn::ComputePipeline pipeline = CreateDefaultComputePipeline(); std::array offsets = {0, 0}; @@ -222,7 +293,7 @@ TEST_P(DynamicBufferOffsetTests, BasicComputePipeline) { // Have non-zero dynamic offsets. TEST_P(DynamicBufferOffsetTests, SetDynamicOffestsComputePipeline) { - dawn::ComputePipeline pipeline = CreateComputePipeline(); + dawn::ComputePipeline pipeline = CreateDefaultComputePipeline(); std::array offsets = {kMinDynamicBufferOffsetAlignment, kMinDynamicBufferOffsetAlignment}; @@ -241,6 +312,147 @@ TEST_P(DynamicBufferOffsetTests, SetDynamicOffestsComputePipeline) { kMinDynamicBufferOffsetAlignment, expectedData.size()); } +// Test inherit dynamic offsets on render pipeline +TEST_P(DynamicBufferOffsetTests, InheritDynamicOffestsRenderPipeline) { + // Using default pipeline and setting dynamic offsets + dawn::RenderPipeline pipeline = CreateDefaultRenderPipeline(); + + dawn::ShaderModule testVs = CreateDefaultVsModule(); + dawn::ShaderModule testFs = CreateInheritFsModule(); + dawn::BindGroupLayout bgl[2]; + bgl[0] = mDefaultBindGroupLayout; + bgl[1] = utils::MakeBindGroupLayout( + device, {{0, dawn::ShaderStageBit::Fragment, dawn::BindingType::UniformBuffer}}); + dawn::PipelineLayoutDescriptor descriptor; + descriptor.bindGroupLayoutCount = 2; + descriptor.bindGroupLayouts = bgl; + dawn::PipelineLayout layout = device.CreatePipelineLayout(&descriptor); + dawn::RenderPipeline testPipeline = CreateRenderPipeline(layout, testVs, testFs); + + std::array uniformData = {0}; + + dawn::Buffer uniformBuffer = utils::CreateBufferFromData( + device, uniformData.data(), kBufferSize, dawn::BufferUsageBit::Uniform); + dawn::BindGroup bindGroup = + utils::MakeBindGroup(device, bgl[1], {{0, uniformBuffer, 0, kBindingSize}}); + + utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); + + dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + std::array offsets = {kMinDynamicBufferOffsetAlignment, + kMinDynamicBufferOffsetAlignment}; + dawn::RenderPassEncoder renderPassEncoder = + commandEncoder.BeginRenderPass(&renderPass.renderPassInfo); + renderPassEncoder.SetPipeline(pipeline); + renderPassEncoder.SetBindGroup(0, mBindGroup, offsets.size(), offsets.data()); + renderPassEncoder.Draw(3, 1, 0, 0); + renderPassEncoder.SetPipeline(testPipeline); + renderPassEncoder.SetBindGroup(1, bindGroup, 0, nullptr); + renderPassEncoder.Draw(3, 1, 0, 0); + renderPassEncoder.EndPass(); + dawn::CommandBuffer commands = commandEncoder.Finish(); + queue.Submit(1, &commands); + + std::vector expectedData = {12, 16}; + EXPECT_PIXEL_RGBA8_EQ(RGBA8(5, 6, 255, 255), renderPass.color, 0, 0); + EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), mDynamicStorageBuffer, + kMinDynamicBufferOffsetAlignment, expectedData.size()); +} + +// Test inherit dynamic offsets on compute pipeline +TEST_P(DynamicBufferOffsetTests, InheritDynamicOffestsComputePipeline) { + dawn::ComputePipeline pipeline = CreateDefaultComputePipeline(); + + dawn::ShaderModule testCs = CreateInheritCsModule(); + dawn::BindGroupLayout bgl[2]; + bgl[0] = mDefaultBindGroupLayout; + bgl[1] = utils::MakeBindGroupLayout( + device, {{0, dawn::ShaderStageBit::Compute, dawn::BindingType::UniformBuffer}}); + dawn::PipelineLayoutDescriptor descriptor; + descriptor.bindGroupLayoutCount = 2; + descriptor.bindGroupLayouts = bgl; + dawn::PipelineLayout layout = device.CreatePipelineLayout(&descriptor); + dawn::ComputePipeline testPipeline = CreateComputePipeline(layout, testCs); + + std::array uniformData = {0}; + + dawn::Buffer uniformBuffer = utils::CreateBufferFromData( + device, uniformData.data(), kBufferSize, dawn::BufferUsageBit::Uniform); + dawn::BindGroup bindGroup = + utils::MakeBindGroup(device, bgl[1], {{0, uniformBuffer, 0, kBindingSize}}); + + std::array offsets = {kMinDynamicBufferOffsetAlignment, + kMinDynamicBufferOffsetAlignment}; + + dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + dawn::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass(); + computePassEncoder.SetPipeline(pipeline); + computePassEncoder.SetBindGroup(0, mBindGroup, offsets.size(), offsets.data()); + computePassEncoder.Dispatch(1, 1, 1); + computePassEncoder.SetPipeline(testPipeline); + computePassEncoder.SetBindGroup(1, bindGroup, 0, nullptr); + computePassEncoder.Dispatch(1, 1, 1); + computePassEncoder.EndPass(); + dawn::CommandBuffer commands = commandEncoder.Finish(); + queue.Submit(1, &commands); + + std::vector expectedData = {12, 16}; + EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), mDynamicStorageBuffer, + kMinDynamicBufferOffsetAlignment, expectedData.size()); +} + +// Setting multiple dynamic offsets for the same bindgroup in one render pass. +TEST_P(DynamicBufferOffsetTests, UpdateDynamicOffestsMultipleTimesRenderPipeline) { + // Using default pipeline and setting dynamic offsets + dawn::RenderPipeline pipeline = CreateDefaultRenderPipeline(); + + utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); + + dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + std::array offsets = {kMinDynamicBufferOffsetAlignment, + kMinDynamicBufferOffsetAlignment}; + std::array testOffsets = {0, 0}; + + dawn::RenderPassEncoder renderPassEncoder = + commandEncoder.BeginRenderPass(&renderPass.renderPassInfo); + renderPassEncoder.SetPipeline(pipeline); + renderPassEncoder.SetBindGroup(0, mBindGroup, offsets.size(), offsets.data()); + renderPassEncoder.Draw(3, 1, 0, 0); + renderPassEncoder.SetBindGroup(0, mBindGroup, testOffsets.size(), testOffsets.data()); + renderPassEncoder.Draw(3, 1, 0, 0); + renderPassEncoder.EndPass(); + dawn::CommandBuffer commands = commandEncoder.Finish(); + queue.Submit(1, &commands); + + std::vector expectedData = {2, 4}; + EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 255, 255), renderPass.color, 0, 0); + EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), mDynamicStorageBuffer, 0, expectedData.size()); +} + +// Setting multiple dynamic offsets for the same bindgroup in one compute pass. +// TODO(shaobo.yan@intel.com) : enable this test after resolving dawn issue 198. +TEST_P(DynamicBufferOffsetTests, DISABLED_UpdateDynamicOffestsMultipleTimesComputePipeline) { + dawn::ComputePipeline pipeline = CreateDefaultComputePipeline(); + + std::array offsets = {kMinDynamicBufferOffsetAlignment, + kMinDynamicBufferOffsetAlignment}; + std::array testOffsets = {0, 0}; + + dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + dawn::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass(); + computePassEncoder.SetPipeline(pipeline); + computePassEncoder.SetBindGroup(0, mBindGroup, offsets.size(), offsets.data()); + computePassEncoder.Dispatch(1, 1, 1); + computePassEncoder.SetBindGroup(0, mBindGroup, testOffsets.size(), testOffsets.data()); + computePassEncoder.Dispatch(1, 1, 1); + computePassEncoder.EndPass(); + dawn::CommandBuffer commands = commandEncoder.Finish(); + queue.Submit(1, &commands); + + std::vector expectedData = {2, 4}; + EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), mDynamicStorageBuffer, 0, expectedData.size()); +} + DAWN_INSTANTIATE_TEST(DynamicBufferOffsetTests, D3D12Backend, MetalBackend,