Add CommandRecordingContext to D3D12

This change refactors D3D12 backend to have CommandRecordingContext
CommandRecordingContext allows us to, in a future, add additional
data to the context such as textures that need to be acquired and
released before command lists are executed.

The Device's pending command list and the command list which resides
in the Queue object were converted to use CommandRecordingContext.

Bug=dawn:234

Change-Id: Ic13a229fc1f15895ef71117ce638c942de224743
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/11940
Commit-Queue: Rafael Cintron <rafael.cintron@microsoft.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/BUILD.gn b/BUILD.gn
index 6da0aff..3cfb2bd 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -254,6 +254,8 @@
       "src/dawn_native/d3d12/CommandAllocatorManager.h",
       "src/dawn_native/d3d12/CommandBufferD3D12.cpp",
       "src/dawn_native/d3d12/CommandBufferD3D12.h",
+      "src/dawn_native/d3d12/CommandRecordingContext.cpp",
+      "src/dawn_native/d3d12/CommandRecordingContext.h",
       "src/dawn_native/d3d12/CommittedResourceAllocatorD3D12.cpp",
       "src/dawn_native/d3d12/CommittedResourceAllocatorD3D12.h",
       "src/dawn_native/d3d12/ComputePipelineD3D12.cpp",
diff --git a/src/dawn_native/RefCounted.h b/src/dawn_native/RefCounted.h
index 5d21ced..89b0666 100644
--- a/src/dawn_native/RefCounted.h
+++ b/src/dawn_native/RefCounted.h
@@ -104,6 +104,12 @@
             return mPointee;
         }
 
