From 40618d0b93aa46edc2906ef318d50fb1cf4fe80a Mon Sep 17 00:00:00 2001 From: Austin Eng Date: Wed, 14 Aug 2019 02:01:14 +0000 Subject: [PATCH] Implement RenderBundle in the backend Bug: dawn:154 Change-Id: I45496fb2103150dabe32fbc7cb5856dc40c9339f Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/9222 Reviewed-by: Kai Ninomiya Commit-Queue: Austin Eng --- BUILD.gn | 1 + src/dawn_native/d3d12/CommandBufferD3D12.cpp | 318 +++++++++++-------- src/dawn_native/metal/CommandBufferMTL.mm | 111 ++++--- src/dawn_native/opengl/CommandBufferGL.cpp | 102 +++--- src/dawn_native/vulkan/CommandBufferVk.cpp | 120 ++++--- src/tests/end2end/RenderBundleTests.cpp | 210 ++++++++++++ 6 files changed, 590 insertions(+), 272 deletions(-) create mode 100644 src/tests/end2end/RenderBundleTests.cpp diff --git a/BUILD.gn b/BUILD.gn index 492ea22259..fe9b67c35b 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -769,6 +769,7 @@ source_set("dawn_end2end_tests_sources") { "src/tests/end2end/ObjectCachingTests.cpp", "src/tests/end2end/OpArrayLengthTests.cpp", "src/tests/end2end/PrimitiveTopologyTests.cpp", + "src/tests/end2end/RenderBundleTests.cpp", "src/tests/end2end/RenderPassLoadOpTests.cpp", "src/tests/end2end/RenderPassTests.cpp", "src/tests/end2end/SamplerTests.cpp", diff --git a/src/dawn_native/d3d12/CommandBufferD3D12.cpp b/src/dawn_native/d3d12/CommandBufferD3D12.cpp index 095b410b70..150dd6366f 100644 --- a/src/dawn_native/d3d12/CommandBufferD3D12.cpp +++ b/src/dawn_native/d3d12/CommandBufferD3D12.cpp @@ -17,6 +17,7 @@ #include "common/Assert.h" #include "dawn_native/CommandEncoder.h" #include "dawn_native/Commands.h" +#include "dawn_native/RenderBundle.h" #include "dawn_native/d3d12/BindGroupD3D12.h" #include "dawn_native/d3d12/BindGroupLayoutD3D12.h" #include "dawn_native/d3d12/BufferD3D12.h" @@ -398,7 +399,7 @@ namespace dawn_native { namespace d3d12 { Command type; PipelineLayout* lastLayout = nullptr; - while (commands->NextCommandId(&type)) { + auto HandleCommand = [&](CommandIterator* commands, Command type) { switch (type) { case Command::SetComputePipeline: { SetComputePipelineCmd* cmd = @@ -431,6 +432,26 @@ namespace dawn_native { namespace d3d12 { default: SkipCommand(commands, type); } + }; + + while (commands->NextCommandId(&type)) { + switch (type) { + case Command::ExecuteBundles: { + ExecuteBundlesCmd* cmd = commands->NextCommand(); + auto bundles = commands->NextData>(cmd->count); + + for (uint32_t i = 0; i < cmd->count; ++i) { + CommandIterator* commands = bundles[i]->GetCommands(); + commands->Reset(); + while (commands->NextCommandId(&type)) { + HandleCommand(commands, type); + } + } + } break; + default: + HandleCommand(commands, type); + break; + } } commands->Reset(); @@ -987,6 +1008,155 @@ namespace dawn_native { namespace d3d12 { PipelineLayout* lastLayout = nullptr; VertexBuffersInfo vertexBuffersInfo = {}; + auto EncodeRenderBundleCommand = [&](CommandIterator* iter, Command type) { + switch (type) { + case Command::Draw: { + DrawCmd* draw = iter->NextCommand(); + + FlushSetVertexBuffers(commandList, &vertexBuffersInfo, lastPipeline); + commandList->DrawInstanced(draw->vertexCount, draw->instanceCount, + draw->firstVertex, draw->firstInstance); + } break; + + case Command::DrawIndexed: { + DrawIndexedCmd* draw = iter->NextCommand(); + + FlushSetVertexBuffers(commandList, &vertexBuffersInfo, lastPipeline); + commandList->DrawIndexedInstanced(draw->indexCount, draw->instanceCount, + draw->firstIndex, draw->baseVertex, + draw->firstInstance); + } break; + + case Command::DrawIndirect: { + DrawIndirectCmd* draw = iter->NextCommand(); + + FlushSetVertexBuffers(commandList, &vertexBuffersInfo, lastPipeline); + Buffer* buffer = ToBackend(draw->indirectBuffer.Get()); + ComPtr signature = + ToBackend(GetDevice())->GetDrawIndirectSignature(); + commandList->ExecuteIndirect(signature.Get(), 1, + buffer->GetD3D12Resource().Get(), + draw->indirectOffset, nullptr, 0); + } break; + + case Command::DrawIndexedIndirect: { + DrawIndexedIndirectCmd* draw = iter->NextCommand(); + + FlushSetVertexBuffers(commandList, &vertexBuffersInfo, lastPipeline); + Buffer* buffer = ToBackend(draw->indirectBuffer.Get()); + ComPtr signature = + ToBackend(GetDevice())->GetDrawIndexedIndirectSignature(); + commandList->ExecuteIndirect(signature.Get(), 1, + buffer->GetD3D12Resource().Get(), + draw->indirectOffset, nullptr, 0); + } break; + + case Command::InsertDebugMarker: { + InsertDebugMarkerCmd* cmd = iter->NextCommand(); + const char* label = iter->NextData(cmd->length + 1); + + if (ToBackend(GetDevice())->GetFunctions()->IsPIXEventRuntimeLoaded()) { + // PIX color is 1 byte per channel in ARGB format + constexpr uint64_t kPIXBlackColor = 0xff000000; + ToBackend(GetDevice()) + ->GetFunctions() + ->pixSetMarkerOnCommandList(commandList.Get(), kPIXBlackColor, label); + } + } break; + + case Command::PopDebugGroup: { + iter->NextCommand(); + + if (ToBackend(GetDevice())->GetFunctions()->IsPIXEventRuntimeLoaded()) { + ToBackend(GetDevice()) + ->GetFunctions() + ->pixEndEventOnCommandList(commandList.Get()); + } + } break; + + case Command::PushDebugGroup: { + PushDebugGroupCmd* cmd = iter->NextCommand(); + const char* label = iter->NextData(cmd->length + 1); + + if (ToBackend(GetDevice())->GetFunctions()->IsPIXEventRuntimeLoaded()) { + // PIX color is 1 byte per channel in ARGB format + constexpr uint64_t kPIXBlackColor = 0xff000000; + ToBackend(GetDevice()) + ->GetFunctions() + ->pixBeginEventOnCommandList(commandList.Get(), kPIXBlackColor, label); + } + } break; + + case Command::SetRenderPipeline: { + SetRenderPipelineCmd* cmd = iter->NextCommand(); + RenderPipeline* pipeline = ToBackend(cmd->pipeline).Get(); + PipelineLayout* layout = ToBackend(pipeline->GetLayout()); + + commandList->SetGraphicsRootSignature(layout->GetRootSignature().Get()); + commandList->SetPipelineState(pipeline->GetPipelineState().Get()); + commandList->IASetPrimitiveTopology(pipeline->GetD3D12PrimitiveTopology()); + + bindingTracker->SetInheritedBindGroups(commandList, lastLayout, layout); + + lastPipeline = pipeline; + lastLayout = layout; + } break; + + case Command::SetBindGroup: { + SetBindGroupCmd* cmd = iter->NextCommand(); + BindGroup* group = ToBackend(cmd->group.Get()); + uint64_t* dynamicOffsets = nullptr; + + if (cmd->dynamicOffsetCount > 0) { + dynamicOffsets = iter->NextData(cmd->dynamicOffsetCount); + } + + bindingTracker->SetBindGroup(commandList, lastLayout, group, cmd->index, + cmd->dynamicOffsetCount, dynamicOffsets); + } break; + + case Command::SetIndexBuffer: { + SetIndexBufferCmd* cmd = iter->NextCommand(); + + Buffer* buffer = ToBackend(cmd->buffer.Get()); + D3D12_INDEX_BUFFER_VIEW bufferView; + bufferView.BufferLocation = buffer->GetVA() + cmd->offset; + bufferView.SizeInBytes = buffer->GetSize() - cmd->offset; + // TODO(cwallez@chromium.org): Make index buffers lazily applied, right now + // this will break if the pipeline is changed for one with a different index + // format after SetIndexBuffer + bufferView.Format = + DXGIIndexFormat(lastPipeline->GetVertexInputDescriptor()->indexFormat); + + commandList->IASetIndexBuffer(&bufferView); + } break; + + case Command::SetVertexBuffers: { + SetVertexBuffersCmd* cmd = iter->NextCommand(); + auto buffers = iter->NextData>(cmd->count); + auto offsets = iter->NextData(cmd->count); + + vertexBuffersInfo.startSlot = + std::min(vertexBuffersInfo.startSlot, cmd->startSlot); + vertexBuffersInfo.endSlot = + std::max(vertexBuffersInfo.endSlot, cmd->startSlot + cmd->count); + + for (uint32_t i = 0; i < cmd->count; ++i) { + Buffer* buffer = ToBackend(buffers[i].Get()); + auto* d3d12BufferView = + &vertexBuffersInfo.d3d12BufferViews[cmd->startSlot + i]; + d3d12BufferView->BufferLocation = buffer->GetVA() + offsets[i]; + d3d12BufferView->SizeInBytes = buffer->GetSize() - offsets[i]; + // The bufferView stride is set based on the input state before a draw. + } + } break; + + default: + UNREACHABLE(); + break; + } + }; + Command type; while (mCommands.NextCommandId(&type)) { switch (type) { @@ -1001,98 +1171,6 @@ namespace dawn_native { namespace d3d12 { return; } break; - case Command::Draw: { - DrawCmd* draw = mCommands.NextCommand(); - - FlushSetVertexBuffers(commandList, &vertexBuffersInfo, lastPipeline); - commandList->DrawInstanced(draw->vertexCount, draw->instanceCount, - draw->firstVertex, draw->firstInstance); - } break; - - case Command::DrawIndexed: { - DrawIndexedCmd* draw = mCommands.NextCommand(); - - FlushSetVertexBuffers(commandList, &vertexBuffersInfo, lastPipeline); - commandList->DrawIndexedInstanced(draw->indexCount, draw->instanceCount, - draw->firstIndex, draw->baseVertex, - draw->firstInstance); - } break; - - case Command::DrawIndirect: { - DrawIndirectCmd* draw = mCommands.NextCommand(); - - FlushSetVertexBuffers(commandList, &vertexBuffersInfo, lastPipeline); - Buffer* buffer = ToBackend(draw->indirectBuffer.Get()); - ComPtr signature = - ToBackend(GetDevice())->GetDrawIndirectSignature(); - commandList->ExecuteIndirect(signature.Get(), 1, - buffer->GetD3D12Resource().Get(), - draw->indirectOffset, nullptr, 0); - } break; - - case Command::DrawIndexedIndirect: { - DrawIndexedIndirectCmd* draw = mCommands.NextCommand(); - - FlushSetVertexBuffers(commandList, &vertexBuffersInfo, lastPipeline); - Buffer* buffer = ToBackend(draw->indirectBuffer.Get()); - ComPtr signature = - ToBackend(GetDevice())->GetDrawIndexedIndirectSignature(); - commandList->ExecuteIndirect(signature.Get(), 1, - buffer->GetD3D12Resource().Get(), - draw->indirectOffset, nullptr, 0); - } break; - - case Command::InsertDebugMarker: { - InsertDebugMarkerCmd* cmd = mCommands.NextCommand(); - const char* label = mCommands.NextData(cmd->length + 1); - - if (ToBackend(GetDevice())->GetFunctions()->IsPIXEventRuntimeLoaded()) { - // PIX color is 1 byte per channel in ARGB format - constexpr uint64_t kPIXBlackColor = 0xff000000; - ToBackend(GetDevice()) - ->GetFunctions() - ->pixSetMarkerOnCommandList(commandList.Get(), kPIXBlackColor, label); - } - } break; - - case Command::PopDebugGroup: { - mCommands.NextCommand(); - - if (ToBackend(GetDevice())->GetFunctions()->IsPIXEventRuntimeLoaded()) { - ToBackend(GetDevice()) - ->GetFunctions() - ->pixEndEventOnCommandList(commandList.Get()); - } - } break; - - case Command::PushDebugGroup: { - PushDebugGroupCmd* cmd = mCommands.NextCommand(); - const char* label = mCommands.NextData(cmd->length + 1); - - if (ToBackend(GetDevice())->GetFunctions()->IsPIXEventRuntimeLoaded()) { - // PIX color is 1 byte per channel in ARGB format - constexpr uint64_t kPIXBlackColor = 0xff000000; - ToBackend(GetDevice()) - ->GetFunctions() - ->pixBeginEventOnCommandList(commandList.Get(), kPIXBlackColor, label); - } - } break; - - case Command::SetRenderPipeline: { - SetRenderPipelineCmd* cmd = mCommands.NextCommand(); - RenderPipeline* pipeline = ToBackend(cmd->pipeline).Get(); - PipelineLayout* layout = ToBackend(pipeline->GetLayout()); - - commandList->SetGraphicsRootSignature(layout->GetRootSignature().Get()); - commandList->SetPipelineState(pipeline->GetPipelineState().Get()); - commandList->IASetPrimitiveTopology(pipeline->GetD3D12PrimitiveTopology()); - - bindingTracker->SetInheritedBindGroups(commandList, lastLayout, layout); - - lastPipeline = pipeline; - lastLayout = layout; - } break; - case Command::SetStencilReference: { SetStencilReferenceCmd* cmd = mCommands.NextCommand(); @@ -1128,56 +1206,20 @@ namespace dawn_native { namespace d3d12 { commandList->OMSetBlendFactor(static_cast(&cmd->color.r)); } break; - case Command::SetBindGroup: { - 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; - - case Command::SetIndexBuffer: { - SetIndexBufferCmd* cmd = mCommands.NextCommand(); - - Buffer* buffer = ToBackend(cmd->buffer.Get()); - D3D12_INDEX_BUFFER_VIEW bufferView; - bufferView.BufferLocation = buffer->GetVA() + cmd->offset; - bufferView.SizeInBytes = buffer->GetSize() - cmd->offset; - // TODO(cwallez@chromium.org): Make index buffers lazily applied, right now - // this will break if the pipeline is changed for one with a different index - // format after SetIndexBuffer - bufferView.Format = - DXGIIndexFormat(lastPipeline->GetVertexInputDescriptor()->indexFormat); - - commandList->IASetIndexBuffer(&bufferView); - } break; - - case Command::SetVertexBuffers: { - SetVertexBuffersCmd* cmd = mCommands.NextCommand(); - auto buffers = mCommands.NextData>(cmd->count); - auto offsets = mCommands.NextData(cmd->count); - - vertexBuffersInfo.startSlot = - std::min(vertexBuffersInfo.startSlot, cmd->startSlot); - vertexBuffersInfo.endSlot = - std::max(vertexBuffersInfo.endSlot, cmd->startSlot + cmd->count); + case Command::ExecuteBundles: { + ExecuteBundlesCmd* cmd = mCommands.NextCommand(); + auto bundles = mCommands.NextData>(cmd->count); for (uint32_t i = 0; i < cmd->count; ++i) { - Buffer* buffer = ToBackend(buffers[i].Get()); - auto* d3d12BufferView = - &vertexBuffersInfo.d3d12BufferViews[cmd->startSlot + i]; - d3d12BufferView->BufferLocation = buffer->GetVA() + offsets[i]; - d3d12BufferView->SizeInBytes = buffer->GetSize() - offsets[i]; - // The bufferView stride is set based on the input state before a draw. + CommandIterator* iter = bundles[i]->GetCommands(); + iter->Reset(); + while (iter->NextCommandId(&type)) { + EncodeRenderBundleCommand(iter, type); + } } } break; - default: { UNREACHABLE(); } break; + default: { EncodeRenderBundleCommand(&mCommands, type); } break; } } } diff --git a/src/dawn_native/metal/CommandBufferMTL.mm b/src/dawn_native/metal/CommandBufferMTL.mm index a096073ff8..3c79768519 100644 --- a/src/dawn_native/metal/CommandBufferMTL.mm +++ b/src/dawn_native/metal/CommandBufferMTL.mm @@ -17,6 +17,7 @@ #include "dawn_native/BindGroup.h" #include "dawn_native/CommandEncoder.h" #include "dawn_native/Commands.h" +#include "dawn_native/RenderBundle.h" #include "dawn_native/metal/BufferMTL.h" #include "dawn_native/metal/ComputePipelineMTL.h" #include "dawn_native/metal/DeviceMTL.h" @@ -871,17 +872,10 @@ namespace dawn_native { namespace metal { id encoder = [commandBuffer renderCommandEncoderWithDescriptor:mtlRenderPass]; - Command type; - while (mCommands.NextCommandId(&type)) { + auto EncodeRenderBundleCommand = [&](CommandIterator* iter, Command type) { switch (type) { - case Command::EndRenderPass: { - mCommands.NextCommand(); - [encoder endEncoding]; - return; - } break; - case Command::Draw: { - DrawCmd* draw = mCommands.NextCommand(); + DrawCmd* draw = iter->NextCommand(); vertexInputBuffers.Apply(encoder, lastPipeline); storageBufferLengths.Apply(lastPipeline, encoder); @@ -897,7 +891,7 @@ namespace dawn_native { namespace metal { } break; case Command::DrawIndexed: { - DrawIndexedCmd* draw = mCommands.NextCommand(); + DrawIndexedCmd* draw = iter->NextCommand(); size_t formatSize = IndexFormatSize(lastPipeline->GetVertexInputDescriptor()->indexFormat); @@ -919,7 +913,7 @@ namespace dawn_native { namespace metal { } break; case Command::DrawIndirect: { - DrawIndirectCmd* draw = mCommands.NextCommand(); + DrawIndirectCmd* draw = iter->NextCommand(); vertexInputBuffers.Apply(encoder, lastPipeline); storageBufferLengths.Apply(lastPipeline, encoder); @@ -932,7 +926,7 @@ namespace dawn_native { namespace metal { } break; case Command::DrawIndexedIndirect: { - DrawIndirectCmd* draw = mCommands.NextCommand(); + DrawIndirectCmd* draw = iter->NextCommand(); vertexInputBuffers.Apply(encoder, lastPipeline); storageBufferLengths.Apply(lastPipeline, encoder); @@ -948,8 +942,8 @@ namespace dawn_native { namespace metal { } break; case Command::InsertDebugMarker: { - InsertDebugMarkerCmd* cmd = mCommands.NextCommand(); - char* label = mCommands.NextData(cmd->length + 1); + InsertDebugMarkerCmd* cmd = iter->NextCommand(); + char* label = iter->NextData(cmd->length + 1); NSString* mtlLabel = [[NSString alloc] initWithUTF8String:label]; [encoder insertDebugSignpost:mtlLabel]; @@ -957,14 +951,14 @@ namespace dawn_native { namespace metal { } break; case Command::PopDebugGroup: { - mCommands.NextCommand(); + iter->NextCommand(); [encoder popDebugGroup]; } break; case Command::PushDebugGroup: { - PushDebugGroupCmd* cmd = mCommands.NextCommand(); - char* label = mCommands.NextData(cmd->length + 1); + PushDebugGroupCmd* cmd = iter->NextCommand(); + char* label = iter->NextData(cmd->length + 1); NSString* mtlLabel = [[NSString alloc] initWithUTF8String:label]; [encoder pushDebugGroup:mtlLabel]; @@ -972,7 +966,7 @@ namespace dawn_native { namespace metal { } break; case Command::SetRenderPipeline: { - SetRenderPipelineCmd* cmd = mCommands.NextCommand(); + SetRenderPipelineCmd* cmd = iter->NextCommand(); RenderPipeline* newPipeline = ToBackend(cmd->pipeline).Get(); vertexInputBuffers.OnSetPipeline(lastPipeline, newPipeline); @@ -984,6 +978,49 @@ namespace dawn_native { namespace metal { lastPipeline = newPipeline; } break; + case Command::SetBindGroup: { + SetBindGroupCmd* cmd = iter->NextCommand(); + uint64_t* dynamicOffsets = nullptr; + if (cmd->dynamicOffsetCount > 0) { + dynamicOffsets = iter->NextData(cmd->dynamicOffsetCount); + } + + ApplyBindGroup(cmd->index, ToBackend(cmd->group.Get()), cmd->dynamicOffsetCount, + dynamicOffsets, ToBackend(lastPipeline->GetLayout()), + &storageBufferLengths, encoder, nil); + } break; + + case Command::SetIndexBuffer: { + SetIndexBufferCmd* cmd = iter->NextCommand(); + auto b = ToBackend(cmd->buffer.Get()); + indexBuffer = b->GetMTLBuffer(); + indexBufferBaseOffset = cmd->offset; + } break; + + case Command::SetVertexBuffers: { + SetVertexBuffersCmd* cmd = iter->NextCommand(); + const Ref* buffers = iter->NextData>(cmd->count); + const uint64_t* offsets = iter->NextData(cmd->count); + + vertexInputBuffers.OnSetVertexBuffers(cmd->startSlot, cmd->count, buffers, + offsets); + } break; + + default: + UNREACHABLE(); + break; + } + }; + + Command type; + while (mCommands.NextCommandId(&type)) { + switch (type) { + case Command::EndRenderPass: { + mCommands.NextCommand(); + [encoder endEncoding]; + return; + } break; + case Command::SetStencilReference: { SetStencilReferenceCmd* cmd = mCommands.NextCommand(); [encoder setStencilReferenceValue:cmd->reference]; @@ -1030,36 +1067,20 @@ namespace dawn_native { namespace metal { alpha:cmd->color.a]; } break; - case Command::SetBindGroup: { - SetBindGroupCmd* cmd = mCommands.NextCommand(); - uint64_t* dynamicOffsets = nullptr; - if (cmd->dynamicOffsetCount > 0) { - dynamicOffsets = mCommands.NextData(cmd->dynamicOffsetCount); + case Command::ExecuteBundles: { + ExecuteBundlesCmd* cmd = mCommands.NextCommand(); + auto bundles = mCommands.NextData>(cmd->count); + + for (uint32_t i = 0; i < cmd->count; ++i) { + CommandIterator* iter = bundles[i]->GetCommands(); + iter->Reset(); + while (iter->NextCommandId(&type)) { + EncodeRenderBundleCommand(iter, type); + } } - - ApplyBindGroup(cmd->index, ToBackend(cmd->group.Get()), cmd->dynamicOffsetCount, - dynamicOffsets, ToBackend(lastPipeline->GetLayout()), - &storageBufferLengths, encoder, nil); } break; - case Command::SetIndexBuffer: { - SetIndexBufferCmd* cmd = mCommands.NextCommand(); - auto b = ToBackend(cmd->buffer.Get()); - indexBuffer = b->GetMTLBuffer(); - indexBufferBaseOffset = cmd->offset; - } break; - - case Command::SetVertexBuffers: { - SetVertexBuffersCmd* cmd = mCommands.NextCommand(); - const Ref* buffers = - mCommands.NextData>(cmd->count); - const uint64_t* offsets = mCommands.NextData(cmd->count); - - vertexInputBuffers.OnSetVertexBuffers(cmd->startSlot, cmd->count, buffers, - offsets); - } break; - - default: { UNREACHABLE(); } break; + default: { EncodeRenderBundleCommand(&mCommands, type); } break; } } diff --git a/src/dawn_native/opengl/CommandBufferGL.cpp b/src/dawn_native/opengl/CommandBufferGL.cpp index 32ee0bf15b..9fafb6f4fb 100644 --- a/src/dawn_native/opengl/CommandBufferGL.cpp +++ b/src/dawn_native/opengl/CommandBufferGL.cpp @@ -17,6 +17,7 @@ #include "dawn_native/BindGroup.h" #include "dawn_native/CommandEncoder.h" #include "dawn_native/Commands.h" +#include "dawn_native/RenderBundle.h" #include "dawn_native/opengl/BufferGL.h" #include "dawn_native/opengl/ComputePipelineGL.h" #include "dawn_native/opengl/DeviceGL.h" @@ -735,21 +736,10 @@ namespace dawn_native { namespace opengl { InputBufferTracker inputBuffers; - Command type; - while (mCommands.NextCommandId(&type)) { + auto DoRenderBundleCommand = [&](CommandIterator* iter, Command type) { switch (type) { - case Command::EndRenderPass: { - mCommands.NextCommand(); - - if (renderPass->attachmentState->GetSampleCount() > 1) { - ResolveMultisampledRenderTargets(gl, renderPass); - } - gl.DeleteFramebuffers(1, &fbo); - return; - } break; - case Command::Draw: { - DrawCmd* draw = mCommands.NextCommand(); + DrawCmd* draw = iter->NextCommand(); inputBuffers.Apply(gl); if (draw->firstInstance > 0) { @@ -765,7 +755,7 @@ namespace dawn_native { namespace opengl { } break; case Command::DrawIndexed: { - DrawIndexedCmd* draw = mCommands.NextCommand(); + DrawIndexedCmd* draw = iter->NextCommand(); inputBuffers.Apply(gl); dawn::IndexFormat indexFormat = @@ -790,7 +780,7 @@ namespace dawn_native { namespace opengl { } break; case Command::DrawIndirect: { - DrawIndirectCmd* draw = mCommands.NextCommand(); + DrawIndirectCmd* draw = iter->NextCommand(); inputBuffers.Apply(gl); uint64_t indirectBufferOffset = draw->indirectOffset; @@ -803,7 +793,7 @@ namespace dawn_native { namespace opengl { } break; case Command::DrawIndexedIndirect: { - DrawIndexedIndirectCmd* draw = mCommands.NextCommand(); + DrawIndexedIndirectCmd* draw = iter->NextCommand(); inputBuffers.Apply(gl); dawn::IndexFormat indexFormat = @@ -824,17 +814,60 @@ namespace dawn_native { namespace opengl { case Command::PushDebugGroup: { // Due to lack of linux driver support for GL_EXT_debug_marker // extension these functions are skipped. - SkipCommand(&mCommands, type); + SkipCommand(iter, type); } break; case Command::SetRenderPipeline: { - SetRenderPipelineCmd* cmd = mCommands.NextCommand(); + SetRenderPipelineCmd* cmd = iter->NextCommand(); lastPipeline = ToBackend(cmd->pipeline).Get(); lastPipeline->ApplyNow(persistentPipelineState); inputBuffers.OnSetPipeline(lastPipeline); } break; + case Command::SetBindGroup: { + SetBindGroupCmd* cmd = iter->NextCommand(); + uint64_t* dynamicOffsets = nullptr; + if (cmd->dynamicOffsetCount > 0) { + dynamicOffsets = iter->NextData(cmd->dynamicOffsetCount); + } + ApplyBindGroup(gl, cmd->index, cmd->group.Get(), + ToBackend(lastPipeline->GetLayout()), lastPipeline, + cmd->dynamicOffsetCount, dynamicOffsets); + } break; + + case Command::SetIndexBuffer: { + SetIndexBufferCmd* cmd = iter->NextCommand(); + indexBufferBaseOffset = cmd->offset; + inputBuffers.OnSetIndexBuffer(cmd->buffer.Get()); + } break; + + case Command::SetVertexBuffers: { + SetVertexBuffersCmd* cmd = iter->NextCommand(); + auto buffers = iter->NextData>(cmd->count); + auto offsets = iter->NextData(cmd->count); + inputBuffers.OnSetVertexBuffers(cmd->startSlot, cmd->count, buffers, offsets); + } break; + + default: + UNREACHABLE(); + break; + } + }; + + Command type; + while (mCommands.NextCommandId(&type)) { + switch (type) { + case Command::EndRenderPass: { + mCommands.NextCommand(); + + if (renderPass->attachmentState->GetSampleCount() > 1) { + ResolveMultisampledRenderTargets(gl, renderPass); + } + gl.DeleteFramebuffers(1, &fbo); + return; + } break; + case Command::SetStencilReference: { SetStencilReferenceCmd* cmd = mCommands.NextCommand(); persistentPipelineState.SetStencilReference(gl, cmd->reference); @@ -856,31 +889,20 @@ namespace dawn_native { namespace opengl { gl.BlendColor(cmd->color.r, cmd->color.g, cmd->color.b, cmd->color.a); } break; - case Command::SetBindGroup: { - SetBindGroupCmd* cmd = mCommands.NextCommand(); - uint64_t* dynamicOffsets = nullptr; - if (cmd->dynamicOffsetCount > 0) { - dynamicOffsets = mCommands.NextData(cmd->dynamicOffsetCount); + case Command::ExecuteBundles: { + ExecuteBundlesCmd* cmd = mCommands.NextCommand(); + auto bundles = mCommands.NextData>(cmd->count); + + for (uint32_t i = 0; i < cmd->count; ++i) { + CommandIterator* iter = bundles[i]->GetCommands(); + iter->Reset(); + while (iter->NextCommandId(&type)) { + DoRenderBundleCommand(iter, type); + } } - ApplyBindGroup(gl, cmd->index, cmd->group.Get(), - ToBackend(lastPipeline->GetLayout()), lastPipeline, - cmd->dynamicOffsetCount, dynamicOffsets); } break; - case Command::SetIndexBuffer: { - SetIndexBufferCmd* cmd = mCommands.NextCommand(); - indexBufferBaseOffset = cmd->offset; - inputBuffers.OnSetIndexBuffer(cmd->buffer.Get()); - } break; - - case Command::SetVertexBuffers: { - SetVertexBuffersCmd* cmd = mCommands.NextCommand(); - auto buffers = mCommands.NextData>(cmd->count); - auto offsets = mCommands.NextData(cmd->count); - inputBuffers.OnSetVertexBuffers(cmd->startSlot, cmd->count, buffers, offsets); - } break; - - default: { UNREACHABLE(); } break; + default: { DoRenderBundleCommand(&mCommands, type); } break; } } diff --git a/src/dawn_native/vulkan/CommandBufferVk.cpp b/src/dawn_native/vulkan/CommandBufferVk.cpp index 28e9058c01..7b1c7fea9d 100644 --- a/src/dawn_native/vulkan/CommandBufferVk.cpp +++ b/src/dawn_native/vulkan/CommandBufferVk.cpp @@ -16,6 +16,7 @@ #include "dawn_native/CommandEncoder.h" #include "dawn_native/Commands.h" +#include "dawn_native/RenderBundle.h" #include "dawn_native/vulkan/BindGroupVk.h" #include "dawn_native/vulkan/BufferVk.h" #include "dawn_native/vulkan/CommandRecordingContext.h" @@ -671,17 +672,10 @@ namespace dawn_native { namespace vulkan { DescriptorSetTracker descriptorSets; RenderPipeline* lastPipeline = nullptr; - Command type; - while (mCommands.NextCommandId(&type)) { + auto EncodeRenderBundleCommand = [&](CommandIterator* iter, Command type) { switch (type) { - case Command::EndRenderPass: { - mCommands.NextCommand(); - device->fn.CmdEndRenderPass(commands); - return; - } break; - case Command::Draw: { - DrawCmd* draw = mCommands.NextCommand(); + DrawCmd* draw = iter->NextCommand(); descriptorSets.Flush(device, commands, VK_PIPELINE_BIND_POINT_GRAPHICS); device->fn.CmdDraw(commands, draw->vertexCount, draw->instanceCount, @@ -689,7 +683,7 @@ namespace dawn_native { namespace vulkan { } break; case Command::DrawIndexed: { - DrawIndexedCmd* draw = mCommands.NextCommand(); + DrawIndexedCmd* draw = iter->NextCommand(); descriptorSets.Flush(device, commands, VK_PIPELINE_BIND_POINT_GRAPHICS); device->fn.CmdDrawIndexed(commands, draw->indexCount, draw->instanceCount, @@ -698,7 +692,7 @@ namespace dawn_native { namespace vulkan { } break; case Command::DrawIndirect: { - DrawIndirectCmd* draw = mCommands.NextCommand(); + DrawIndirectCmd* draw = iter->NextCommand(); VkBuffer indirectBuffer = ToBackend(draw->indirectBuffer)->GetHandle(); descriptorSets.Flush(device, commands, VK_PIPELINE_BIND_POINT_GRAPHICS); @@ -708,7 +702,7 @@ namespace dawn_native { namespace vulkan { } break; case Command::DrawIndexedIndirect: { - DrawIndirectCmd* draw = mCommands.NextCommand(); + DrawIndirectCmd* draw = iter->NextCommand(); VkBuffer indirectBuffer = ToBackend(draw->indirectBuffer)->GetHandle(); descriptorSets.Flush(device, commands, VK_PIPELINE_BIND_POINT_GRAPHICS); @@ -719,8 +713,8 @@ namespace dawn_native { namespace vulkan { case Command::InsertDebugMarker: { if (device->GetDeviceInfo().debugMarker) { - InsertDebugMarkerCmd* cmd = mCommands.NextCommand(); - const char* label = mCommands.NextData(cmd->length + 1); + InsertDebugMarkerCmd* cmd = iter->NextCommand(); + const char* label = iter->NextData(cmd->length + 1); VkDebugMarkerMarkerInfoEXT markerInfo; markerInfo.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT; markerInfo.pNext = nullptr; @@ -732,23 +726,23 @@ namespace dawn_native { namespace vulkan { markerInfo.color[3] = 1.0; device->fn.CmdDebugMarkerInsertEXT(commands, &markerInfo); } else { - SkipCommand(&mCommands, Command::InsertDebugMarker); + SkipCommand(iter, Command::InsertDebugMarker); } } break; case Command::PopDebugGroup: { if (device->GetDeviceInfo().debugMarker) { - mCommands.NextCommand(); + iter->NextCommand(); device->fn.CmdDebugMarkerEndEXT(commands); } else { - SkipCommand(&mCommands, Command::PopDebugGroup); + SkipCommand(iter, Command::PopDebugGroup); } } break; case Command::PushDebugGroup: { if (device->GetDeviceInfo().debugMarker) { - PushDebugGroupCmd* cmd = mCommands.NextCommand(); - const char* label = mCommands.NextData(cmd->length + 1); + PushDebugGroupCmd* cmd = iter->NextCommand(); + const char* label = iter->NextData(cmd->length + 1); VkDebugMarkerMarkerInfoEXT markerInfo; markerInfo.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT; markerInfo.pNext = nullptr; @@ -760,35 +754,24 @@ namespace dawn_native { namespace vulkan { markerInfo.color[3] = 1.0; device->fn.CmdDebugMarkerBeginEXT(commands, &markerInfo); } else { - SkipCommand(&mCommands, Command::PushDebugGroup); + SkipCommand(iter, Command::PushDebugGroup); } } break; case Command::SetBindGroup: { - SetBindGroupCmd* cmd = mCommands.NextCommand(); + SetBindGroupCmd* cmd = iter->NextCommand(); VkDescriptorSet set = ToBackend(cmd->group.Get())->GetHandle(); uint64_t* dynamicOffsets = nullptr; if (cmd->dynamicOffsetCount > 0) { - dynamicOffsets = mCommands.NextData(cmd->dynamicOffsetCount); + dynamicOffsets = iter->NextData(cmd->dynamicOffsetCount); } descriptorSets.OnSetBindGroup(cmd->index, set, cmd->dynamicOffsetCount, dynamicOffsets); } break; - case Command::SetBlendColor: { - SetBlendColorCmd* cmd = mCommands.NextCommand(); - float blendConstants[4] = { - cmd->color.r, - cmd->color.g, - cmd->color.b, - cmd->color.a, - }; - device->fn.CmdSetBlendConstants(commands, blendConstants); - } break; - case Command::SetIndexBuffer: { - SetIndexBufferCmd* cmd = mCommands.NextCommand(); + SetIndexBufferCmd* cmd = iter->NextCommand(); VkBuffer indexBuffer = ToBackend(cmd->buffer)->GetHandle(); // TODO(cwallez@chromium.org): get the index type from the last render pipeline @@ -801,7 +784,7 @@ namespace dawn_native { namespace vulkan { } break; case Command::SetRenderPipeline: { - SetRenderPipelineCmd* cmd = mCommands.NextCommand(); + SetRenderPipelineCmd* cmd = iter->NextCommand(); RenderPipeline* pipeline = ToBackend(cmd->pipeline).Get(); device->fn.CmdBindPipeline(commands, VK_PIPELINE_BIND_POINT_GRAPHICS, @@ -811,6 +794,50 @@ namespace dawn_native { namespace vulkan { descriptorSets.OnPipelineLayoutChange(ToBackend(pipeline->GetLayout())); } break; + case Command::SetVertexBuffers: { + SetVertexBuffersCmd* cmd = iter->NextCommand(); + auto buffers = iter->NextData>(cmd->count); + auto offsets = iter->NextData(cmd->count); + + std::array vkBuffers; + std::array vkOffsets; + + for (uint32_t i = 0; i < cmd->count; ++i) { + Buffer* buffer = ToBackend(buffers[i].Get()); + vkBuffers[i] = buffer->GetHandle(); + vkOffsets[i] = static_cast(offsets[i]); + } + + device->fn.CmdBindVertexBuffers(commands, cmd->startSlot, cmd->count, + vkBuffers.data(), vkOffsets.data()); + } break; + + default: + UNREACHABLE(); + break; + } + }; + + Command type; + while (mCommands.NextCommandId(&type)) { + switch (type) { + case Command::EndRenderPass: { + mCommands.NextCommand(); + device->fn.CmdEndRenderPass(commands); + return; + } break; + + case Command::SetBlendColor: { + SetBlendColorCmd* cmd = mCommands.NextCommand(); + float blendConstants[4] = { + cmd->color.r, + cmd->color.g, + cmd->color.b, + cmd->color.a, + }; + device->fn.CmdSetBlendConstants(commands, blendConstants); + } break; + case Command::SetStencilReference: { SetStencilReferenceCmd* cmd = mCommands.NextCommand(); device->fn.CmdSetStencilReference(commands, VK_STENCIL_FRONT_AND_BACK, @@ -841,25 +868,20 @@ namespace dawn_native { namespace vulkan { device->fn.CmdSetScissor(commands, 0, 1, &rect); } break; - case Command::SetVertexBuffers: { - SetVertexBuffersCmd* cmd = mCommands.NextCommand(); - auto buffers = mCommands.NextData>(cmd->count); - auto offsets = mCommands.NextData(cmd->count); - - std::array vkBuffers; - std::array vkOffsets; + case Command::ExecuteBundles: { + ExecuteBundlesCmd* cmd = mCommands.NextCommand(); + auto bundles = mCommands.NextData>(cmd->count); for (uint32_t i = 0; i < cmd->count; ++i) { - Buffer* buffer = ToBackend(buffers[i].Get()); - vkBuffers[i] = buffer->GetHandle(); - vkOffsets[i] = static_cast(offsets[i]); + CommandIterator* iter = bundles[i]->GetCommands(); + iter->Reset(); + while (iter->NextCommandId(&type)) { + EncodeRenderBundleCommand(iter, type); + } } - - device->fn.CmdBindVertexBuffers(commands, cmd->startSlot, cmd->count, - vkBuffers.data(), vkOffsets.data()); } break; - default: { UNREACHABLE(); } break; + default: { EncodeRenderBundleCommand(&mCommands, type); } break; } } diff --git a/src/tests/end2end/RenderBundleTests.cpp b/src/tests/end2end/RenderBundleTests.cpp new file mode 100644 index 0000000000..ad2dd3cfbd --- /dev/null +++ b/src/tests/end2end/RenderBundleTests.cpp @@ -0,0 +1,210 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/DawnTest.h" + +#include "utils/ComboRenderBundleEncoderDescriptor.h" +#include "utils/ComboRenderPipelineDescriptor.h" +#include "utils/DawnHelpers.h" + +constexpr uint32_t kRTSize = 4; +constexpr RGBA8 kColors[2] = {RGBA8(0, 255, 0, 255), RGBA8(0, 0, 255, 255)}; + +// RenderBundleTest tests simple usage of RenderBundles to draw. The implementaiton +// of RenderBundle is shared significantly with render pass execution which is +// tested in all other rendering tests. +class RenderBundleTest : public DawnTest { + protected: + void SetUp() override { + DawnTest::SetUp(); + + renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); + + dawn::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::ShaderStage::Vertex, R"( + #version 450 + layout(location = 0) in vec4 pos; + void main() { + gl_Position = pos; + })"); + + dawn::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::ShaderStage::Fragment, R"( + #version 450 + layout(location = 0) out vec4 fragColor; + layout (set = 0, binding = 0) uniform fragmentUniformBuffer { + vec4 color; + }; + void main() { + fragColor = color; + })"); + + dawn::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, {{0, dawn::ShaderStageBit::Fragment, dawn::BindingType::UniformBuffer}}); + + float colors0[] = {kColors[0].r / 255.f, kColors[0].g / 255.f, kColors[0].b / 255.f, + kColors[0].a / 255.f}; + float colors1[] = {kColors[1].r / 255.f, kColors[1].g / 255.f, kColors[1].b / 255.f, + kColors[1].a / 255.f}; + + dawn::Buffer buffer0 = utils::CreateBufferFromData(device, colors0, 4 * sizeof(float), + dawn::BufferUsageBit::Uniform); + dawn::Buffer buffer1 = utils::CreateBufferFromData(device, colors1, 4 * sizeof(float), + dawn::BufferUsageBit::Uniform); + + bindGroups[0] = utils::MakeBindGroup(device, bgl, {{0, buffer0, 0, 4 * sizeof(float)}}); + bindGroups[1] = utils::MakeBindGroup(device, bgl, {{0, buffer1, 0, 4 * sizeof(float)}}); + + dawn::PipelineLayoutDescriptor pipelineLayoutDesc; + pipelineLayoutDesc.bindGroupLayoutCount = 1; + pipelineLayoutDesc.bindGroupLayouts = &bgl; + + dawn::PipelineLayout pipelineLayout = device.CreatePipelineLayout(&pipelineLayoutDesc); + + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.layout = pipelineLayout; + descriptor.cVertexStage.module = vsModule; + descriptor.cFragmentStage.module = fsModule; + descriptor.primitiveTopology = dawn::PrimitiveTopology::TriangleStrip; + descriptor.cVertexInput.bufferCount = 1; + descriptor.cVertexInput.cBuffers[0].stride = 4 * sizeof(float); + descriptor.cVertexInput.cBuffers[0].attributeCount = 1; + descriptor.cVertexInput.cAttributes[0].format = dawn::VertexFormat::Float4; + descriptor.cColorStates[0]->format = renderPass.colorFormat; + + pipeline = device.CreateRenderPipeline(&descriptor); + + vertexBuffer = utils::CreateBufferFromData( + device, dawn::BufferUsageBit::Vertex, + {// The bottom left triangle + -1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f, + + // The top right triangle + -1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 0.0f, 1.0f}); + } + + utils::BasicRenderPass renderPass; + dawn::RenderPipeline pipeline; + dawn::Buffer vertexBuffer; + dawn::BindGroup bindGroups[2]; +}; + +// Basic test of RenderBundle. +TEST_P(RenderBundleTest, Basic) { + utils::ComboRenderBundleEncoderDescriptor desc = {}; + desc.colorFormatsCount = 1; + desc.cColorFormats[0] = renderPass.colorFormat; + + dawn::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); + + uint64_t zeroOffset = 0; + renderBundleEncoder.SetPipeline(pipeline); + renderBundleEncoder.SetVertexBuffers(0, 1, &vertexBuffer, &zeroOffset); + renderBundleEncoder.SetBindGroup(0, bindGroups[0], 0, nullptr); + renderBundleEncoder.Draw(6, 1, 0, 0); + + dawn::RenderBundle renderBundle = renderBundleEncoder.Finish(); + + dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + + dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + pass.ExecuteBundles(1, &renderBundle); + pass.EndPass(); + + dawn::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + EXPECT_PIXEL_RGBA8_EQ(kColors[0], renderPass.color, 1, 3); + EXPECT_PIXEL_RGBA8_EQ(kColors[0], renderPass.color, 3, 1); +} + +// Test execution of multiple render bundles +TEST_P(RenderBundleTest, MultipleBundles) { + utils::ComboRenderBundleEncoderDescriptor desc = {}; + desc.colorFormatsCount = 1; + desc.cColorFormats[0] = renderPass.colorFormat; + + dawn::RenderBundle renderBundles[2]; + uint64_t zeroOffset = 0; + { + dawn::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); + + renderBundleEncoder.SetPipeline(pipeline); + renderBundleEncoder.SetVertexBuffers(0, 1, &vertexBuffer, &zeroOffset); + renderBundleEncoder.SetBindGroup(0, bindGroups[0], 0, nullptr); + renderBundleEncoder.Draw(3, 1, 0, 0); + + renderBundles[0] = renderBundleEncoder.Finish(); + } + { + dawn::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); + + renderBundleEncoder.SetPipeline(pipeline); + renderBundleEncoder.SetVertexBuffers(0, 1, &vertexBuffer, &zeroOffset); + renderBundleEncoder.SetBindGroup(0, bindGroups[1], 0, nullptr); + renderBundleEncoder.Draw(3, 1, 3, 0); + + renderBundles[1] = renderBundleEncoder.Finish(); + } + + dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + + dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + pass.ExecuteBundles(2, renderBundles); + pass.EndPass(); + + dawn::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + EXPECT_PIXEL_RGBA8_EQ(kColors[0], renderPass.color, 1, 3); + EXPECT_PIXEL_RGBA8_EQ(kColors[1], renderPass.color, 3, 1); +} + +// Test execution of a bundle along with render pass commands. +TEST_P(RenderBundleTest, BundleAndRenderPassCommands) { + utils::ComboRenderBundleEncoderDescriptor desc = {}; + desc.colorFormatsCount = 1; + desc.cColorFormats[0] = renderPass.colorFormat; + + dawn::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); + + uint64_t zeroOffset = 0; + renderBundleEncoder.SetPipeline(pipeline); + renderBundleEncoder.SetVertexBuffers(0, 1, &vertexBuffer, &zeroOffset); + renderBundleEncoder.SetBindGroup(0, bindGroups[0], 0, nullptr); + renderBundleEncoder.Draw(3, 1, 0, 0); + + dawn::RenderBundle renderBundle = renderBundleEncoder.Finish(); + + dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + + dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + pass.ExecuteBundles(1, &renderBundle); + + pass.SetPipeline(pipeline); + pass.SetVertexBuffers(0, 1, &vertexBuffer, &zeroOffset); + pass.SetBindGroup(0, bindGroups[1], 0, nullptr); + pass.Draw(3, 1, 3, 0); + + pass.ExecuteBundles(1, &renderBundle); + pass.EndPass(); + + dawn::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + EXPECT_PIXEL_RGBA8_EQ(kColors[0], renderPass.color, 1, 3); + EXPECT_PIXEL_RGBA8_EQ(kColors[1], renderPass.color, 3, 1); +} + +DAWN_INSTANTIATE_TEST(RenderBundleTest, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend);