From c1583a558c5898098423bc2de0b9da1ab04f4770 Mon Sep 17 00:00:00 2001
From: Idan Raiter <idanr@google.com>
Date: Fri, 2 Aug 2019 05:16:42 +0000
Subject: [PATCH] Create CommandRecordingContext

Adds a structure that contains wait / signal semaphores and the current command buffer. Will allow us to have a list of pending semaphores after recording, either to consume or do something smarter with in the future.

Bug: chromium:976495
Change-Id: Ib61455039bd97ac8b0ff701af2b694cc8794226d
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/9600
Commit-Queue: Kai Ninomiya <kainino@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
---
 src/dawn_native/vulkan/BufferVk.cpp           | 15 +++--
 src/dawn_native/vulkan/BufferVk.h             |  4 +-
 src/dawn_native/vulkan/CommandBufferVk.cpp    | 66 +++++++++++--------
 src/dawn_native/vulkan/CommandBufferVk.h      |  8 ++-
 .../vulkan/CommandRecordingContext.h          | 33 ++++++++++
 src/dawn_native/vulkan/DeviceVk.cpp           | 36 ++++++----
 src/dawn_native/vulkan/DeviceVk.h             |  6 +-
 .../vulkan/NativeSwapChainImplVk.cpp          |  2 +-
 src/dawn_native/vulkan/QueueVk.cpp            |  5 +-
 src/dawn_native/vulkan/SwapChainVk.cpp        |  4 +-
 src/dawn_native/vulkan/TextureVk.cpp          | 25 +++----
 src/dawn_native/vulkan/TextureVk.h            |  9 ++-
 12 files changed, 141 insertions(+), 72 deletions(-)
 create mode 100644 src/dawn_native/vulkan/CommandRecordingContext.h

