D3D12: Remove RenderPassDescriptorD3D12

This patch removes RenderPassDescriptorD3D12 completely and moves the
creation of RTVs and DSVs to CommandBufferD3D12::RecordCommands(), where
we allocate all RTVs and DSVs used in the current command buffer in one
RTV heap and one DSV heap. Note that the method to allocate RTVs and
DSVs are too simple in this patch, and we will optimize it later.

This patch also adds a test to make sure Dawn works correctly when we
use two different render passes in one command buffer.

This patch is one of the preparations on completely removing
RenderPassDescriptorBuilder from Dawn.

BUG=dawn:6

Change-Id: I02e30c007fb8668a7474a3caf7a858782d0c92df
Reviewed-on: https://dawn-review.googlesource.com/c/4520
Commit-Queue: Jiawei Shao <jiawei.shao@intel.com>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
diff --git a/BUILD.gn b/BUILD.gn
index 933c915..77cdae1 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -537,8 +537,6 @@
       "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 @@
     "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 c85c8ed..d8e7429 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 @@
         }
     };
 
+    struct OMSetRenderTargetArgs {
+        unsigned int numRTVs = 0;
+        std::array<D3D12_CPU_DESCRIPTOR_HANDLE, kMaxColorAttachments> 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<uint32_t>(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 <RenderPass, OMSetRenderTargetArgs> 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<uint32_t>(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 @@
                             BindGroup* group = ToBackend(cmd->group.Get());
                             bindingTracker->TrackSetBindGroup(group, cmd->index, indexInSubmit);
                         } break;
+                        case Command::BeginRenderPass: {
+                            BeginRenderPassCmd* cmd = commands->NextCommand<BeginRenderPassCmd>();
+                            renderPassTracker->TrackRenderPass(cmd->info.Get());
+                        } break;
                         default:
                             SkipCommand(commands, type);
                     }
@@ -206,6 +293,8 @@
                 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 @@
                                        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 @@
 
                     TransitionForPass(commandList, passResourceUsages[nextPassNumber]);
                     bindingTracker.SetInComputePass(false);
-                    RecordRenderPass(commandList, &bindingTracker,
+                    RecordRenderPass(commandList, &bindingTracker, &renderPassTracker,
                                      ToBackend(beginRenderPassCmd->info.Get()));
 
                     nextPassNumber++;
@@ -418,6 +509,8 @@
                 default: { UNREACHABLE(); } break;
             }
         }
+
+        DAWN_ASSERT(renderPassTracker.IsHeapAllocationCompleted());
     }
 
     void CommandBuffer::FlushSetVertexBuffers(ComPtr<ID3D12GraphicsCommandList> commandList,
@@ -503,7 +596,10 @@
 
     void CommandBuffer::RecordRenderPass(ComPtr<ID3D12GraphicsCommandList> 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 @@
 
                 // 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 @@
                 }
 
                 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<uint8_t>(attachmentInfo.clearStencil);
@@ -548,8 +644,6 @@
 
         // 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 2ab0bb4..d1569ce 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 @@
                                BindGroupStateTracker* bindingTracker);
         void RecordRenderPass(ComPtr<ID3D12GraphicsCommandList> 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 9f5920c..681d849 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 d803900..ed415f6 100644
--- a/src/dawn_native/d3d12/Forward.h
+++ b/src/dawn_native/d3d12/Forward.h
@@ -29,7 +29,7 @@
     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 74ac4c9..0000000
--- 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<uint32_t>(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<ID3D12Resource> 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<ID3D12Resource> 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 11aec24..0000000
--- 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 <array>
-#include <vector>
-
-namespace dawn_native { namespace d3d12 {
-
-    class Device;
-
-    class RenderPassDescriptor : public RenderPassDescriptorBase {
-      public:
-        struct OMSetRenderTargetArgs {
-            unsigned int numRTVs = 0;
-            std::array<D3D12_CPU_DESCRIPTOR_HANDLE, kMaxColorAttachments> 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 0000000..b5eccae
--- /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)