+        T* Detach() {
+            T* pointee = mPointee;
+            mPointee = nullptr;
+            return pointee;
+        }
+
       private:
         void Reference() const {
             if (mPointee != nullptr) {
diff --git a/src/dawn_native/d3d12/BufferD3D12.cpp b/src/dawn_native/d3d12/BufferD3D12.cpp
index b8544bd..f2299f8 100644
--- a/src/dawn_native/d3d12/BufferD3D12.cpp
+++ b/src/dawn_native/d3d12/BufferD3D12.cpp
@@ -17,6 +17,7 @@
 #include "common/Assert.h"
 #include "common/Constants.h"
 #include "common/Math.h"
+#include "dawn_native/d3d12/CommandRecordingContext.h"
 #include "dawn_native/d3d12/DeviceD3D12.h"
 
 namespace dawn_native { namespace d3d12 {
@@ -130,7 +131,8 @@
     // When true is returned, a D3D12_RESOURCE_BARRIER has been created and must be used in a
     // ResourceBarrier call. Failing to do so will cause the tracked state to become invalid and can
     // cause subsequent errors.
-    bool Buffer::TransitionUsageAndGetResourceBarrier(D3D12_RESOURCE_BARRIER* barrier,
+    bool Buffer::TransitionUsageAndGetResourceBarrier(CommandRecordingContext* commandContext,
+                                                      D3D12_RESOURCE_BARRIER* barrier,
                                                       dawn::BufferUsage newUsage) {
         // Resources in upload and readback heaps must be kept in the COPY_SOURCE/DEST state
         if (mFixedResourceState) {
@@ -187,12 +189,12 @@
         return true;
     }
 
-    void Buffer::TransitionUsageNow(ComPtr<ID3D12GraphicsCommandList> commandList,
+    void Buffer::TransitionUsageNow(CommandRecordingContext* commandContext,
                                     dawn::BufferUsage usage) {
         D3D12_RESOURCE_BARRIER barrier;
 
-        if (TransitionUsageAndGetResourceBarrier(&barrier, usage)) {
-            commandList->ResourceBarrier(1, &barrier);
+        if (TransitionUsageAndGetResourceBarrier(commandContext, &barrier, usage)) {
+            commandContext->GetCommandList()->ResourceBarrier(1, &barrier);
         }
     }
 
diff --git a/src/dawn_native/d3d12/BufferD3D12.h b/src/dawn_native/d3d12/BufferD3D12.h
index f7ccd13..289c4ec 100644
--- a/src/dawn_native/d3d12/BufferD3D12.h
+++ b/src/dawn_native/d3d12/BufferD3D12.h
@@ -23,6 +23,7 @@
 
 namespace dawn_native { namespace d3d12 {
 
+    class CommandRecordingContext;
     class Device;
 
     class Buffer : public BufferBase {
@@ -36,10 +37,10 @@
         ComPtr<ID3D12Resource> GetD3D12Resource() const;
         D3D12_GPU_VIRTUAL_ADDRESS GetVA() const;
         void OnMapCommandSerialFinished(uint32_t mapSerial, void* data, bool isWrite);
-        bool TransitionUsageAndGetResourceBarrier(D3D12_RESOURCE_BARRIER* barrier,
+        bool TransitionUsageAndGetResourceBarrier(CommandRecordingContext* commandContext,
+                                                  D3D12_RESOURCE_BARRIER* barrier,
                                                   dawn::BufferUsage newUsage);
-        void TransitionUsageNow(ComPtr<ID3D12GraphicsCommandList> commandList,
-                                dawn::BufferUsage usage);
+        void TransitionUsageNow(CommandRecordingContext* commandContext, dawn::BufferUsage usage);
 
       private:
         // Dawn API
diff --git a/src/dawn_native/d3d12/CommandBufferD3D12.cpp b/src/dawn_native/d3d12/CommandBufferD3D12.cpp
index df928bb..d095395 100644
--- a/src/dawn_native/d3d12/CommandBufferD3D12.cpp
+++ b/src/dawn_native/d3d12/CommandBufferD3D12.cpp
@@ -22,6 +22,7 @@
 #include "dawn_native/d3d12/BindGroupD3D12.h"
 #include "dawn_native/d3d12/BindGroupLayoutD3D12.h"
 #include "dawn_native/d3d12/BufferD3D12.h"
+#include "dawn_native/d3d12/CommandRecordingContext.h"
 #include "dawn_native/d3d12/ComputePipelineD3D12.h"
 #include "dawn_native/d3d12/DescriptorHeapAllocator.h"
 #include "dawn_native/d3d12/DeviceD3D12.h"
@@ -502,7 +503,7 @@
             return {};
         }
 
-        void ResolveMultisampledRenderPass(ComPtr<ID3D12GraphicsCommandList> commandList,
+        void ResolveMultisampledRenderPass(CommandRecordingContext* commandContext,
                                            BeginRenderPassCmd* renderPass) {
             ASSERT(renderPass != nullptr);
 
@@ -519,8 +520,10 @@
                 Texture* resolveTexture = ToBackend(resolveTarget->GetTexture());
 
                 // Transition the usages of the color attachment and resolve target.
-                colorTexture->TransitionUsageNow(commandList, D3D12_RESOURCE_STATE_RESOLVE_SOURCE);
-                resolveTexture->TransitionUsageNow(commandList, D3D12_RESOURCE_STATE_RESOLVE_DEST);
+                colorTexture->TransitionUsageNow(commandContext,
+                                                 D3D12_RESOURCE_STATE_RESOLVE_SOURCE);
+                resolveTexture->TransitionUsageNow(commandContext,
+                                                   D3D12_RESOURCE_STATE_RESOLVE_DEST);
 
                 // Do MSAA resolve with ResolveSubResource().
                 ID3D12Resource* colorTextureHandle = colorTexture->GetD3D12Resource();
@@ -528,7 +531,7 @@
                 const uint32_t resolveTextureSubresourceIndex = resolveTexture->GetSubresourceIndex(
                     resolveTarget->GetBaseMipLevel(), resolveTarget->GetBaseArrayLayer());
                 constexpr uint32_t kColorTextureSubresourceIndex = 0;
-                commandList->ResolveSubresource(
+                commandContext->GetCommandList()->ResolveSubresource(
                     resolveTextureHandle, resolveTextureSubresourceIndex, colorTextureHandle,
                     kColorTextureSubresourceIndex, colorTexture->GetD3D12Format());
             }
@@ -545,12 +548,14 @@
         FreeCommands(&mCommands);
     }
 
-    MaybeError CommandBuffer::RecordCommands(ComPtr<ID3D12GraphicsCommandList> commandList,
+    MaybeError CommandBuffer::RecordCommands(CommandRecordingContext* commandContext,
                                              uint32_t indexInSubmit) {
         Device* device = ToBackend(GetDevice());
         BindGroupStateTracker bindingTracker(device);
         RenderPassDescriptorHeapTracker renderPassTracker(device);
 
+        ID3D12GraphicsCommandList* commandList = commandContext->GetCommandList();
+
         // 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
@@ -563,14 +568,17 @@
         }
 
         // Records the necessary barriers for the resource usage pre-computed by the frontend
-        auto TransitionForPass = [](ComPtr<ID3D12GraphicsCommandList> commandList,
+        auto TransitionForPass = [](CommandRecordingContext* commandContext,
                                     const PassResourceUsage& usages) {
             std::vector<D3D12_RESOURCE_BARRIER> barriers;
 
+            ID3D12GraphicsCommandList* commandList = commandContext->GetCommandList();
+
             for (size_t i = 0; i < usages.buffers.size(); ++i) {
                 D3D12_RESOURCE_BARRIER barrier;
                 if (ToBackend(usages.buffers[i])
-                        ->TransitionUsageAndGetResourceBarrier(&barrier, usages.bufferUsages[i])) {
+                        ->TransitionUsageAndGetResourceBarrier(commandContext, &barrier,
+                                                               usages.bufferUsages[i])) {
                     barriers.push_back(barrier);
                 }
             }
@@ -581,15 +589,17 @@
                 // cleared during record render pass if the texture subresource has not been
                 // initialized before the render pass.
                 if (!(usages.textureUsages[i] & dawn::TextureUsage::OutputAttachment)) {
-                    texture->EnsureSubresourceContentInitialized(
-                        commandList, 0, texture->GetNumMipLevels(), 0, texture->GetArrayLayers());
+                    texture->EnsureSubresourceContentInitialized(commandContext, 0,
+                                                                 texture->GetNumMipLevels(), 0,
+                                                                 texture->GetArrayLayers());
                 }
             }
 
             for (size_t i = 0; i < usages.textures.size(); ++i) {
                 D3D12_RESOURCE_BARRIER barrier;
                 if (ToBackend(usages.textures[i])
-                        ->TransitionUsageAndGetResourceBarrier(&barrier, usages.textureUsages[i])) {
+                        ->TransitionUsageAndGetResourceBarrier(commandContext, &barrier,
+                                                               usages.textureUsages[i])) {
                     barriers.push_back(barrier);
                 }
             }
@@ -608,7 +618,7 @@
                 case Command::BeginComputePass: {
                     mCommands.NextCommand<BeginComputePassCmd>();
 
-                    TransitionForPass(commandList, passResourceUsages[nextPassNumber]);
+                    TransitionForPass(commandContext, passResourceUsages[nextPassNumber]);
                     bindingTracker.SetInComputePass(true);
                     RecordComputePass(commandList, &bindingTracker);
 
@@ -619,9 +629,9 @@
                     BeginRenderPassCmd* beginRenderPassCmd =
                         mCommands.NextCommand<BeginRenderPassCmd>();
 
-                    TransitionForPass(commandList, passResourceUsages[nextPassNumber]);
+                    TransitionForPass(commandContext, passResourceUsages[nextPassNumber]);
                     bindingTracker.SetInComputePass(false);
-                    RecordRenderPass(commandList, &bindingTracker, &renderPassTracker,
+                    RecordRenderPass(commandContext, &bindingTracker, &renderPassTracker,
                                      beginRenderPassCmd);
 
                     nextPassNumber++;
@@ -632,8 +642,8 @@
                     Buffer* srcBuffer = ToBackend(copy->source.Get());
                     Buffer* dstBuffer = ToBackend(copy->destination.Get());
 
-                    srcBuffer->TransitionUsageNow(commandList, dawn::BufferUsage::CopySrc);
-                    dstBuffer->TransitionUsageNow(commandList, dawn::BufferUsage::CopyDst);
+                    srcBuffer->TransitionUsageNow(commandContext, dawn::BufferUsage::CopySrc);
+                    dstBuffer->TransitionUsageNow(commandContext, dawn::BufferUsage::CopyDst);
 
                     commandList->CopyBufferRegion(
                         dstBuffer->GetD3D12Resource().Get(), copy->destinationOffset,
@@ -651,12 +661,12 @@
                             true, copy->destination.mipLevel, 1, copy->destination.arrayLayer, 1);
                     } else {
                         texture->EnsureSubresourceContentInitialized(
-                            commandList, copy->destination.mipLevel, 1,
+                            commandContext, copy->destination.mipLevel, 1,
                             copy->destination.arrayLayer, 1);
                     }
 
-                    buffer->TransitionUsageNow(commandList, dawn::BufferUsage::CopySrc);
-                    texture->TransitionUsageNow(commandList, dawn::TextureUsage::CopyDst);
+                    buffer->TransitionUsageNow(commandContext, dawn::BufferUsage::CopySrc);
+                    texture->TransitionUsageNow(commandContext, dawn::TextureUsage::CopyDst);
 
                     auto copySplit = ComputeTextureCopySplit(
                         copy->destination.origin, copy->copySize, texture->GetFormat(),
@@ -687,11 +697,11 @@
                     Texture* texture = ToBackend(copy->source.texture.Get());
                     Buffer* buffer = ToBackend(copy->destination.buffer.Get());
 
-                    texture->EnsureSubresourceContentInitialized(commandList, copy->source.mipLevel,
-                                                                 1, copy->source.arrayLayer, 1);
+                    texture->EnsureSubresourceContentInitialized(
+                        commandContext, copy->source.mipLevel, 1, copy->source.arrayLayer, 1);
 
-                    texture->TransitionUsageNow(commandList, dawn::TextureUsage::CopySrc);
-                    buffer->TransitionUsageNow(commandList, dawn::BufferUsage::CopyDst);
+                    texture->TransitionUsageNow(commandContext, dawn::TextureUsage::CopySrc);
+                    buffer->TransitionUsageNow(commandContext, dawn::BufferUsage::CopyDst);
 
                     TextureCopySplit copySplit = ComputeTextureCopySplit(
                         copy->source.origin, copy->copySize, texture->GetFormat(),
@@ -726,19 +736,19 @@
                     Texture* source = ToBackend(copy->source.texture.Get());
                     Texture* destination = ToBackend(copy->destination.texture.Get());
 
-                    source->EnsureSubresourceContentInitialized(commandList, copy->source.mipLevel,
-                                                                1, copy->source.arrayLayer, 1);
+                    source->EnsureSubresourceContentInitialized(
+                        commandContext, copy->source.mipLevel, 1, copy->source.arrayLayer, 1);
                     if (IsCompleteSubresourceCopiedTo(destination, copy->copySize,
                                                       copy->destination.mipLevel)) {
                         destination->SetIsSubresourceContentInitialized(
                             true, copy->destination.mipLevel, 1, copy->destination.arrayLayer, 1);
                     } else {
                         destination->EnsureSubresourceContentInitialized(
-                            commandList, copy->destination.mipLevel, 1,
+                            commandContext, copy->destination.mipLevel, 1,
                             copy->destination.arrayLayer, 1);
                     }
-                    source->TransitionUsageNow(commandList, dawn::TextureUsage::CopySrc);
-                    destination->TransitionUsageNow(commandList, dawn::TextureUsage::CopyDst);
+                    source->TransitionUsageNow(commandContext, dawn::TextureUsage::CopySrc);
+                    destination->TransitionUsageNow(commandContext, dawn::TextureUsage::CopyDst);
 
                     if (CanUseCopyResource(source->GetNumMipLevels(), source->GetSize(),
                                            destination->GetSize(), copy->copySize)) {
@@ -771,7 +781,7 @@
         return {};
     }
 
-    void CommandBuffer::RecordComputePass(ComPtr<ID3D12GraphicsCommandList> commandList,
+    void CommandBuffer::RecordComputePass(ID3D12GraphicsCommandList* commandList,
                                           BindGroupStateTracker* bindingTracker) {
         PipelineLayout* lastLayout = nullptr;
 
@@ -781,14 +791,14 @@
                 case Command::Dispatch: {
                     DispatchCmd* dispatch = mCommands.NextCommand<DispatchCmd>();
 
-                    bindingTracker->Apply(commandList.Get());
+                    bindingTracker->Apply(commandList);
                     commandList->Dispatch(dispatch->x, dispatch->y, dispatch->z);
                 } break;
 
                 case Command::DispatchIndirect: {
                     DispatchIndirectCmd* dispatch = mCommands.NextCommand<DispatchIndirectCmd>();
 
-                    bindingTracker->Apply(commandList.Get());
+                    bindingTracker->Apply(commandList);
                     Buffer* buffer = ToBackend(dispatch->indirectBuffer.Get());
                     ComPtr<ID3D12CommandSignature> signature =
                         ToBackend(GetDevice())->GetDispatchIndirectSignature();
@@ -837,7 +847,7 @@
                         constexpr uint64_t kPIXBlackColor = 0xff000000;
                         ToBackend(GetDevice())
                             ->GetFunctions()
-                            ->pixSetMarkerOnCommandList(commandList.Get(), kPIXBlackColor, label);
+                            ->pixSetMarkerOnCommandList(commandList, kPIXBlackColor, label);
                     }
                 } break;
 
@@ -847,7 +857,7 @@
                     if (ToBackend(GetDevice())->GetFunctions()->IsPIXEventRuntimeLoaded()) {
                         ToBackend(GetDevice())
                             ->GetFunctions()
-                            ->pixEndEventOnCommandList(commandList.Get());
+                            ->pixEndEventOnCommandList(commandList);
                     }
                 } break;
 
@@ -860,7 +870,7 @@
                         constexpr uint64_t kPIXBlackColor = 0xff000000;
                         ToBackend(GetDevice())
                             ->GetFunctions()
-                            ->pixBeginEventOnCommandList(commandList.Get(), kPIXBlackColor, label);
+                            ->pixBeginEventOnCommandList(commandList, kPIXBlackColor, label);
                     }
                 } break;
 
@@ -869,11 +879,12 @@
         }
     }
 
-    void CommandBuffer::RecordRenderPass(ComPtr<ID3D12GraphicsCommandList> commandList,
+    void CommandBuffer::RecordRenderPass(CommandRecordingContext* commandContext,
                                          BindGroupStateTracker* bindingTracker,
                                          RenderPassDescriptorHeapTracker* renderPassTracker,
                                          BeginRenderPassCmd* renderPass) {
         OMSetRenderTargetArgs args = renderPassTracker->GetSubpassOMSetRenderTargetArgs(renderPass);
+        ID3D12GraphicsCommandList* commandList = commandContext->GetCommandList();
 
         // Clear framebuffer attachments as needed and transition to render target
         {
@@ -1012,8 +1023,8 @@
                 case Command::Draw: {
                     DrawCmd* draw = iter->NextCommand<DrawCmd>();
 
-                    bindingTracker->Apply(commandList.Get());
-                    vertexBufferTracker.Apply(commandList.Get(), lastPipeline);
+                    bindingTracker->Apply(commandList);
+                    vertexBufferTracker.Apply(commandList, lastPipeline);
                     commandList->DrawInstanced(draw->vertexCount, draw->instanceCount,
                                                draw->firstVertex, draw->firstInstance);
                 } break;
@@ -1021,9 +1032,9 @@
                 case Command::DrawIndexed: {
                     DrawIndexedCmd* draw = iter->NextCommand<DrawIndexedCmd>();
 
-                    bindingTracker->Apply(commandList.Get());
-                    indexBufferTracker.Apply(commandList.Get());
-                    vertexBufferTracker.Apply(commandList.Get(), lastPipeline);
+                    bindingTracker->Apply(commandList);
+                    indexBufferTracker.Apply(commandList);
+                    vertexBufferTracker.Apply(commandList, lastPipeline);
                     commandList->DrawIndexedInstanced(draw->indexCount, draw->instanceCount,
                                                       draw->firstIndex, draw->baseVertex,
                                                       draw->firstInstance);
@@ -1032,8 +1043,8 @@
                 case Command::DrawIndirect: {
                     DrawIndirectCmd* draw = iter->NextCommand<DrawIndirectCmd>();
 
-                    bindingTracker->Apply(commandList.Get());
-                    vertexBufferTracker.Apply(commandList.Get(), lastPipeline);
+                    bindingTracker->Apply(commandList);
+                    vertexBufferTracker.Apply(commandList, lastPipeline);
                     Buffer* buffer = ToBackend(draw->indirectBuffer.Get());
                     ComPtr<ID3D12CommandSignature> signature =
                         ToBackend(GetDevice())->GetDrawIndirectSignature();
@@ -1045,9 +1056,9 @@
                 case Command::DrawIndexedIndirect: {
                     DrawIndexedIndirectCmd* draw = iter->NextCommand<DrawIndexedIndirectCmd>();
 
-                    bindingTracker->Apply(commandList.Get());
-                    indexBufferTracker.Apply(commandList.Get());
-                    vertexBufferTracker.Apply(commandList.Get(), lastPipeline);
+                    bindingTracker->Apply(commandList);
+                    indexBufferTracker.Apply(commandList);
+                    vertexBufferTracker.Apply(commandList, lastPipeline);
                     Buffer* buffer = ToBackend(draw->indirectBuffer.Get());
                     ComPtr<ID3D12CommandSignature> signature =
                         ToBackend(GetDevice())->GetDrawIndexedIndirectSignature();
@@ -1065,7 +1076,7 @@
                         constexpr uint64_t kPIXBlackColor = 0xff000000;
                         ToBackend(GetDevice())
                             ->GetFunctions()
-                            ->pixSetMarkerOnCommandList(commandList.Get(), kPIXBlackColor, label);
+                            ->pixSetMarkerOnCommandList(commandList, kPIXBlackColor, label);
                     }
                 } break;
 
@@ -1075,7 +1086,7 @@
                     if (ToBackend(GetDevice())->GetFunctions()->IsPIXEventRuntimeLoaded()) {
                         ToBackend(GetDevice())
                             ->GetFunctions()
-                            ->pixEndEventOnCommandList(commandList.Get());
+                            ->pixEndEventOnCommandList(commandList);
                     }
                 } break;
 
@@ -1088,7 +1099,7 @@
                         constexpr uint64_t kPIXBlackColor = 0xff000000;
                         ToBackend(GetDevice())
                             ->GetFunctions()
-                            ->pixBeginEventOnCommandList(commandList.Get(), kPIXBlackColor, label);
+                            ->pixBeginEventOnCommandList(commandList, kPIXBlackColor, label);
                     }
                 } break;
 
@@ -1149,7 +1160,7 @@
                     // TODO(brandon1.jones@intel.com): avoid calling this function and enable MSAA
                     // resolve in D3D12 render pass on the platforms that support this feature.
                     if (renderPass->attachmentState->GetSampleCount() > 1) {
-                        ResolveMultisampledRenderPass(commandList, renderPass);
+                        ResolveMultisampledRenderPass(commandContext, renderPass);
                     }
                     return;
                 } break;
diff --git a/src/dawn_native/d3d12/CommandBufferD3D12.h b/src/dawn_native/d3d12/CommandBufferD3D12.h
index 64a9a86..ce7f451 100644
--- a/src/dawn_native/d3d12/CommandBufferD3D12.h
+++ b/src/dawn_native/d3d12/CommandBufferD3D12.h
@@ -32,6 +32,7 @@
 namespace dawn_native { namespace d3d12 {
 
     class BindGroupStateTracker;
+    class CommandRecordingContext;
     class Device;
     class RenderPassDescriptorHeapTracker;
     class RenderPipeline;
@@ -41,13 +42,12 @@
         CommandBuffer(CommandEncoderBase* encoder, const CommandBufferDescriptor* descriptor);
         ~CommandBuffer();
 
-        MaybeError RecordCommands(ComPtr<ID3D12GraphicsCommandList> commandList,
-                                  uint32_t indexInSubmit);
+        MaybeError RecordCommands(CommandRecordingContext* commandContext, uint32_t indexInSubmit);
 
       private:
-        void RecordComputePass(ComPtr<ID3D12GraphicsCommandList> commandList,
+        void RecordComputePass(ID3D12GraphicsCommandList* commandList,
                                BindGroupStateTracker* bindingTracker);
-        void RecordRenderPass(ComPtr<ID3D12GraphicsCommandList> commandList,
+        void RecordRenderPass(CommandRecordingContext* commandContext,
                               BindGroupStateTracker* bindingTracker,
                               RenderPassDescriptorHeapTracker* renderPassTracker,
                               BeginRenderPassCmd* renderPass);
diff --git a/src/dawn_native/d3d12/CommandRecordingContext.cpp b/src/dawn_native/d3d12/CommandRecordingContext.cpp
new file mode 100644
index 0000000..eb4ec7b
--- /dev/null
+++ b/src/dawn_native/d3d12/CommandRecordingContext.cpp
@@ -0,0 +1,72 @@
+// 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 "dawn_native/d3d12/CommandRecordingContext.h"
+#include "dawn_native/d3d12/CommandAllocatorManager.h"
+
+namespace dawn_native { namespace d3d12 {
+
+    MaybeError CommandRecordingContext::Open(ID3D12Device* d3d12Device,
+                                             CommandAllocatorManager* commandAllocationManager) {
+        ASSERT(!IsOpen());
+        if (mD3d12CommandList != nullptr) {
+            const HRESULT hr = mD3d12CommandList->Reset(
+                commandAllocationManager->ReserveCommandAllocator().Get(), nullptr);
+            if (FAILED(hr)) {
+                mD3d12CommandList.Reset();
+                return DAWN_DEVICE_LOST_ERROR("Error resetting command list.");
+            }
+        } else {
+            ComPtr<ID3D12GraphicsCommandList> d3d12GraphicsCommandList;
+            const HRESULT hr = d3d12Device->CreateCommandList(
+                0, D3D12_COMMAND_LIST_TYPE_DIRECT,
+                commandAllocationManager->ReserveCommandAllocator().Get(), nullptr,
+                IID_PPV_ARGS(&d3d12GraphicsCommandList));
+            if (FAILED(hr)) {
+                return DAWN_DEVICE_LOST_ERROR("Error creating a direct command list.");
+            }
+            mD3d12CommandList = std::move(d3d12GraphicsCommandList);
+        }
+
+        mIsOpen = true;
+
+        return {};
+    }
+
+    ResultOrError<ID3D12GraphicsCommandList*> CommandRecordingContext::Close() {
+        ASSERT(IsOpen());
+        mIsOpen = false;
+        const HRESULT hr = mD3d12CommandList->Close();
+        if (FAILED(hr)) {
+            mD3d12CommandList.Reset();
+            return DAWN_DEVICE_LOST_ERROR("Error closing pending command list.");
+        }
+        return mD3d12CommandList.Get();
+    }
+
+    ID3D12GraphicsCommandList* CommandRecordingContext::GetCommandList() const {
+        ASSERT(mD3d12CommandList != nullptr);
+        ASSERT(IsOpen());
+        return mD3d12CommandList.Get();
+    }
+
+    void CommandRecordingContext::Release() {
+        mD3d12CommandList.Reset();
+        mIsOpen = false;
+    }
+
+    bool CommandRecordingContext::IsOpen() const {
+        return mIsOpen;
+    }
+
+}}  // namespace dawn_native::d3d12
diff --git a/src/dawn_native/d3d12/CommandRecordingContext.h b/src/dawn_native/d3d12/CommandRecordingContext.h
new file mode 100644
index 0000000..544dae9
--- /dev/null
+++ b/src/dawn_native/d3d12/CommandRecordingContext.h
@@ -0,0 +1,38 @@
+// 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_D3D12_COMMANDRECORDINGCONTEXT_H_
+#define DAWNNATIVE_D3D12_COMMANDRECORDINGCONTEXT_H_
+
+#include "dawn_native/Error.h"
+#include "dawn_native/d3d12/d3d12_platform.h"
+
+namespace dawn_native { namespace d3d12 {
+    class CommandAllocatorManager;
+
+    class CommandRecordingContext {
+      public:
+        MaybeError Open(ID3D12Device* d3d12Device,
+                        CommandAllocatorManager* commandAllocationManager);
+        ResultOrError<ID3D12GraphicsCommandList*> Close();
+        ID3D12GraphicsCommandList* GetCommandList() const;
+        void Release();
+        bool IsOpen() const;
+
+      private:
+        ComPtr<ID3D12GraphicsCommandList> mD3d12CommandList;
+        bool mIsOpen = false;
+    };
+}}  // namespace dawn_native::d3d12
+
+#endif  // DAWNNATIVE_D3D12_COMMANDRECORDINGCONTEXT_H_
diff --git a/src/dawn_native/d3d12/DeviceD3D12.cpp b/src/dawn_native/d3d12/DeviceD3D12.cpp
index b3c4c97..914100f 100644
--- a/src/dawn_native/d3d12/DeviceD3D12.cpp
+++ b/src/dawn_native/d3d12/DeviceD3D12.cpp
@@ -101,11 +101,8 @@
 
     Device::~Device() {
         // Immediately forget about all pending commands
-        if (mPendingCommands.open) {
-            mPendingCommands.commandList->Close();
-            mPendingCommands.open = false;
-            mPendingCommands.commandList = nullptr;
-        }
+        mPendingCommands.Release();
+
         NextSerial();
         WaitForSerial(mLastSubmittedSerial);  // Wait for all in-flight commands to finish executing
 
@@ -133,7 +130,7 @@
         mUsedComObjectRefs.ClearUpTo(mCompletedSerial);
 
         ASSERT(mUsedComObjectRefs.Empty());
-        ASSERT(mPendingCommands.commandList == nullptr);
+        ASSERT(!mPendingCommands.IsOpen());
     }
 
     ComPtr<ID3D12Device> Device::GetD3D12Device() const {
@@ -176,27 +173,17 @@
         return mResourceAllocator.get();
     }
 
-    void Device::OpenCommandList(ComPtr<ID3D12GraphicsCommandList>* commandList) {
-        ComPtr<ID3D12GraphicsCommandList>& cmdList = *commandList;
-        if (!cmdList) {
-            ASSERT_SUCCESS(mD3d12Device->CreateCommandList(
-                0, D3D12_COMMAND_LIST_TYPE_DIRECT,
-                mCommandAllocatorManager->ReserveCommandAllocator().Get(), nullptr,
-                IID_PPV_ARGS(&cmdList)));
-        } else {
-            ASSERT_SUCCESS(
-                cmdList->Reset(mCommandAllocatorManager->ReserveCommandAllocator().Get(), nullptr));
-        }
+    CommandAllocatorManager* Device::GetCommandAllocatorManager() const {
+        return mCommandAllocatorManager.get();
     }
 
-    ComPtr<ID3D12GraphicsCommandList> Device::GetPendingCommandList() {
+    ResultOrError<CommandRecordingContext*> Device::GetPendingCommandContext() {
         // Callers of GetPendingCommandList do so to record commands. Only reserve a command
         // allocator when it is needed so we don't submit empty command lists
-        if (!mPendingCommands.open) {
-            OpenCommandList(&mPendingCommands.commandList);
-            mPendingCommands.open = true;
+        if (!mPendingCommands.IsOpen()) {
+            DAWN_TRY(mPendingCommands.Open(mD3d12Device.Get(), mCommandAllocatorManager.get()));
         }
-        return mPendingCommands.commandList;
+        return &mPendingCommands;
     }
 
     Serial Device::GetCompletedCommandSerial() const {
@@ -224,7 +211,7 @@
         mDescriptorHeapAllocator->Deallocate(mCompletedSerial);
         mMapRequestTracker->Tick(mCompletedSerial);
         mUsedComObjectRefs.ClearUpTo(mCompletedSerial);
-        DAWN_TRY(ExecuteCommandList(nullptr));
+        DAWN_TRY(ExecuteCommandContext(nullptr));
         NextSerial();
 
         return {};
@@ -247,27 +234,23 @@
         mUsedComObjectRefs.Enqueue(object, GetPendingCommandSerial());
     }
 
-    MaybeError Device::ExecuteCommandList(ID3D12CommandList* d3d12CommandList) {
+    MaybeError Device::ExecuteCommandContext(CommandRecordingContext* commandContext) {
         UINT numLists = 0;
         std::array<ID3D12CommandList*, 2> d3d12CommandLists;
 
         // If there are pending commands, prepend them to ExecuteCommandLists
-        if (mPendingCommands.open) {
-            const HRESULT hr = mPendingCommands.commandList->Close();
-            if (FAILED(hr)) {
-                mPendingCommands.open = false;
-                mPendingCommands.commandList.Reset();
-                return DAWN_DEVICE_LOST_ERROR("Error closing pending command list.");
-            }
-            mPendingCommands.open = false;
-            d3d12CommandLists[numLists++] = mPendingCommands.commandList.Get();
+        if (mPendingCommands.IsOpen()) {
+            ID3D12GraphicsCommandList* d3d12CommandList;
+            DAWN_TRY_ASSIGN(d3d12CommandList, mPendingCommands.Close());
+            d3d12CommandLists[numLists++] = d3d12CommandList;
         }
-        if (d3d12CommandList != nullptr) {
+        if (commandContext != nullptr) {
+            ID3D12GraphicsCommandList* d3d12CommandList;
+            DAWN_TRY_ASSIGN(d3d12CommandList, commandContext->Close());
             d3d12CommandLists[numLists++] = d3d12CommandList;
         }
         if (numLists > 0) {
             mCommandQueue->ExecuteCommandLists(numLists, d3d12CommandLists.data());
-            mPendingCommands.commandList.Reset();
         }
 
         return {};
@@ -317,7 +300,7 @@
         return new SwapChain(this, descriptor);
     }
     ResultOrError<TextureBase*> Device::CreateTextureImpl(const TextureDescriptor* descriptor) {
-        return new Texture(this, descriptor);
+        return Texture::Create(this, descriptor);
     }
     ResultOrError<TextureViewBase*> Device::CreateTextureViewImpl(
         TextureBase* texture,
@@ -337,10 +320,13 @@
                                                BufferBase* destination,
                                                uint64_t destinationOffset,
                                                uint64_t size) {
-        ToBackend(destination)
-            ->TransitionUsageNow(GetPendingCommandList(), dawn::BufferUsage::CopyDst);
+        CommandRecordingContext* commandRecordingContext;
+        DAWN_TRY_ASSIGN(commandRecordingContext, GetPendingCommandContext());
 
-        GetPendingCommandList()->CopyBufferRegion(
+        ToBackend(destination)
+            ->TransitionUsageNow(commandRecordingContext, dawn::BufferUsage::CopyDst);
+
+        commandRecordingContext->GetCommandList()->CopyBufferRegion(
             ToBackend(destination)->GetD3D12Resource().Get(), destinationOffset,
             ToBackend(source)->GetResource(), sourceOffset, size);
 
@@ -381,6 +367,6 @@
             return nullptr;
         }
 
-        return new Texture(this, descriptor, d3d12Resource.Get());
+        return new Texture(this, descriptor, std::move(d3d12Resource));
     }
 }}  // namespace dawn_native::d3d12
diff --git a/src/dawn_native/d3d12/DeviceD3D12.h b/src/dawn_native/d3d12/DeviceD3D12.h
index eb57543..c499eeb 100644
--- a/src/dawn_native/d3d12/DeviceD3D12.h
+++ b/src/dawn_native/d3d12/DeviceD3D12.h
@@ -19,9 +19,9 @@
 
 #include "common/SerialQueue.h"
 #include "dawn_native/Device.h"
+#include "dawn_native/d3d12/CommandRecordingContext.h"
 #include "dawn_native/d3d12/Forward.h"
 #include "dawn_native/d3d12/ResourceHeapAllocationD3D12.h"
-#include "dawn_native/d3d12/d3d12_platform.h"
 
 #include <memory>
 
@@ -65,12 +65,12 @@
         DescriptorHeapAllocator* GetDescriptorHeapAllocator() const;
         MapRequestTracker* GetMapRequestTracker() const;
         ResourceAllocator* GetResourceAllocator() const;
+        CommandAllocatorManager* GetCommandAllocatorManager() const;
 
         const PlatformFunctions* GetFunctions() const;
         ComPtr<IDXGIFactory4> GetFactory() const;
 
-        void OpenCommandList(ComPtr<ID3D12GraphicsCommandList>* commandList);
-        ComPtr<ID3D12GraphicsCommandList> GetPendingCommandList();
+        ResultOrError<CommandRecordingContext*> GetPendingCommandContext();
         Serial GetPendingCommandSerial() const override;
 
         void NextSerial();
@@ -78,7 +78,7 @@
 
         void ReferenceUntilUnused(ComPtr<IUnknown> object);
 
-        MaybeError ExecuteCommandList(ID3D12CommandList* d3d12CommandList);
+        MaybeError ExecuteCommandContext(CommandRecordingContext* commandContext);
 
         ResultOrError<std::unique_ptr<StagingBufferBase>> CreateStagingBuffer(size_t size) override;
         MaybeError CopyFromStagingToBuffer(StagingBufferBase* source,
@@ -132,10 +132,7 @@
         ComPtr<ID3D12CommandSignature> mDrawIndirectSignature;
         ComPtr<ID3D12CommandSignature> mDrawIndexedIndirectSignature;
 
-        struct PendingCommandList {
-            ComPtr<ID3D12GraphicsCommandList> commandList;
-            bool open = false;
-        } mPendingCommands;
+        CommandRecordingContext mPendingCommands;
 
         SerialQueue<ComPtr<IUnknown>> mUsedComObjectRefs;
 
diff --git a/src/dawn_native/d3d12/QueueD3D12.cpp b/src/dawn_native/d3d12/QueueD3D12.cpp
index acd24b8..ccda50c 100644
--- a/src/dawn_native/d3d12/QueueD3D12.cpp
+++ b/src/dawn_native/d3d12/QueueD3D12.cpp
@@ -27,13 +27,13 @@
 
         device->Tick();
 
-        device->OpenCommandList(&mCommandList);
+        DAWN_TRY(mCommandContext.Open(device->GetD3D12Device().Get(),
+                                      device->GetCommandAllocatorManager()));
         for (uint32_t i = 0; i < commandCount; ++i) {
-            DAWN_TRY(ToBackend(commands[i])->RecordCommands(mCommandList, i));
+            DAWN_TRY(ToBackend(commands[i])->RecordCommands(&mCommandContext, i));
         }
-        ASSERT_SUCCESS(mCommandList->Close());
 
-        DAWN_TRY(device->ExecuteCommandList(mCommandList.Get()));
+        DAWN_TRY(device->ExecuteCommandContext(&mCommandContext));
 
         device->NextSerial();
         return {};
diff --git a/src/dawn_native/d3d12/QueueD3D12.h b/src/dawn_native/d3d12/QueueD3D12.h
index 9a11f21..121d19c 100644
--- a/src/dawn_native/d3d12/QueueD3D12.h
+++ b/src/dawn_native/d3d12/QueueD3D12.h
@@ -17,6 +17,7 @@
 
 #include "dawn_native/Queue.h"
 
+#include "dawn_native/d3d12/CommandRecordingContext.h"
 #include "dawn_native/d3d12/d3d12_platform.h"
 
 namespace dawn_native { namespace d3d12 {
@@ -31,7 +32,7 @@
       private:
         MaybeError SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) override;
 
-        ComPtr<ID3D12GraphicsCommandList> mCommandList;
+        CommandRecordingContext mCommandContext;
     };
 
 }}  // namespace dawn_native::d3d12
diff --git a/src/dawn_native/d3d12/SwapChainD3D12.cpp b/src/dawn_native/d3d12/SwapChainD3D12.cpp
index 49cd6d5..7d24b35 100644
--- a/src/dawn_native/d3d12/SwapChainD3D12.cpp
+++ b/src/dawn_native/d3d12/SwapChainD3D12.cpp
@@ -51,10 +51,13 @@
     MaybeError SwapChain::OnBeforePresent(TextureBase* texture) {
         Device* device = ToBackend(GetDevice());
 
-        // Perform the necessary transition for the texture to be presented.
-        ToBackend(texture)->TransitionUsageNow(device->GetPendingCommandList(), mTextureUsage);
+        CommandRecordingContext* commandContext;
+        DAWN_TRY_ASSIGN(commandContext, device->GetPendingCommandContext());
 
-        DAWN_TRY(device->ExecuteCommandList(nullptr));
+        // Perform the necessary transition for the texture to be presented.
+        ToBackend(texture)->TransitionUsageNow(commandContext, mTextureUsage);
+
+        DAWN_TRY(device->ExecuteCommandContext(nullptr));
 
         return {};
     }
diff --git a/src/dawn_native/d3d12/TextureD3D12.cpp b/src/dawn_native/d3d12/TextureD3D12.cpp
index 35ab67c..3c38511 100644
--- a/src/dawn_native/d3d12/TextureD3D12.cpp
+++ b/src/dawn_native/d3d12/TextureD3D12.cpp
@@ -270,8 +270,18 @@
         return {};
     }
 
+    ResultOrError<TextureBase*> Texture::Create(Device* device,
+                                                const TextureDescriptor* descriptor) {
+        Ref<Texture> dawnTexture = AcquireRef(new Texture(device, descriptor));
+        DAWN_TRY(dawnTexture->InitializeAsInternalTexture());
+        return dawnTexture.Detach();
+    }
+
     Texture::Texture(Device* device, const TextureDescriptor* descriptor)
         : TextureBase(device, descriptor, TextureState::OwnedInternal) {
+    }
+
+    MaybeError Texture::InitializeAsInternalTexture() {
         D3D12_RESOURCE_DESC resourceDescriptor;
         resourceDescriptor.Dimension = D3D12TextureDimension(GetDimension());
         resourceDescriptor.Alignment = 0;
@@ -283,7 +293,7 @@
         resourceDescriptor.DepthOrArraySize = GetDepthOrArraySize();
         resourceDescriptor.MipLevels = static_cast<UINT16>(GetNumMipLevels());
         resourceDescriptor.Format = D3D12TextureFormat(GetFormat().format);
-        resourceDescriptor.SampleDesc.Count = descriptor->sampleCount;
+        resourceDescriptor.SampleDesc.Count = GetSampleCount();
         // TODO(bryan.bernhart@intel.com): investigate how to specify standard MSAA sample pattern.
         resourceDescriptor.SampleDesc.Quality = 0;
         resourceDescriptor.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
@@ -295,18 +305,25 @@
                         ->Allocate(D3D12_HEAP_TYPE_DEFAULT, resourceDescriptor,
                                    D3D12_RESOURCE_STATE_COMMON);
 
+        Device* device = ToBackend(GetDevice());
+
         if (device->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting)) {
-            device->ConsumedError(ClearTexture(device->GetPendingCommandList(), 0,
-                                               GetNumMipLevels(), 0, GetArrayLayers(),
-                                               TextureBase::ClearValue::NonZero));
+            CommandRecordingContext* commandContext;
+            DAWN_TRY_ASSIGN(commandContext, device->GetPendingCommandContext());
+
+            DAWN_TRY(ClearTexture(commandContext, 0, GetNumMipLevels(), 0, GetArrayLayers(),
+                                  TextureBase::ClearValue::NonZero));
         }
+
+        return {};
     }
 
     // With this constructor, the lifetime of the ID3D12Resource is externally managed.
     Texture::Texture(Device* device,
                      const TextureDescriptor* descriptor,
-                     ID3D12Resource* nativeTexture)
-        : TextureBase(device, descriptor, TextureState::OwnedExternal), mResource(nativeTexture) {
+                     ComPtr<ID3D12Resource> nativeTexture)
+        : TextureBase(device, descriptor, TextureState::OwnedExternal),
+          mResource(std::move(nativeTexture)) {
         SetIsSubresourceContentInitialized(true, 0, descriptor->mipLevelCount, 0,
                                            descriptor->arrayLayerCount);
     }
@@ -341,16 +358,18 @@
     // When true is returned, a D3D12_RESOURCE_BARRIER has been created and must be used in a
     // ResourceBarrier call. Failing to do so will cause the tracked state to become invalid and can
     // cause subsequent errors.
-    bool Texture::TransitionUsageAndGetResourceBarrier(D3D12_RESOURCE_BARRIER* barrier,
+    bool Texture::TransitionUsageAndGetResourceBarrier(CommandRecordingContext* commandContext,
+                                                       D3D12_RESOURCE_BARRIER* barrier,
                                                        dawn::TextureUsage newUsage) {
-        return TransitionUsageAndGetResourceBarrier(barrier,
+        return TransitionUsageAndGetResourceBarrier(commandContext, barrier,
                                                     D3D12TextureUsage(newUsage, GetFormat()));
     }
 
     // When true is returned, a D3D12_RESOURCE_BARRIER has been created and must be used in a
     // ResourceBarrier call. Failing to do so will cause the tracked state to become invalid and can
     // cause subsequent errors.
-    bool Texture::TransitionUsageAndGetResourceBarrier(D3D12_RESOURCE_BARRIER* barrier,
+    bool Texture::TransitionUsageAndGetResourceBarrier(CommandRecordingContext* commandContext,
+                                                       D3D12_RESOURCE_BARRIER* barrier,
                                                        D3D12_RESOURCE_STATES newState) {
         // Avoid transitioning the texture when it isn't needed.
         // TODO(cwallez@chromium.org): Need some form of UAV barriers at some point.
@@ -417,17 +436,17 @@
         return true;
     }
 
-    void Texture::TransitionUsageNow(ComPtr<ID3D12GraphicsCommandList> commandList,
+    void Texture::TransitionUsageNow(CommandRecordingContext* commandContext,
                                      dawn::TextureUsage usage) {
-        TransitionUsageNow(commandList, D3D12TextureUsage(usage, GetFormat()));
+        TransitionUsageNow(commandContext, D3D12TextureUsage(usage, GetFormat()));
     }
 
-    void Texture::TransitionUsageNow(ComPtr<ID3D12GraphicsCommandList> commandList,
+    void Texture::TransitionUsageNow(CommandRecordingContext* commandContext,
                                      D3D12_RESOURCE_STATES newState) {
         D3D12_RESOURCE_BARRIER barrier;
 
-        if (TransitionUsageAndGetResourceBarrier(&barrier, newState)) {
-            commandList->ResourceBarrier(1, &barrier);
+        if (TransitionUsageAndGetResourceBarrier(commandContext, &barrier, newState)) {
+            commandContext->GetCommandList()->ResourceBarrier(1, &barrier);
         }
     }
 
@@ -475,7 +494,7 @@
         return dsvDesc;
     }
 
-    MaybeError Texture::ClearTexture(ComPtr<ID3D12GraphicsCommandList> commandList,
+    MaybeError Texture::ClearTexture(CommandRecordingContext* commandContext,
                                      uint32_t baseMipLevel,
                                      uint32_t levelCount,
                                      uint32_t baseArrayLayer,
@@ -488,12 +507,14 @@
             return {};
         }
 
+        ID3D12GraphicsCommandList* commandList = commandContext->GetCommandList();
+
         Device* device = ToBackend(GetDevice());
         DescriptorHeapAllocator* descriptorHeapAllocator = device->GetDescriptorHeapAllocator();
         uint8_t clearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0 : 1;
         if (GetFormat().isRenderable) {
             if (GetFormat().HasDepthOrStencil()) {
-                TransitionUsageNow(commandList, D3D12_RESOURCE_STATE_DEPTH_WRITE);
+                TransitionUsageNow(commandContext, D3D12_RESOURCE_STATE_DEPTH_WRITE);
                 DescriptorHeapHandle dsvHeap;
                 DAWN_TRY_ASSIGN(dsvHeap, descriptorHeapAllocator->AllocateCPUHeap(
                                              D3D12_DESCRIPTOR_HEAP_TYPE_DSV, 1));
@@ -513,7 +534,7 @@
                 commandList->ClearDepthStencilView(dsvHandle, clearFlags, clearColor, clearColor, 0,
                                                    nullptr);
             } else {
-                TransitionUsageNow(commandList, D3D12_RESOURCE_STATE_RENDER_TARGET);
+                TransitionUsageNow(commandContext, D3D12_RESOURCE_STATE_RENDER_TARGET);
                 DescriptorHeapHandle rtvHeap;
                 DAWN_TRY_ASSIGN(rtvHeap, descriptorHeapAllocator->AllocateCPUHeap(
                                              D3D12_DESCRIPTOR_HEAP_TYPE_RTV, 1));
@@ -548,7 +569,7 @@
                       reinterpret_cast<uint32_t*>(uploadHandle.mappedBuffer + bufferSize),
                       clearColor);
 
-            TransitionUsageNow(commandList, D3D12_RESOURCE_STATE_COPY_DEST);
+            TransitionUsageNow(commandContext, D3D12_RESOURCE_STATE_COPY_DEST);
 
             // compute d3d12 texture copy locations for texture and buffer
             Extent3D copySize = {GetSize().width, GetSize().height, 1};
@@ -586,7 +607,7 @@
         return {};
     }
 
-    void Texture::EnsureSubresourceContentInitialized(ComPtr<ID3D12GraphicsCommandList> commandList,
+    void Texture::EnsureSubresourceContentInitialized(CommandRecordingContext* commandContext,
                                                       uint32_t baseMipLevel,
                                                       uint32_t levelCount,
                                                       uint32_t baseArrayLayer,
@@ -598,7 +619,7 @@
                                              layerCount)) {
             // If subresource has not been initialized, clear it to black as it could contain
             // dirty bits from recycled memory
-            GetDevice()->ConsumedError(ClearTexture(commandList, baseMipLevel, levelCount,
+            GetDevice()->ConsumedError(ClearTexture(commandContext, baseMipLevel, levelCount,
                                                     baseArrayLayer, layerCount,
                                                     TextureBase::ClearValue::Zero));
         }
diff --git a/src/dawn_native/d3d12/TextureD3D12.h b/src/dawn_native/d3d12/TextureD3D12.h
index af32dd3..162b50e 100644
--- a/src/dawn_native/d3d12/TextureD3D12.h
+++ b/src/dawn_native/d3d12/TextureD3D12.h
@@ -22,6 +22,7 @@
 
 namespace dawn_native { namespace d3d12 {
 
+    class CommandRecordingContext;
     class Device;
 
     DXGI_FORMAT D3D12TextureFormat(dawn::TextureFormat format);
@@ -31,33 +32,39 @@
 
     class Texture : public TextureBase {
       public:
-        Texture(Device* device, const TextureDescriptor* descriptor);
-        Texture(Device* device, const TextureDescriptor* descriptor, ID3D12Resource* nativeTexture);
+        static ResultOrError<TextureBase*> Create(Device* device,
+                                                  const TextureDescriptor* descriptor);
+        Texture(Device* device,
+                const TextureDescriptor* descriptor,
+                ComPtr<ID3D12Resource> nativeTexture);
         ~Texture();
 
         DXGI_FORMAT GetD3D12Format() const;
         ID3D12Resource* GetD3D12Resource() const;
-        bool TransitionUsageAndGetResourceBarrier(D3D12_RESOURCE_BARRIER* barrier,
+        bool TransitionUsageAndGetResourceBarrier(CommandRecordingContext* commandContext,
+                                                  D3D12_RESOURCE_BARRIER* barrier,
                                                   dawn::TextureUsage newUsage);
-        void TransitionUsageNow(ComPtr<ID3D12GraphicsCommandList> commandList,
-                                dawn::TextureUsage usage);
-        void TransitionUsageNow(ComPtr<ID3D12GraphicsCommandList> commandList,
+        void TransitionUsageNow(CommandRecordingContext* commandContext, dawn::TextureUsage usage);
+        void TransitionUsageNow(CommandRecordingContext* commandContext,
                                 D3D12_RESOURCE_STATES newState);
 
         D3D12_RENDER_TARGET_VIEW_DESC GetRTVDescriptor(uint32_t baseMipLevel,
                                                        uint32_t baseArrayLayer,
                                                        uint32_t layerCount) const;
         D3D12_DEPTH_STENCIL_VIEW_DESC GetDSVDescriptor(uint32_t baseMipLevel) const;
-        void EnsureSubresourceContentInitialized(ComPtr<ID3D12GraphicsCommandList> commandList,
+        void EnsureSubresourceContentInitialized(CommandRecordingContext* commandContext,
                                                  uint32_t baseMipLevel,
                                                  uint32_t levelCount,
                                                  uint32_t baseArrayLayer,
                                                  uint32_t layerCount);
 
       private:
+        Texture(Device* device, const TextureDescriptor* descriptor);
+        MaybeError InitializeAsInternalTexture();
+
         // Dawn API
         void DestroyImpl() override;
-        MaybeError ClearTexture(ComPtr<ID3D12GraphicsCommandList> commandList,
+        MaybeError ClearTexture(CommandRecordingContext* commandContext,
                                 uint32_t baseMipLevel,
                                 uint32_t levelCount,
                                 uint32_t baseArrayLayer,
@@ -66,7 +73,8 @@
 
         UINT16 GetDepthOrArraySize();
 
-        bool TransitionUsageAndGetResourceBarrier(D3D12_RESOURCE_BARRIER* barrier,
+        bool TransitionUsageAndGetResourceBarrier(CommandRecordingContext* commandContext,
+                                                  D3D12_RESOURCE_BARRIER* barrier,
                                                   D3D12_RESOURCE_STATES newState);
 
         ComPtr<ID3D12Resource> mResource;