diff --git a/src/dawn_native/vulkan/BufferVk.cpp b/src/dawn_native/vulkan/BufferVk.cpp
index e7b3bea1e7..d1e6c62bb1 100644
--- a/src/dawn_native/vulkan/BufferVk.cpp
+++ b/src/dawn_native/vulkan/BufferVk.cpp
@@ -163,7 +163,8 @@ namespace dawn_native { namespace vulkan {
         return mHandle;
     }
 
-    void Buffer::TransitionUsageNow(VkCommandBuffer commands, dawn::BufferUsageBit usage) {
+    void Buffer::TransitionUsageNow(CommandRecordingContext* recordingContext,
+                                    dawn::BufferUsageBit usage) {
         bool lastIncludesTarget = (mLastUsage & usage) == usage;
         bool lastReadOnly = (mLastUsage & kReadOnlyBufferUsages) == mLastUsage;
 
@@ -193,8 +194,8 @@ namespace dawn_native { namespace vulkan {
         barrier.size = GetSize();
 
         ToBackend(GetDevice())
-            ->fn.CmdPipelineBarrier(commands, srcStages, dstStages, 0, 0, nullptr, 1, &barrier, 0,
-                                    nullptr);
+            ->fn.CmdPipelineBarrier(recordingContext->commandBuffer, srcStages, dstStages, 0, 0,
+                                    nullptr, 1, &barrier, 0, nullptr);
 
         mLastUsage = usage;
     }
@@ -212,8 +213,8 @@ namespace dawn_native { namespace vulkan {
     MaybeError Buffer::MapReadAsyncImpl(uint32_t serial) {
         Device* device = ToBackend(GetDevice());
 
-        VkCommandBuffer commands = device->GetPendingCommandBuffer();
-        TransitionUsageNow(commands, dawn::BufferUsageBit::MapRead);
+        CommandRecordingContext* recordingContext = device->GetPendingRecordingContext();
+        TransitionUsageNow(recordingContext, dawn::BufferUsageBit::MapRead);
 
         uint8_t* memory = mMemoryAllocation.GetMappedPointer();
         ASSERT(memory != nullptr);
@@ -226,8 +227,8 @@ namespace dawn_native { namespace vulkan {
     MaybeError Buffer::MapWriteAsyncImpl(uint32_t serial) {
         Device* device = ToBackend(GetDevice());
 
-        VkCommandBuffer commands = device->GetPendingCommandBuffer();
-        TransitionUsageNow(commands, dawn::BufferUsageBit::MapWrite);
+        CommandRecordingContext* recordingContext = device->GetPendingRecordingContext();
+        TransitionUsageNow(recordingContext, dawn::BufferUsageBit::MapWrite);
 
         uint8_t* memory = mMemoryAllocation.GetMappedPointer();
         ASSERT(memory != nullptr);
diff --git a/src/dawn_native/vulkan/BufferVk.h b/src/dawn_native/vulkan/BufferVk.h
index 81d7a4e3ff..abf735daf5 100644
--- a/src/dawn_native/vulkan/BufferVk.h
+++ b/src/dawn_native/vulkan/BufferVk.h
@@ -23,6 +23,7 @@
 
 namespace dawn_native { namespace vulkan {
 
+    struct CommandRecordingContext;
     class Device;
 
     class Buffer : public BufferBase {
@@ -38,7 +39,8 @@ namespace dawn_native { namespace vulkan {
         // Transitions the buffer to be used as `usage`, recording any necessary barrier in
         // `commands`.
         // TODO(cwallez@chromium.org): coalesce barriers and do them early when possible.
-        void TransitionUsageNow(VkCommandBuffer commands, dawn::BufferUsageBit usage);
+        void TransitionUsageNow(CommandRecordingContext* recordingContext,
+                                dawn::BufferUsageBit usage);
 
       private:
         // Dawn API
diff --git a/src/dawn_native/vulkan/CommandBufferVk.cpp b/src/dawn_native/vulkan/CommandBufferVk.cpp
index c682a6d79f..1d626e03fa 100644
--- a/src/dawn_native/vulkan/CommandBufferVk.cpp
+++ b/src/dawn_native/vulkan/CommandBufferVk.cpp
@@ -18,6 +18,7 @@
 #include "dawn_native/Commands.h"
 #include "dawn_native/vulkan/BindGroupVk.h"
 #include "dawn_native/vulkan/BufferVk.h"
+#include "dawn_native/vulkan/CommandRecordingContext.h"
 #include "dawn_native/vulkan/ComputePipelineVk.h"
 #include "dawn_native/vulkan/DeviceVk.h"
 #include "dawn_native/vulkan/FencedDeleter.h"
@@ -189,9 +190,11 @@ namespace dawn_native { namespace vulkan {
             std::array<std::array<uint32_t, kMaxBindingsPerGroup>, kMaxBindGroups> mDynamicOffsets;
         };
 
-        void RecordBeginRenderPass(VkCommandBuffer commands,
+        void RecordBeginRenderPass(CommandRecordingContext* recordingContext,
                                    Device* device,
                                    BeginRenderPassCmd* renderPass) {
+            VkCommandBuffer commands = recordingContext->commandBuffer;
+
             // Query a VkRenderPass from the cache
             VkRenderPass renderPassVK = VK_NULL_HANDLE;
             {
@@ -232,7 +235,7 @@ namespace dawn_native { namespace vulkan {
                         attachmentInfo.stencilLoadOp == dawn::LoadOp::Load) {
                         ToBackend(attachmentInfo.view->GetTexture())
                             ->EnsureSubresourceContentInitialized(
-                                commands, attachmentInfo.view->GetBaseMipLevel(),
+                                recordingContext, attachmentInfo.view->GetBaseMipLevel(),
                                 attachmentInfo.view->GetLevelCount(),
                                 attachmentInfo.view->GetBaseArrayLayer(),
                                 attachmentInfo.view->GetLayerCount());
@@ -339,14 +342,16 @@ namespace dawn_native { namespace vulkan {
         FreeCommands(&mCommands);
     }
 
-    void CommandBuffer::RecordCommands(VkCommandBuffer commands) {
+    void CommandBuffer::RecordCommands(CommandRecordingContext* recordingContext) {
         Device* device = ToBackend(GetDevice());
+        VkCommandBuffer commands = recordingContext->commandBuffer;
 
         // Records the necessary barriers for the resource usage pre-computed by the frontend
-        auto TransitionForPass = [](VkCommandBuffer commands, const PassResourceUsage& usages) {
+        auto TransitionForPass = [](CommandRecordingContext* recordingContext,
+                                    const PassResourceUsage& usages) {
             for (size_t i = 0; i < usages.buffers.size(); ++i) {
                 Buffer* buffer = ToBackend(usages.buffers[i]);
-                buffer->TransitionUsageNow(commands, usages.bufferUsages[i]);
+                buffer->TransitionUsageNow(recordingContext, usages.bufferUsages[i]);
             }
             for (size_t i = 0; i < usages.textures.size(); ++i) {
                 Texture* texture = ToBackend(usages.textures[i]);
@@ -354,8 +359,8 @@ namespace dawn_native { namespace vulkan {
                 // TODO(natlee@microsoft.com): Update clearing here when subresource tracking is
                 // implemented
                 texture->EnsureSubresourceContentInitialized(
-                    commands, 0, texture->GetNumMipLevels(), 0, texture->GetArrayLayers());
-                texture->TransitionUsageNow(commands, usages.textureUsages[i]);
+                    recordingContext, 0, texture->GetNumMipLevels(), 0, texture->GetArrayLayers());
+                texture->TransitionUsageNow(recordingContext, usages.textureUsages[i]);
             }
         };
 
@@ -370,8 +375,8 @@ namespace dawn_native { namespace vulkan {
                     Buffer* srcBuffer = ToBackend(copy->source.Get());
                     Buffer* dstBuffer = ToBackend(copy->destination.Get());
 
-                    srcBuffer->TransitionUsageNow(commands, dawn::BufferUsageBit::CopySrc);
-                    dstBuffer->TransitionUsageNow(commands, dawn::BufferUsageBit::CopyDst);
+                    srcBuffer->TransitionUsageNow(recordingContext, dawn::BufferUsageBit::CopySrc);
+                    dstBuffer->TransitionUsageNow(recordingContext, dawn::BufferUsageBit::CopyDst);
 
                     VkBufferCopy region;
                     region.srcOffset = copy->sourceOffset;
@@ -399,13 +404,14 @@ namespace dawn_native { namespace vulkan {
                             subresource.mipLevel, 1, subresource.baseArrayLayer, 1);
                     } else {
                         ToBackend(dst.texture)
-                            ->EnsureSubresourceContentInitialized(commands, subresource.mipLevel, 1,
+                            ->EnsureSubresourceContentInitialized(recordingContext,
+                                                                  subresource.mipLevel, 1,
                                                                   subresource.baseArrayLayer, 1);
                     }
                     ToBackend(src.buffer)
-                        ->TransitionUsageNow(commands, dawn::BufferUsageBit::CopySrc);
+                        ->TransitionUsageNow(recordingContext, dawn::BufferUsageBit::CopySrc);
                     ToBackend(dst.texture)
-                        ->TransitionUsageNow(commands, dawn::TextureUsageBit::CopyDst);
+                        ->TransitionUsageNow(recordingContext, dawn::TextureUsageBit::CopyDst);
                     VkBuffer srcBuffer = ToBackend(src.buffer)->GetHandle();
                     VkImage dstImage = ToBackend(dst.texture)->GetHandle();
 
@@ -426,13 +432,14 @@ namespace dawn_native { namespace vulkan {
                     VkImageSubresourceLayers subresource = region.imageSubresource;
 
                     ToBackend(src.texture)
-                        ->EnsureSubresourceContentInitialized(commands, subresource.mipLevel, 1,
+                        ->EnsureSubresourceContentInitialized(recordingContext,
+                                                              subresource.mipLevel, 1,
                                                               subresource.baseArrayLayer, 1);
 
                     ToBackend(src.texture)
-                        ->TransitionUsageNow(commands, dawn::TextureUsageBit::CopySrc);
+                        ->TransitionUsageNow(recordingContext, dawn::TextureUsageBit::CopySrc);
                     ToBackend(dst.buffer)
-                        ->TransitionUsageNow(commands, dawn::BufferUsageBit::CopyDst);
+                        ->TransitionUsageNow(recordingContext, dawn::BufferUsageBit::CopyDst);
 
                     VkImage srcImage = ToBackend(src.texture)->GetHandle();
                     VkBuffer dstBuffer = ToBackend(dst.buffer)->GetHandle();
@@ -452,7 +459,8 @@ namespace dawn_native { namespace vulkan {
                     VkImageSubresourceLayers srcSubresource = region.srcSubresource;
 
                     ToBackend(src.texture)
-                        ->EnsureSubresourceContentInitialized(commands, srcSubresource.mipLevel, 1,
+                        ->EnsureSubresourceContentInitialized(recordingContext,
+                                                              srcSubresource.mipLevel, 1,
                                                               srcSubresource.baseArrayLayer, 1);
                     if (IsCompleteSubresourceCopiedTo(dst.texture.Get(), copy->copySize,
                                                       dstSubresource.mipLevel)) {
@@ -461,14 +469,14 @@ namespace dawn_native { namespace vulkan {
                             dstSubresource.mipLevel, 1, dstSubresource.baseArrayLayer, 1);
                     } else {
                         ToBackend(dst.texture)
-                            ->EnsureSubresourceContentInitialized(commands, dstSubresource.mipLevel,
-                                                                  1, dstSubresource.baseArrayLayer,
-                                                                  1);
+                            ->EnsureSubresourceContentInitialized(recordingContext,
+                                                                  dstSubresource.mipLevel, 1,
+                                                                  dstSubresource.baseArrayLayer, 1);
                     }
                     ToBackend(src.texture)
-                        ->TransitionUsageNow(commands, dawn::TextureUsageBit::CopySrc);
+                        ->TransitionUsageNow(recordingContext, dawn::TextureUsageBit::CopySrc);
                     ToBackend(dst.texture)
-                        ->TransitionUsageNow(commands, dawn::TextureUsageBit::CopyDst);
+                        ->TransitionUsageNow(recordingContext, dawn::TextureUsageBit::CopyDst);
                     VkImage srcImage = ToBackend(src.texture)->GetHandle();
                     VkImage dstImage = ToBackend(dst.texture)->GetHandle();
 
@@ -481,8 +489,8 @@ namespace dawn_native { namespace vulkan {
                 case Command::BeginRenderPass: {
                     BeginRenderPassCmd* cmd = mCommands.NextCommand<BeginRenderPassCmd>();
 
-                    TransitionForPass(commands, passResourceUsages[nextPassNumber]);
-                    RecordRenderPass(commands, cmd);
+                    TransitionForPass(recordingContext, passResourceUsages[nextPassNumber]);
+                    RecordRenderPass(recordingContext, cmd);
 
                     nextPassNumber++;
                 } break;
@@ -490,8 +498,8 @@ namespace dawn_native { namespace vulkan {
                 case Command::BeginComputePass: {
                     mCommands.NextCommand<BeginComputePassCmd>();
 
-                    TransitionForPass(commands, passResourceUsages[nextPassNumber]);
-                    RecordComputePass(commands);
+                    TransitionForPass(recordingContext, passResourceUsages[nextPassNumber]);
+                    RecordComputePass(recordingContext);
 
                     nextPassNumber++;
                 } break;
@@ -501,8 +509,9 @@ namespace dawn_native { namespace vulkan {
         }
     }
 
-    void CommandBuffer::RecordComputePass(VkCommandBuffer commands) {
+    void CommandBuffer::RecordComputePass(CommandRecordingContext* recordingContext) {
         Device* device = ToBackend(GetDevice());
+        VkCommandBuffer commands = recordingContext->commandBuffer;
 
         DescriptorSetTracker descriptorSets;
 
@@ -558,11 +567,12 @@ namespace dawn_native { namespace vulkan {
         // EndComputePass should have been called
         UNREACHABLE();
     }
-    void CommandBuffer::RecordRenderPass(VkCommandBuffer commands,
+    void CommandBuffer::RecordRenderPass(CommandRecordingContext* recordingContext,
                                          BeginRenderPassCmd* renderPassCmd) {
         Device* device = ToBackend(GetDevice());
+        VkCommandBuffer commands = recordingContext->commandBuffer;
 
-        RecordBeginRenderPass(commands, device, renderPassCmd);
+        RecordBeginRenderPass(recordingContext, device, renderPassCmd);
 
         // Set the default value for the dynamic state
         {
diff --git a/src/dawn_native/vulkan/CommandBufferVk.h b/src/dawn_native/vulkan/CommandBufferVk.h
index a9608fed6e..6025eef04b 100644
--- a/src/dawn_native/vulkan/CommandBufferVk.h
+++ b/src/dawn_native/vulkan/CommandBufferVk.h
@@ -26,6 +26,7 @@ namespace dawn_native {
 
 namespace dawn_native { namespace vulkan {
 
+    struct CommandRecordingContext;
     class Device;
 
     class CommandBuffer : public CommandBufferBase {
@@ -33,11 +34,12 @@ namespace dawn_native { namespace vulkan {
         CommandBuffer(CommandEncoderBase* encoder, const CommandBufferDescriptor* descriptor);
         ~CommandBuffer();
 
-        void RecordCommands(VkCommandBuffer commands);
+        void RecordCommands(CommandRecordingContext* recordingContext);
 
       private:
-        void RecordComputePass(VkCommandBuffer commands);
-        void RecordRenderPass(VkCommandBuffer commands, BeginRenderPassCmd* renderPass);
+        void RecordComputePass(CommandRecordingContext* recordingContext);
+        void RecordRenderPass(CommandRecordingContext* recordingContext,
+                              BeginRenderPassCmd* renderPass);
 
         CommandIterator mCommands;
     };
diff --git a/src/dawn_native/vulkan/CommandRecordingContext.h b/src/dawn_native/vulkan/CommandRecordingContext.h
new file mode 100644
index 0000000000..5de9087397
--- /dev/null
+++ b/src/dawn_native/vulkan/CommandRecordingContext.h
@@ -0,0 +1,33 @@
+// 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.
+#ifndef DAWNNATIVE_VULKAN_COMMANDRECORDINGCONTEXT_H_
+#define DAWNNATIVE_VULKAN_COMMANDRECORDINGCONTEXT_H_
+
+#include "common/vulkan_platform.h"
+
+#include <vector>
+
+namespace dawn_native { namespace vulkan {
+
+    // Used to track operations that are handled after recording.
+    // Currently only tracks semaphores, but may be used to do barrier coalescing in the future.
+    struct CommandRecordingContext {
+        VkCommandBuffer commandBuffer = VK_NULL_HANDLE;
+        std::vector<VkSemaphore> waitSemaphores = {};
+        std::vector<VkSemaphore> signalSemaphores = {};
+    };
+
+}}  // namespace dawn_native::vulkan
+
+#endif  // DAWNNATIVE_VULKAN_COMMANDRECORDINGCONTEXT_H_
diff --git a/src/dawn_native/vulkan/DeviceVk.cpp b/src/dawn_native/vulkan/DeviceVk.cpp
index 3d370e3284..94845c7191 100644
--- a/src/dawn_native/vulkan/DeviceVk.cpp
+++ b/src/dawn_native/vulkan/DeviceVk.cpp
@@ -108,7 +108,8 @@ namespace dawn_native { namespace vulkan {
         }
         mUnusedCommands.clear();
 
-        ASSERT(mWaitSemaphores.empty());
+        ASSERT(mRecordingContext.waitSemaphores.empty());
+        ASSERT(mRecordingContext.signalSemaphores.empty());
 
         for (VkFence fence : mUnusedFences) {
             fn.DestroyFence(mVkDevice, fence, nullptr);
@@ -276,6 +277,14 @@ namespace dawn_native { namespace vulkan {
         return mPendingCommands.commandBuffer;
     }
 
+    CommandRecordingContext* Device::GetPendingRecordingContext() {
+        if (mRecordingContext.commandBuffer == VK_NULL_HANDLE) {
+            mRecordingContext.commandBuffer = GetPendingCommandBuffer();
+        }
+
+        return &mRecordingContext;
+    }
+
     void Device::SubmitPendingCommands() {
         if (mPendingCommands.pool == VK_NULL_HANDLE) {
             return;
@@ -285,19 +294,21 @@ namespace dawn_native { namespace vulkan {
             ASSERT(false);
         }
 
-        std::vector<VkPipelineStageFlags> dstStageMasks(mWaitSemaphores.size(),
+        std::vector<VkPipelineStageFlags> dstStageMasks(mRecordingContext.waitSemaphores.size(),
                                                         VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
 
         VkSubmitInfo submitInfo;
         submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
         submitInfo.pNext = nullptr;
-        submitInfo.waitSemaphoreCount = static_cast<uint32_t>(mWaitSemaphores.size());
-        submitInfo.pWaitSemaphores = mWaitSemaphores.data();
+        submitInfo.waitSemaphoreCount =
+            static_cast<uint32_t>(mRecordingContext.waitSemaphores.size());
+        submitInfo.pWaitSemaphores = mRecordingContext.waitSemaphores.data();
         submitInfo.pWaitDstStageMask = dstStageMasks.data();
         submitInfo.commandBufferCount = 1;
         submitInfo.pCommandBuffers = &mPendingCommands.commandBuffer;
-        submitInfo.signalSemaphoreCount = 0;
-        submitInfo.pSignalSemaphores = 0;
+        submitInfo.signalSemaphoreCount =
+            static_cast<uint32_t>(mRecordingContext.signalSemaphores.size());
+        submitInfo.pSignalSemaphores = mRecordingContext.signalSemaphores.data();
 
         VkFence fence = GetUnusedFence();
         if (fn.QueueSubmit(mQueue, 1, &submitInfo, fence) != VK_SUCCESS) {
@@ -309,14 +320,15 @@ namespace dawn_native { namespace vulkan {
         mPendingCommands = CommandPoolAndBuffer();
         mFencesInFlight.emplace(fence, mLastSubmittedSerial);
 
-        for (VkSemaphore semaphore : mWaitSemaphores) {
+        for (VkSemaphore semaphore : mRecordingContext.waitSemaphores) {
             mDeleter->DeleteWhenUnused(semaphore);
         }
-        mWaitSemaphores.clear();
-    }
 
-    void Device::AddWaitSemaphore(VkSemaphore semaphore) {
-        mWaitSemaphores.push_back(semaphore);
+        for (VkSemaphore semaphore : mRecordingContext.signalSemaphores) {
+            mDeleter->DeleteWhenUnused(semaphore);
+        }
+
+        mRecordingContext = CommandRecordingContext();
     }
 
     ResultOrError<VulkanDeviceKnobs> Device::CreateDevice(VkPhysicalDevice physicalDevice) {
@@ -529,7 +541,7 @@ namespace dawn_native { namespace vulkan {
         // Insert pipeline barrier to ensure correct ordering with previous memory operations on the
         // buffer.
         ToBackend(destination)
-            ->TransitionUsageNow(GetPendingCommandBuffer(), dawn::BufferUsageBit::CopyDst);
+            ->TransitionUsageNow(GetPendingRecordingContext(), dawn::BufferUsageBit::CopyDst);
 
         VkBufferCopy copy;
         copy.srcOffset = sourceOffset;
diff --git a/src/dawn_native/vulkan/DeviceVk.h b/src/dawn_native/vulkan/DeviceVk.h
index 7eb33620d6..be76c162d7 100644
--- a/src/dawn_native/vulkan/DeviceVk.h
+++ b/src/dawn_native/vulkan/DeviceVk.h
@@ -20,6 +20,7 @@
 #include "common/Serial.h"
 #include "common/SerialQueue.h"
 #include "dawn_native/Device.h"
+#include "dawn_native/vulkan/CommandRecordingContext.h"
 #include "dawn_native/vulkan/Forward.h"
 #include "dawn_native/vulkan/VulkanFunctions.h"
 #include "dawn_native/vulkan/VulkanInfo.h"
@@ -59,9 +60,9 @@ namespace dawn_native { namespace vulkan {
         RenderPassCache* GetRenderPassCache() const;
 
         VkCommandBuffer GetPendingCommandBuffer();
+        CommandRecordingContext* GetPendingRecordingContext();
         Serial GetPendingCommandSerial() const override;
         void SubmitPendingCommands();
-        void AddWaitSemaphore(VkSemaphore semaphore);
 
         // Dawn API
         CommandBufferBase* CreateCommandBuffer(CommandEncoderBase* encoder,
@@ -142,7 +143,8 @@ namespace dawn_native { namespace vulkan {
         SerialQueue<CommandPoolAndBuffer> mCommandsInFlight;
         std::vector<CommandPoolAndBuffer> mUnusedCommands;
         CommandPoolAndBuffer mPendingCommands;
-        std::vector<VkSemaphore> mWaitSemaphores;
+
+        CommandRecordingContext mRecordingContext;
     };
 
 }}  // namespace dawn_native::vulkan
diff --git a/src/dawn_native/vulkan/NativeSwapChainImplVk.cpp b/src/dawn_native/vulkan/NativeSwapChainImplVk.cpp
index 264ad32770..5f1e5ad39a 100644
--- a/src/dawn_native/vulkan/NativeSwapChainImplVk.cpp
+++ b/src/dawn_native/vulkan/NativeSwapChainImplVk.cpp
@@ -184,7 +184,7 @@ namespace dawn_native { namespace vulkan {
         }
 
         nextTexture->texture.u64 = mSwapChainImages[mLastImageIndex].GetU64();
-        mDevice->AddWaitSemaphore(semaphore);
+        mDevice->GetPendingRecordingContext()->waitSemaphores.push_back(semaphore);
 
         return DAWN_SWAP_CHAIN_NO_ERROR;
     }
diff --git a/src/dawn_native/vulkan/QueueVk.cpp b/src/dawn_native/vulkan/QueueVk.cpp
index a5451b182b..c268bcd3d4 100644
--- a/src/dawn_native/vulkan/QueueVk.cpp
+++ b/src/dawn_native/vulkan/QueueVk.cpp
@@ -15,6 +15,7 @@
 #include "dawn_native/vulkan/QueueVk.h"
 
 #include "dawn_native/vulkan/CommandBufferVk.h"
+#include "dawn_native/vulkan/CommandRecordingContext.h"
 #include "dawn_native/vulkan/DeviceVk.h"
 
 namespace dawn_native { namespace vulkan {
@@ -30,9 +31,9 @@ namespace dawn_native { namespace vulkan {
 
         device->Tick();
 
-        VkCommandBuffer commandBuffer = device->GetPendingCommandBuffer();
+        CommandRecordingContext* recordingContext = device->GetPendingRecordingContext();
         for (uint32_t i = 0; i < commandCount; ++i) {
-            ToBackend(commands[i])->RecordCommands(commandBuffer);
+            ToBackend(commands[i])->RecordCommands(recordingContext);
         }
 
         device->SubmitPendingCommands();
diff --git a/src/dawn_native/vulkan/SwapChainVk.cpp b/src/dawn_native/vulkan/SwapChainVk.cpp
index f4e857d4d8..37877b4161 100644
--- a/src/dawn_native/vulkan/SwapChainVk.cpp
+++ b/src/dawn_native/vulkan/SwapChainVk.cpp
@@ -51,8 +51,8 @@ namespace dawn_native { namespace vulkan {
 
         // Perform the necessary pipeline barriers for the texture to be used with the usage
         // requested by the implementation.
-        VkCommandBuffer commands = device->GetPendingCommandBuffer();
-        ToBackend(texture)->TransitionUsageNow(commands, mTextureUsage);
+        CommandRecordingContext* recordingContext = device->GetPendingRecordingContext();
+        ToBackend(texture)->TransitionUsageNow(recordingContext, mTextureUsage);
 
         device->SubmitPendingCommands();
     }
diff --git a/src/dawn_native/vulkan/TextureVk.cpp b/src/dawn_native/vulkan/TextureVk.cpp
index 7512712160..727292c946 100644
--- a/src/dawn_native/vulkan/TextureVk.cpp
+++ b/src/dawn_native/vulkan/TextureVk.cpp
@@ -15,6 +15,7 @@
 #include "dawn_native/vulkan/TextureVk.h"
 
 #include "dawn_native/vulkan/AdapterVk.h"
+#include "dawn_native/vulkan/CommandRecordingContext.h"
 #include "dawn_native/vulkan/DeviceVk.h"
 #include "dawn_native/vulkan/FencedDeleter.h"
 
@@ -436,7 +437,7 @@ namespace dawn_native { namespace vulkan {
             range.levelCount = GetNumMipLevels();
             range.baseArrayLayer = 0;
             range.layerCount = GetArrayLayers();
-            TransitionUsageNow(ToBackend(GetDevice())->GetPendingCommandBuffer(),
+            TransitionUsageNow(ToBackend(GetDevice())->GetPendingRecordingContext(),
                                dawn::TextureUsageBit::CopyDst);
 
             if (GetFormat().HasDepthOrStencil()) {
@@ -494,7 +495,8 @@ namespace dawn_native { namespace vulkan {
         return VulkanAspectMask(GetFormat());
     }
 
-    void Texture::TransitionUsageNow(VkCommandBuffer commands, dawn::TextureUsageBit usage) {
+    void Texture::TransitionUsageNow(CommandRecordingContext* recordingContext,
+                                     dawn::TextureUsageBit usage) {
         // Avoid encoding barriers when it isn't needed.
         bool lastReadOnly = (mLastUsage & kReadOnlyTextureUsages) == mLastUsage;
         if (lastReadOnly && mLastUsage == usage) {
@@ -525,13 +527,13 @@ namespace dawn_native { namespace vulkan {
         barrier.subresourceRange.layerCount = GetArrayLayers();
 
         ToBackend(GetDevice())
-            ->fn.CmdPipelineBarrier(commands, srcStages, dstStages, 0, 0, nullptr, 0, nullptr, 1,
-                                    &barrier);
+            ->fn.CmdPipelineBarrier(recordingContext->commandBuffer, srcStages, dstStages, 0, 0,
+                                    nullptr, 0, nullptr, 1, &barrier);
 
         mLastUsage = usage;
     }
 
-    void Texture::ClearTexture(VkCommandBuffer commands,
+    void Texture::ClearTexture(CommandRecordingContext* recordingContext,
                                uint32_t baseMipLevel,
                                uint32_t levelCount,
                                uint32_t baseArrayLayer,
@@ -543,13 +545,13 @@ namespace dawn_native { namespace vulkan {
         range.baseArrayLayer = baseArrayLayer;
         range.layerCount = layerCount;
 
-        TransitionUsageNow(commands, dawn::TextureUsageBit::CopyDst);
+        TransitionUsageNow(recordingContext, dawn::TextureUsageBit::CopyDst);
         if (GetFormat().HasDepthOrStencil()) {
             VkClearDepthStencilValue clear_color[1];
             clear_color[0].depth = 0.0f;
             clear_color[0].stencil = 0u;
             ToBackend(GetDevice())
-                ->fn.CmdClearDepthStencilImage(commands, GetHandle(),
+                ->fn.CmdClearDepthStencilImage(recordingContext->commandBuffer, GetHandle(),
                                                VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, clear_color, 1,
                                                &range);
         } else {
@@ -559,14 +561,15 @@ namespace dawn_native { namespace vulkan {
             clear_color[0].float32[2] = 0.0f;
             clear_color[0].float32[3] = 0.0f;
             ToBackend(GetDevice())
-                ->fn.CmdClearColorImage(commands, GetHandle(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
-                                        clear_color, 1, &range);
+                ->fn.CmdClearColorImage(recordingContext->commandBuffer, GetHandle(),
+                                        VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, clear_color, 1,
+                                        &range);
         }
         SetIsSubresourceContentInitialized(baseMipLevel, levelCount, baseArrayLayer, layerCount);
         GetDevice()->IncrementLazyClearCountForTesting();
     }
 
-    void Texture::EnsureSubresourceContentInitialized(VkCommandBuffer commands,
+    void Texture::EnsureSubresourceContentInitialized(CommandRecordingContext* recordingContext,
                                                       uint32_t baseMipLevel,
                                                       uint32_t levelCount,
                                                       uint32_t baseArrayLayer,
@@ -584,7 +587,7 @@ namespace dawn_native { namespace vulkan {
 
             // If subresource has not been initialized, clear it to black as it could contain dirty
             // bits from recycled memory
-            ClearTexture(commands, baseMipLevel, levelCount, baseArrayLayer, layerCount);
+            ClearTexture(recordingContext, baseMipLevel, levelCount, baseArrayLayer, layerCount);
         }
     }
 
diff --git a/src/dawn_native/vulkan/TextureVk.h b/src/dawn_native/vulkan/TextureVk.h
index 41cdf6d30a..9f716928c1 100644
--- a/src/dawn_native/vulkan/TextureVk.h
+++ b/src/dawn_native/vulkan/TextureVk.h
@@ -22,6 +22,8 @@
 
 namespace dawn_native { namespace vulkan {
 
+    struct CommandRecordingContext;
+
     VkFormat VulkanImageFormat(dawn::TextureFormat format);
     VkImageUsageFlags VulkanImageUsage(dawn::TextureUsageBit usage, const Format& format);
     VkSampleCountFlagBits VulkanSampleCount(uint32_t sampleCount);
@@ -38,8 +40,9 @@ namespace dawn_native { namespace vulkan {
         // Transitions the texture to be used as `usage`, recording any necessary barrier in
         // `commands`.
         // TODO(cwallez@chromium.org): coalesce barriers and do them early when possible.
-        void TransitionUsageNow(VkCommandBuffer commands, dawn::TextureUsageBit usage);
-        void EnsureSubresourceContentInitialized(VkCommandBuffer commands,
+        void TransitionUsageNow(CommandRecordingContext* recordingContext,
+                                dawn::TextureUsageBit usage);
+        void EnsureSubresourceContentInitialized(CommandRecordingContext* recordingContext,
                                                  uint32_t baseMipLevel,
                                                  uint32_t levelCount,
                                                  uint32_t baseArrayLayer,
@@ -47,7 +50,7 @@ namespace dawn_native { namespace vulkan {
 
       private:
         void DestroyImpl() override;
-        void ClearTexture(VkCommandBuffer commands,
+        void ClearTexture(CommandRecordingContext* recordingContext,
                           uint32_t baseMipLevel,
                           uint32_t levelCount,
                           uint32_t baseArrayLayer,