diff --git a/BUILD.gn b/BUILD.gn index 933c915ce6..77cdae1791 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -537,8 +537,6 @@ source_set("libdawn_native_sources") { "src/dawn_native/d3d12/PlatformFunctions.h", "src/dawn_native/d3d12/QueueD3D12.cpp", "src/dawn_native/d3d12/QueueD3D12.h", - "src/dawn_native/d3d12/RenderPassDescriptorD3D12.cpp", - "src/dawn_native/d3d12/RenderPassDescriptorD3D12.h", "src/dawn_native/d3d12/RenderPipelineD3D12.cpp", "src/dawn_native/d3d12/RenderPipelineD3D12.h", "src/dawn_native/d3d12/ResourceAllocator.cpp", @@ -1011,6 +1009,7 @@ test("dawn_end2end_tests") { "src/tests/end2end/PrimitiveTopologyTests.cpp", "src/tests/end2end/PushConstantTests.cpp", "src/tests/end2end/RenderPassLoadOpTests.cpp", + "src/tests/end2end/RenderPassTests.cpp", "src/tests/end2end/SamplerTests.cpp", "src/tests/end2end/ScissorTests.cpp", "src/tests/end2end/TextureViewTests.cpp", diff --git a/src/dawn_native/d3d12/CommandBufferD3D12.cpp b/src/dawn_native/d3d12/CommandBufferD3D12.cpp index c85c8ed451..d8e742990d 100644 --- a/src/dawn_native/d3d12/CommandBufferD3D12.cpp +++ b/src/dawn_native/d3d12/CommandBufferD3D12.cpp @@ -24,7 +24,6 @@ #include "dawn_native/d3d12/DeviceD3D12.h" #include "dawn_native/d3d12/InputStateD3D12.h" #include "dawn_native/d3d12/PipelineLayoutD3D12.h" -#include "dawn_native/d3d12/RenderPassDescriptorD3D12.h" #include "dawn_native/d3d12/RenderPipelineD3D12.h" #include "dawn_native/d3d12/ResourceAllocator.h" #include "dawn_native/d3d12/SamplerD3D12.h" @@ -156,10 +155,94 @@ namespace dawn_native { namespace d3d12 { } }; + struct OMSetRenderTargetArgs { + unsigned int numRTVs = 0; + std::array RTVs = {}; + D3D12_CPU_DESCRIPTOR_HANDLE dsv = {}; + }; + + class RenderPassDescriptorHeapTracker { + public: + RenderPassDescriptorHeapTracker(Device* device) : mDevice(device) { + } + + // This function must only be called before calling AllocateRTVAndDSVHeaps(). + void TrackRenderPass(const RenderPassDescriptor* renderPass) { + DAWN_ASSERT(mRTVHeap.Get() == nullptr && mDSVHeap.Get() == nullptr); + mNumRTVs += static_cast(renderPass->GetColorAttachmentMask().count()); + if (renderPass->HasDepthStencilAttachment()) { + ++mNumDSVs; + } + } + + void AllocateRTVAndDSVHeaps() { + // This function should only be called once. + DAWN_ASSERT(mRTVHeap.Get() == nullptr && mDSVHeap.Get() == nullptr); + DescriptorHeapAllocator* allocator = mDevice->GetDescriptorHeapAllocator(); + if (mNumRTVs > 0) { + mRTVHeap = allocator->AllocateCPUHeap(D3D12_DESCRIPTOR_HEAP_TYPE_RTV, mNumRTVs); + } + if (mNumDSVs > 0) { + mDSVHeap = allocator->AllocateCPUHeap(D3D12_DESCRIPTOR_HEAP_TYPE_DSV, mNumDSVs); + } + } + + // TODO(jiawei.shao@intel.com): use hash map as cache to + // avoid redundant RTV and DSV memory allocations. + OMSetRenderTargetArgs GetSubpassOMSetRenderTargetArgs(RenderPassDescriptor* renderPass) { + OMSetRenderTargetArgs args = {}; + + unsigned int rtvIndex = 0; + uint32_t rtvCount = static_cast(renderPass->GetColorAttachmentMask().count()); + DAWN_ASSERT(mAllocatedRTVs + rtvCount <= mNumRTVs); + for (uint32_t i : IterateBitSet(renderPass->GetColorAttachmentMask())) { + TextureView* view = ToBackend(renderPass->GetColorAttachment(i).view).Get(); + D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = mRTVHeap.GetCPUHandle(mAllocatedRTVs); + D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = view->GetRTVDescriptor(); + mDevice->GetD3D12Device()->CreateRenderTargetView( + ToBackend(view->GetTexture())->GetD3D12Resource(), &rtvDesc, rtvHandle); + args.RTVs[i] = rtvHandle; + + ++rtvIndex; + ++mAllocatedRTVs; + } + args.numRTVs = rtvIndex; + + if (renderPass->HasDepthStencilAttachment()) { + DAWN_ASSERT(mAllocatedDSVs < mNumDSVs); + TextureView* view = ToBackend(renderPass->GetDepthStencilAttachment().view).Get(); + D3D12_CPU_DESCRIPTOR_HANDLE dsvHandle = mDSVHeap.GetCPUHandle(mAllocatedDSVs); + D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc = view->GetDSVDescriptor(); + mDevice->GetD3D12Device()->CreateDepthStencilView( + ToBackend(view->GetTexture())->GetD3D12Resource(), &dsvDesc, dsvHandle); + args.dsv = dsvHandle; + + ++mAllocatedDSVs; + } + + return args; + } + + bool IsHeapAllocationCompleted() const { + return mNumRTVs == mAllocatedRTVs && mNumDSVs == mAllocatedDSVs; + } + + private: + Device* mDevice; + DescriptorHeapHandle mRTVHeap = {}; + DescriptorHeapHandle mDSVHeap = {}; + uint32_t mNumRTVs = 0; + uint32_t mNumDSVs = 0; + + uint32_t mAllocatedRTVs = 0; + uint32_t mAllocatedDSVs = 0; + }; + namespace { void AllocateAndSetDescriptorHeaps(Device* device, BindGroupStateTracker* bindingTracker, + RenderPassDescriptorHeapTracker* renderPassTracker, CommandIterator* commands, int indexInSubmit) { auto* descriptorHeapAllocator = device->GetDescriptorHeapAllocator(); @@ -198,6 +281,10 @@ namespace dawn_native { namespace d3d12 { BindGroup* group = ToBackend(cmd->group.Get()); bindingTracker->TrackSetBindGroup(group, cmd->index, indexInSubmit); } break; + case Command::BeginRenderPass: { + BeginRenderPassCmd* cmd = commands->NextCommand(); + renderPassTracker->TrackRenderPass(cmd->info.Get()); + } break; default: SkipCommand(commands, type); } @@ -206,6 +293,8 @@ namespace dawn_native { namespace d3d12 { commands->Reset(); } + renderPassTracker->AllocateRTVAndDSVHeaps(); + if (bindingTracker->cbvSrvUavDescriptorIndex > 0) { // Allocate a GPU-visible heap and copy from the CPU-only heap to the GPU-visible // heap @@ -245,13 +334,15 @@ namespace dawn_native { namespace d3d12 { uint32_t indexInSubmit) { Device* device = ToBackend(GetDevice()); BindGroupStateTracker bindingTracker(device); + RenderPassDescriptorHeapTracker renderPassTracker(device); // Precompute the allocation of bindgroups in descriptor heaps // TODO(cwallez@chromium.org): Iterating over all the commands here is inefficient. We // should have a system where commands and descriptors are recorded in parallel then the // heaps set using a small CommandList inserted just before the main CommandList. { - AllocateAndSetDescriptorHeaps(device, &bindingTracker, &mCommands, indexInSubmit); + AllocateAndSetDescriptorHeaps(device, &bindingTracker, &renderPassTracker, &mCommands, + indexInSubmit); bindingTracker.Reset(); ID3D12DescriptorHeap* descriptorHeaps[2] = { @@ -301,7 +392,7 @@ namespace dawn_native { namespace d3d12 { TransitionForPass(commandList, passResourceUsages[nextPassNumber]); bindingTracker.SetInComputePass(false); - RecordRenderPass(commandList, &bindingTracker, + RecordRenderPass(commandList, &bindingTracker, &renderPassTracker, ToBackend(beginRenderPassCmd->info.Get())); nextPassNumber++; @@ -418,6 +509,8 @@ namespace dawn_native { namespace d3d12 { default: { UNREACHABLE(); } break; } } + + DAWN_ASSERT(renderPassTracker.IsHeapAllocationCompleted()); } void CommandBuffer::FlushSetVertexBuffers(ComPtr commandList, @@ -503,7 +596,10 @@ namespace dawn_native { namespace d3d12 { void CommandBuffer::RecordRenderPass(ComPtr commandList, BindGroupStateTracker* bindingTracker, + RenderPassDescriptorHeapTracker* renderPassTracker, RenderPassDescriptor* renderPass) { + OMSetRenderTargetArgs args = renderPassTracker->GetSubpassOMSetRenderTargetArgs(renderPass); + // Clear framebuffer attachments as needed and transition to render target { for (uint32_t i : IterateBitSet(renderPass->GetColorAttachmentMask())) { @@ -511,7 +607,7 @@ namespace dawn_native { namespace d3d12 { // Load op - color if (attachmentInfo.loadOp == dawn::LoadOp::Clear) { - D3D12_CPU_DESCRIPTOR_HANDLE handle = renderPass->GetRTVDescriptor(i); + D3D12_CPU_DESCRIPTOR_HANDLE handle = args.RTVs[i]; commandList->ClearRenderTargetView(handle, attachmentInfo.clearColor.data(), 0, nullptr); } @@ -536,7 +632,7 @@ namespace dawn_native { namespace d3d12 { } if (clearFlags) { - auto handle = renderPass->GetDSVDescriptor(); + D3D12_CPU_DESCRIPTOR_HANDLE handle = args.dsv; // TODO(kainino@chromium.org): investigate: should the Dawn clear // stencil type be uint8_t? uint8_t clearStencil = static_cast(attachmentInfo.clearStencil); @@ -548,8 +644,6 @@ namespace dawn_native { namespace d3d12 { // Set up render targets { - RenderPassDescriptor::OMSetRenderTargetArgs args = - renderPass->GetSubpassOMSetRenderTargetArgs(); if (args.dsv.ptr) { commandList->OMSetRenderTargets(args.numRTVs, args.RTVs.data(), FALSE, &args.dsv); } else { diff --git a/src/dawn_native/d3d12/CommandBufferD3D12.h b/src/dawn_native/d3d12/CommandBufferD3D12.h index 2ab0bb4a40..d1569ceda9 100644 --- a/src/dawn_native/d3d12/CommandBufferD3D12.h +++ b/src/dawn_native/d3d12/CommandBufferD3D12.h @@ -18,13 +18,14 @@ #include "dawn_native/CommandAllocator.h" #include "dawn_native/CommandBuffer.h" +#include "dawn_native/d3d12/Forward.h" #include "dawn_native/d3d12/InputStateD3D12.h" #include "dawn_native/d3d12/d3d12_platform.h" namespace dawn_native { namespace d3d12 { class Device; - class RenderPassDescriptor; + class RenderPassDescriptorHeapTracker; struct BindGroupStateTracker; @@ -54,6 +55,7 @@ namespace dawn_native { namespace d3d12 { BindGroupStateTracker* bindingTracker); void RecordRenderPass(ComPtr commandList, BindGroupStateTracker* bindingTracker, + RenderPassDescriptorHeapTracker* renderPassTracker, RenderPassDescriptor* renderPass); CommandIterator mCommands; diff --git a/src/dawn_native/d3d12/DeviceD3D12.cpp b/src/dawn_native/d3d12/DeviceD3D12.cpp index 9f5920c3bb..681d849fc8 100644 --- a/src/dawn_native/d3d12/DeviceD3D12.cpp +++ b/src/dawn_native/d3d12/DeviceD3D12.cpp @@ -17,6 +17,7 @@ #include "common/Assert.h" #include "dawn_native/BackendConnection.h" #include "dawn_native/DynamicUploader.h" +#include "dawn_native/RenderPassDescriptor.h" #include "dawn_native/d3d12/AdapterD3D12.h" #include "dawn_native/d3d12/BackendD3D12.h" #include "dawn_native/d3d12/BindGroupD3D12.h" @@ -30,7 +31,6 @@ #include "dawn_native/d3d12/PipelineLayoutD3D12.h" #include "dawn_native/d3d12/PlatformFunctions.h" #include "dawn_native/d3d12/QueueD3D12.h" -#include "dawn_native/d3d12/RenderPassDescriptorD3D12.h" #include "dawn_native/d3d12/RenderPipelineD3D12.h" #include "dawn_native/d3d12/ResourceAllocator.h" #include "dawn_native/d3d12/SamplerD3D12.h" diff --git a/src/dawn_native/d3d12/Forward.h b/src/dawn_native/d3d12/Forward.h index d803900c07..ed415f629e 100644 --- a/src/dawn_native/d3d12/Forward.h +++ b/src/dawn_native/d3d12/Forward.h @@ -29,7 +29,7 @@ namespace dawn_native { namespace d3d12 { class InputState; class PipelineLayout; class Queue; - class RenderPassDescriptor; + using RenderPassDescriptor = RenderPassDescriptorBase; class RenderPipeline; class Sampler; class ShaderModule; diff --git a/src/dawn_native/d3d12/RenderPassDescriptorD3D12.cpp b/src/dawn_native/d3d12/RenderPassDescriptorD3D12.cpp deleted file mode 100644 index 74ac4c9bf4..0000000000 --- a/src/dawn_native/d3d12/RenderPassDescriptorD3D12.cpp +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2017 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 "dawn_native/d3d12/RenderPassDescriptorD3D12.h" - -#include "common/BitSetIterator.h" -#include "dawn_native/d3d12/DeviceD3D12.h" -#include "dawn_native/d3d12/TextureD3D12.h" - -namespace dawn_native { namespace d3d12 { - - RenderPassDescriptor::RenderPassDescriptor(RenderPassDescriptorBuilder* builder) - : RenderPassDescriptorBase(builder) { - Device* device = ToBackend(GetDevice()); - - // Get and fill an RTV heap with the color attachments - uint32_t colorAttachmentCount = static_cast(GetColorAttachmentMask().count()); - if (colorAttachmentCount != 0) { - mRtvHeap = device->GetDescriptorHeapAllocator()->AllocateCPUHeap( - D3D12_DESCRIPTOR_HEAP_TYPE_RTV, colorAttachmentCount); - - for (uint32_t i : IterateBitSet(GetColorAttachmentMask())) { - TextureView* view = ToBackend(GetColorAttachment(i).view.Get()); - ComPtr resource = ToBackend(view->GetTexture())->GetD3D12Resource(); - - D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = mRtvHeap.GetCPUHandle(i); - D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = view->GetRTVDescriptor(); - device->GetD3D12Device()->CreateRenderTargetView(resource.Get(), &rtvDesc, - rtvHandle); - } - } - - // Get and fill a DSV heap with the depth stencil attachment - if (HasDepthStencilAttachment()) { - mDsvHeap = device->GetDescriptorHeapAllocator()->AllocateCPUHeap( - D3D12_DESCRIPTOR_HEAP_TYPE_DSV, 1); - - TextureView* view = ToBackend(GetDepthStencilAttachment().view.Get()); - ComPtr resource = ToBackend(view->GetTexture())->GetD3D12Resource(); - - D3D12_CPU_DESCRIPTOR_HANDLE dsvHandle = mDsvHeap.GetCPUHandle(0); - D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc = view->GetDSVDescriptor(); - device->GetD3D12Device()->CreateDepthStencilView(resource.Get(), &dsvDesc, dsvHandle); - } - } - - RenderPassDescriptor::OMSetRenderTargetArgs - RenderPassDescriptor::GetSubpassOMSetRenderTargetArgs() { - OMSetRenderTargetArgs args = {}; - - unsigned int rtvIndex = 0; - for (uint32_t i : IterateBitSet(GetColorAttachmentMask())) { - args.RTVs[rtvIndex] = GetRTVDescriptor(i); - rtvIndex++; - } - args.numRTVs = rtvIndex; - - if (HasDepthStencilAttachment()) { - args.dsv = GetDSVDescriptor(); - } - - return args; - } - - D3D12_CPU_DESCRIPTOR_HANDLE RenderPassDescriptor::GetRTVDescriptor(uint32_t attachmentSlot) { - return mRtvHeap.GetCPUHandle(attachmentSlot); - } - - D3D12_CPU_DESCRIPTOR_HANDLE RenderPassDescriptor::GetDSVDescriptor() { - return mDsvHeap.GetCPUHandle(0); - } - -}} // namespace dawn_native::d3d12 diff --git a/src/dawn_native/d3d12/RenderPassDescriptorD3D12.h b/src/dawn_native/d3d12/RenderPassDescriptorD3D12.h deleted file mode 100644 index 11aec2483a..0000000000 --- a/src/dawn_native/d3d12/RenderPassDescriptorD3D12.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2017 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. - -#ifndef DAWNNATIVE_D3D12_RENDERPASSDESCRIPTORD3D12_H_ -#define DAWNNATIVE_D3D12_RENDERPASSDESCRIPTORD3D12_H_ - -#include "dawn_native/RenderPassDescriptor.h" - -#include "common/Constants.h" -#include "dawn_native/d3d12/DescriptorHeapAllocator.h" -#include "dawn_native/d3d12/d3d12_platform.h" - -#include -#include - -namespace dawn_native { namespace d3d12 { - - class Device; - - class RenderPassDescriptor : public RenderPassDescriptorBase { - public: - struct OMSetRenderTargetArgs { - unsigned int numRTVs = 0; - std::array RTVs = {}; - D3D12_CPU_DESCRIPTOR_HANDLE dsv = {}; - }; - - RenderPassDescriptor(RenderPassDescriptorBuilder* builder); - OMSetRenderTargetArgs GetSubpassOMSetRenderTargetArgs(); - D3D12_CPU_DESCRIPTOR_HANDLE GetRTVDescriptor(uint32_t attachmentSlot); - D3D12_CPU_DESCRIPTOR_HANDLE GetDSVDescriptor(); - - private: - DescriptorHeapHandle mRtvHeap = {}; - DescriptorHeapHandle mDsvHeap = {}; - }; - -}} // namespace dawn_native::d3d12 - -#endif // DAWNNATIVE_D3D12_RENDERPASSDESCRIPTORD3D12_H_ diff --git a/src/tests/end2end/RenderPassTests.cpp b/src/tests/end2end/RenderPassTests.cpp new file mode 100644 index 0000000000..b5eccae219 --- /dev/null +++ b/src/tests/end2end/RenderPassTests.cpp @@ -0,0 +1,131 @@ +// 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/ComboRenderPipelineDescriptor.h" +#include "utils/DawnHelpers.h" + +constexpr uint32_t kRTSize = 16; +constexpr dawn::TextureFormat kFormat = dawn::TextureFormat::R8G8B8A8Unorm; + +class RenderPassTest : public DawnTest { +protected: + void SetUp() override { + DawnTest::SetUp(); + + // Shaders to draw a bottom-left triangle in blue. + dawn::ShaderModule vsModule = + utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, R"( + #version 450 + void main() { + const vec2 pos[3] = vec2[3]( + vec2(-1.f, -1.f), vec2(1.f, 1.f), vec2(-1.f, 1.f)); + gl_Position = vec4(pos[gl_VertexIndex], 0.f, 1.f); + })"); + + dawn::ShaderModule fsModule = + utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"( + #version 450 + layout(location = 0) out vec4 fragColor; + void main() { + fragColor = vec4(0.0, 0.0, 1.0, 1.0); + })"); + + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.cVertexStage.module = vsModule; + descriptor.cFragmentStage.module = fsModule; + descriptor.primitiveTopology = dawn::PrimitiveTopology::TriangleStrip; + descriptor.indexFormat = dawn::IndexFormat::Uint32; + descriptor.cColorStates[0].format = kFormat; + + pipeline = device.CreateRenderPipeline(&descriptor); + } + + dawn::Texture CreateDefault2DTexture() { + dawn::TextureDescriptor descriptor; + descriptor.dimension = dawn::TextureDimension::e2D; + descriptor.size.width = kRTSize; + descriptor.size.height = kRTSize; + descriptor.size.depth = 1; + descriptor.arraySize = 1; + descriptor.sampleCount = 1; + descriptor.format = kFormat; + descriptor.levelCount = 1; + descriptor.usage = + dawn::TextureUsageBit::OutputAttachment | dawn::TextureUsageBit::TransferSrc; + return device.CreateTexture(&descriptor); + } + + dawn::RenderPipeline pipeline; +}; + +// Test using two different render passes in one commandBuffer works correctly. +TEST_P(RenderPassTest, TwoRenderPassesInOneCommandBuffer) { + constexpr RGBA8 kRed(255, 0, 0, 255); + constexpr RGBA8 kGreen(0, 255, 0, 255); + + constexpr RGBA8 kBlue(0, 0, 255, 255); + + dawn::Texture renderTarget1 = CreateDefault2DTexture(); + dawn::Texture renderTarget2 = CreateDefault2DTexture(); + dawn::CommandBufferBuilder commandBufferBuilder = device.CreateCommandBufferBuilder(); + + dawn::RenderPassColorAttachmentDescriptor colorAttachment; + colorAttachment.loadOp = dawn::LoadOp::Clear; + colorAttachment.storeOp = dawn::StoreOp::Store; + colorAttachment.resolveTarget = nullptr; + + { + // In the first render pass we clear renderTarget1 to red and draw a blue triangle in the + // bottom left of renderTarget1. + colorAttachment.clearColor = { 1.0, 0.0, 0.0, 1.0 }; + + colorAttachment.attachment = renderTarget1.CreateDefaultTextureView(); + dawn::RenderPassDescriptor renderPass = device.CreateRenderPassDescriptorBuilder() + .SetColorAttachments(1, &colorAttachment) + .GetResult(); + + dawn::RenderPassEncoder pass = commandBufferBuilder.BeginRenderPass(renderPass); + pass.SetPipeline(pipeline); + pass.Draw(3, 1, 0, 0); + pass.EndPass(); + } + + { + // In the second render pass we clear renderTarget2 to green and draw a blue triangle in the + // bottom left of renderTarget2. + colorAttachment.attachment = renderTarget2.CreateDefaultTextureView(); + colorAttachment.clearColor = { 0.0, 1.0, 0.0, 1.0 }; + dawn::RenderPassDescriptor renderPass = device.CreateRenderPassDescriptorBuilder() + .SetColorAttachments(1, &colorAttachment) + .GetResult(); + + dawn::RenderPassEncoder pass = commandBufferBuilder.BeginRenderPass(renderPass); + pass.SetPipeline(pipeline); + pass.Draw(3, 1, 0, 0); + pass.EndPass(); + } + + dawn::CommandBuffer commands = commandBufferBuilder.GetResult(); + queue.Submit(1, &commands); + + EXPECT_PIXEL_RGBA8_EQ(kBlue, renderTarget1, 1, kRTSize - 1); + EXPECT_PIXEL_RGBA8_EQ(kRed, renderTarget1, kRTSize - 1, 1); + + EXPECT_PIXEL_RGBA8_EQ(kBlue, renderTarget2, 1, kRTSize - 1); + EXPECT_PIXEL_RGBA8_EQ(kGreen, renderTarget2, kRTSize - 1, 1); +} + +DAWN_INSTANTIATE_TEST(RenderPassTest, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend)