From 421584173cae282ee496291b1cb08eebcad71be7 Mon Sep 17 00:00:00 2001 From: "Yan, Shaobo" Date: Wed, 7 Aug 2019 18:20:33 +0000 Subject: [PATCH] Fix dynamic buffer offset issues on D3D12 backend This patch fixes 1. Inherit dynamic buffer offsets : dawn chooses vulkan like inherit behaviour, so dynamic offsets need to be inherited. This patch adds inherit dynamic offsets between pipelines support and adds tests to cover it. 2. Dynamic offsets are skipped when groups have been set : in D3D12 backend, when invoke SetBindGroup, dawn will check whether this group has already been set and skip updating root signature if the answer is yes. However, this behaviour will affect dynamic offsets update. With the latest patch, we always update dynamic offsets, even if they didn't change and adds tests to cover it. This patch also hit a dawn's issue about storage buffer validation in compute pass. Currently the validation is a workaround to avoid access conflicts but will impact using dynamic buffer offset in compute pipeline. Fix this issue is hard so disable related test for now and will enable it after the issue been fixed. File dawn bug 198 to track this BUG=dawn:55 Change-Id: I2b0f179b3555d37d5b350292eb729767b0d60ab6 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/9621 Commit-Queue: Austin Eng Reviewed-by: Austin Eng Reviewed-by: Kai Ninomiya --- src/dawn_native/d3d12/CommandBufferD3D12.cpp | 37 +- src/dawn_native/d3d12/PipelineLayoutD3D12.cpp | 2 +- src/dawn_native/d3d12/PipelineLayoutD3D12.h | 2 +- .../end2end/DynamicBufferOffsetTests.cpp | 324 +++++++++++++++--- 4 files changed, 296 insertions(+), 69 deletions(-) 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,