d3d11: support creating webgpu::Device from an external d3d11 device

To support using an external d3d11 device, d3d11::Queue will swap
the D3DDeviceContextState of the D3D11DeviceContext before executing
dawn command buffers, and restore to previous D3DDeviceContextState
after executing dawn command buffers.

Bug: dawn:1938
Change-Id: I33d8541e3975f7e9cfd9e270cacea78461449d74
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/157960
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Peng Huang <penghuang@chromium.org>
diff --git a/dawn.json b/dawn.json
index cfe2851..41a8963 100644
--- a/dawn.json
+++ b/dawn.json
@@ -3246,12 +3246,13 @@
             {"value": 1009, "name": "dawn shader module SPIRV options descriptor", "tags": ["dawn"]},
             {"value": 1010, "name": "request adapter options LUID", "tags": ["dawn", "native"]},
             {"value": 1011, "name": "request adapter options get GL proc", "tags": ["dawn", "native"]},
-            {"value": 1012, "name": "dawn multisample state render to single sampled", "tags": ["dawn"]},
-            {"value": 1013, "name": "dawn render pass color attachment render to single sampled", "tags": ["dawn"]},
-            {"value": 1014, "name": "render pass pixel local storage", "tags": ["dawn"]},
-            {"value": 1015, "name": "pipeline layout pixel local storage", "tags": ["dawn"]},
-            {"value": 1016, "name": "buffer host mapped pointer", "tags": ["dawn"]},
-            {"value": 1017, "name": "dawn experimental subgroup limits", "tags": ["dawn"]},
+            {"value": 1012, "name": "request adapter options D3D11 device", "tags": ["dawn", "native"]},
+            {"value": 1013, "name": "dawn multisample state render to single sampled", "tags": ["dawn"]},
+            {"value": 1014, "name": "dawn render pass color attachment render to single sampled", "tags": ["dawn"]},
+            {"value": 1015, "name": "render pass pixel local storage", "tags": ["dawn"]},
+            {"value": 1016, "name": "pipeline layout pixel local storage", "tags": ["dawn"]},
+            {"value": 1017, "name": "buffer host mapped pointer", "tags": ["dawn"]},
+            {"value": 1018, "name": "dawn experimental subgroup limits", "tags": ["dawn"]},
 
             {"value": 1100, "name": "shared texture memory vk image descriptor", "tags": ["dawn", "native"]},
             {"value": 1101, "name": "shared texture memory vk dedicated allocation descriptor", "tags": ["dawn", "native"]},
diff --git a/docs/dawn/features/adapter_options.md b/docs/dawn/features/adapter_options.md
index c0b4103..22de658 100644
--- a/docs/dawn/features/adapter_options.md
+++ b/docs/dawn/features/adapter_options.md
@@ -29,6 +29,10 @@
 
 When discovering adapters on D3D11 and D3D12, Dawn only discovers adapters matching the provided `RequestAdapterOptionsLUID::adapterLUID`. This extension struct does nothing on other backends.
 
+### `RequestAdapterOptionsD3D11Device`
+
+When discovering adapter on D3D11, Dawn creates an adapter matching the provided `RequestAdapterOptionsD3D11Device::device`, and `wgpu::Device` created from the adapter will share the same D3D11 device from `RequestAdapterOptionsD3D11Device::device`. This extension struct does nothing on other backends.
+
 ### `DawnTogglesDescriptor`
 
 When discovering adapters, Dawn will use chained `DawnTogglesDescriptor` as required adapter
diff --git a/include/dawn/native/D3D11Backend.h b/include/dawn/native/D3D11Backend.h
index 456473d..cfcfb27 100644
--- a/include/dawn/native/D3D11Backend.h
+++ b/include/dawn/native/D3D11Backend.h
@@ -40,6 +40,15 @@
 
 DAWN_NATIVE_EXPORT Microsoft::WRL::ComPtr<ID3D11Device> GetD3D11Device(WGPUDevice device);
 
+// May be chained on RequestAdapterOptions
+struct DAWN_NATIVE_EXPORT RequestAdapterOptionsD3D11Device : wgpu::ChainedStruct {
+    RequestAdapterOptionsD3D11Device() {
+        sType = static_cast<wgpu::SType>(WGPUSType_RequestAdapterOptionsD3D11Device);
+    }
+
+    Microsoft::WRL::ComPtr<ID3D11Device> device;
+};
+
 // May be chained on SharedTextureMemoryDescriptor
 struct DAWN_NATIVE_EXPORT SharedTextureMemoryD3D11Texture2DDescriptor : wgpu::ChainedStruct {
     SharedTextureMemoryD3D11Texture2DDescriptor() {
diff --git a/src/dawn/native/ChainUtilsImpl.inl b/src/dawn/native/ChainUtilsImpl.inl
index 33ddaa6..5d28c58 100644
--- a/src/dawn/native/ChainUtilsImpl.inl
+++ b/src/dawn/native/ChainUtilsImpl.inl
@@ -37,6 +37,7 @@
 }
 
 namespace d3d11 {
+struct RequestAdapterOptionsD3D11Device;
 struct SharedTextureMemoryD3D11Texture2DDescriptor;
 }
 
@@ -60,12 +61,17 @@
     wgpu::SType(WGPUSType_RequestAdapterOptionsLUID);
 
 template <>
+constexpr inline wgpu::SType STypeForImpl<d3d11::RequestAdapterOptionsD3D11Device> =
+    wgpu::SType(WGPUSType_RequestAdapterOptionsD3D11Device);
+
+template <>
 constexpr inline wgpu::SType STypeForImpl<opengl::RequestAdapterOptionsGetGLProc> =
     wgpu::SType(WGPUSType_RequestAdapterOptionsGetGLProc);
 
 template <>
 struct AdditionalExtensions<RequestAdapterOptions> {
     using List = AdditionalExtensionsList<const d3d::RequestAdapterOptionsLUID*,
+                                          const d3d11::RequestAdapterOptionsD3D11Device*,
                                           const opengl::RequestAdapterOptionsGetGLProc*>;
 };
 
diff --git a/src/dawn/native/d3d11/BackendD3D11.cpp b/src/dawn/native/d3d11/BackendD3D11.cpp
index 751a254..41e3da5 100644
--- a/src/dawn/native/d3d11/BackendD3D11.cpp
+++ b/src/dawn/native/d3d11/BackendD3D11.cpp
@@ -31,6 +31,7 @@
 #include <utility>
 
 #include "dawn/common/Log.h"
+#include "dawn/native/ChainUtils.h"
 #include "dawn/native/D3D11Backend.h"
 #include "dawn/native/Instance.h"
 #include "dawn/native/d3d/D3DError.h"
@@ -38,6 +39,51 @@
 #include "dawn/native/d3d11/PlatformFunctionsD3D11.h"
 
 namespace dawn::native::d3d11 {
+namespace {
+
+MaybeError ValidateRequestOptions(const RequestAdapterOptions* options,
+                                  ComPtr<IDXGIAdapter>* dxgiAdapter,
+                                  ComPtr<ID3D11Device>* d3d11Device) {
+    const d3d::RequestAdapterOptionsLUID* luidOptions = nullptr;
+    FindInChain(options->nextInChain, &luidOptions);
+
+    const d3d11::RequestAdapterOptionsD3D11Device* d3d11DeviceOption = nullptr;
+    FindInChain(options->nextInChain, &d3d11DeviceOption);
+
+    if (!d3d11DeviceOption) {
+        return {};
+    }
+
+    DAWN_INVALID_IF(!d3d11DeviceOption->device,
+                    "RequestAdapterOptionsD3D11Device::device is null.");
+
+    ComPtr<ID3D11Multithread> d3d11Multithread;
+    DAWN_TRY(CheckHRESULT(d3d11DeviceOption->device.As(&d3d11Multithread),
+                          "D3D11: Get ID3D11Multithread"));
+
+    DAWN_INVALID_IF(!d3d11Multithread->GetMultithreadProtected(),
+                    "Multithread protection is not enabled.");
+
+    ComPtr<IDXGIDevice> dxgiDevice;
+    DAWN_TRY(CheckHRESULT(d3d11DeviceOption->device.As(&dxgiDevice), "D3D11: Get IDXGIDevice"));
+
+    ComPtr<IDXGIAdapter> adapter;
+    DAWN_TRY(CheckHRESULT(dxgiDevice->GetAdapter(&adapter), "D3D11: Get IDXGIAdapter"));
+
+    DXGI_ADAPTER_DESC adapterDesc;
+    DAWN_TRY(CheckHRESULT(adapter->GetDesc(&adapterDesc), "D3D11: IDXGIAdapter::GetDesc()"));
+
+    DAWN_INVALID_IF(luidOptions && memcmp(&adapterDesc.AdapterLuid, &luidOptions->adapterLUID,
+                                          sizeof(LUID)) != 0,
+                    "RequestAdapterOptionsLUID and RequestAdapterOptionsD3D11Device don't match.");
+
+    *dxgiAdapter = std::move(adapter);
+    *d3d11Device = d3d11DeviceOption->device;
+
+    return {};
+}
+
+}  // namespace
 
 Backend::Backend(InstanceBase* instance) : Base(instance, wgpu::BackendType::D3D11) {}
 
@@ -54,12 +100,48 @@
     return static_cast<const PlatformFunctions*>(Base::GetFunctions());
 }
 
+std::vector<Ref<PhysicalDeviceBase>> Backend::DiscoverPhysicalDevices(
+    const RequestAdapterOptions* options) {
+    if (options->forceFallbackAdapter) {
+        return {};
+    }
+
+    FeatureLevel featureLevel =
+        options->compatibilityMode ? FeatureLevel::Compatibility : FeatureLevel::Core;
+
+    ComPtr<IDXGIAdapter> dxgiAdapter;
+    ComPtr<ID3D11Device> d3d11Device;
+    if (GetInstance()->ConsumedError(ValidateRequestOptions(options, &dxgiAdapter, &d3d11Device))) {
+        return {};
+    }
+
+    if (d3d11Device) {
+        Ref<PhysicalDeviceBase> physicalDevice;
+        if (GetInstance()->ConsumedErrorAndWarnOnce(
+                CreatePhysicalDevice(std::move(dxgiAdapter), std::move(d3d11Device)),
+                &physicalDevice) ||
+            !physicalDevice->SupportsFeatureLevel(featureLevel)) {
+            return {};
+        }
+        return {std::move(physicalDevice)};
+    }
+
+    return Base::DiscoverPhysicalDevices(options);
+}
+
 ResultOrError<Ref<PhysicalDeviceBase>> Backend::CreatePhysicalDeviceFromIDXGIAdapter(
     ComPtr<IDXGIAdapter> dxgiAdapter) {
+    return CreatePhysicalDevice(std::move(dxgiAdapter), /*d3d11Device=*/{});
+}
+
+ResultOrError<Ref<PhysicalDeviceBase>> Backend::CreatePhysicalDevice(
+    ComPtr<IDXGIAdapter> dxgiAdapter,
+    ComPtr<ID3D11Device> d3d11Device) {
     ComPtr<IDXGIAdapter3> dxgiAdapter3;
     DAWN_TRY(CheckHRESULT(dxgiAdapter.As(&dxgiAdapter3), "DXGIAdapter retrieval"));
+
     Ref<PhysicalDevice> physicalDevice =
-        AcquireRef(new PhysicalDevice(this, std::move(dxgiAdapter3)));
+        AcquireRef(new PhysicalDevice(this, std::move(dxgiAdapter3), std::move(d3d11Device)));
     DAWN_TRY(physicalDevice->Initialize());
 
     return {std::move(physicalDevice)};
diff --git a/src/dawn/native/d3d11/BackendD3D11.h b/src/dawn/native/d3d11/BackendD3D11.h
index d8ad088..0dce2e6 100644
--- a/src/dawn/native/d3d11/BackendD3D11.h
+++ b/src/dawn/native/d3d11/BackendD3D11.h
@@ -43,11 +43,17 @@
     MaybeError Initialize();
     const PlatformFunctions* GetFunctions() const;
 
+    std::vector<Ref<PhysicalDeviceBase>> DiscoverPhysicalDevices(
+        const RequestAdapterOptions* options) override;
+
   protected:
     ResultOrError<Ref<PhysicalDeviceBase>> CreatePhysicalDeviceFromIDXGIAdapter(
         ComPtr<IDXGIAdapter> dxgiAdapter) override;
 
   private:
+    ResultOrError<Ref<PhysicalDeviceBase>> CreatePhysicalDevice(ComPtr<IDXGIAdapter> dxgiAdapter,
+                                                                ComPtr<ID3D11Device> d3d11Device);
+
     using Base = d3d::Backend;
 };
 
diff --git a/src/dawn/native/d3d11/BindGroupTrackerD3D11.cpp b/src/dawn/native/d3d11/BindGroupTrackerD3D11.cpp
index e9f192b..819a1b9 100644
--- a/src/dawn/native/d3d11/BindGroupTrackerD3D11.cpp
+++ b/src/dawn/native/d3d11/BindGroupTrackerD3D11.cpp
@@ -44,7 +44,7 @@
 namespace dawn::native::d3d11 {
 namespace {
 
-bool CheckAllSlotsAreEmpty(CommandRecordingContext* commandContext) {
+bool CheckAllSlotsAreEmpty(const ScopedSwapStateCommandRecordingContext* commandContext) {
     auto* deviceContext = commandContext->GetD3D11DeviceContext4();
 
     // Reserve one slot for builtin constants.
@@ -101,7 +101,7 @@
     return true;
 }
 
-void ResetAllRenderSlots(CommandRecordingContext* commandContext) {
+void ResetAllRenderSlots(const ScopedSwapStateCommandRecordingContext* commandContext) {
     auto* deviceContext = commandContext->GetD3D11DeviceContext4();
 
     // Reserve one slot for builtin constants.
@@ -130,12 +130,13 @@
 
 }  // namespace
 
-BindGroupTracker::BindGroupTracker(CommandRecordingContext* commandContext, bool isRenderPass)
+BindGroupTracker::BindGroupTracker(const ScopedSwapStateCommandRecordingContext* commandContext,
+                                   bool isRenderPass)
     : mCommandContext(commandContext),
       mIsRenderPass(isRenderPass),
       mVisibleStages(isRenderPass ? wgpu::ShaderStage::Vertex | wgpu::ShaderStage::Fragment
                                   : wgpu::ShaderStage::Compute) {
-    mLastAppliedPipelineLayout = commandContext->GetDevice()->GetEmptyPipelineLayout();
+    mLastAppliedPipelineLayout = mCommandContext->GetDevice()->GetEmptyPipelineLayout();
 }
 
 BindGroupTracker::~BindGroupTracker() {
diff --git a/src/dawn/native/d3d11/BindGroupTrackerD3D11.h b/src/dawn/native/d3d11/BindGroupTrackerD3D11.h
index ed2912d..ba70285 100644
--- a/src/dawn/native/d3d11/BindGroupTrackerD3D11.h
+++ b/src/dawn/native/d3d11/BindGroupTrackerD3D11.h
@@ -33,13 +33,14 @@
 
 namespace dawn::native::d3d11 {
 
-class CommandRecordingContext;
+class ScopedSwapStateCommandRecordingContext;
 
 // We need convert WebGPU bind slot to d3d11 bind slot according a map in PipelineLayout, so we
 // cannot inherit BindGroupTrackerGroups.
 class BindGroupTracker : public BindGroupTrackerBase</*CanInheritBindGroups=*/true, uint64_t> {
   public:
-    BindGroupTracker(CommandRecordingContext* commandContext, bool isRenderPass);
+    BindGroupTracker(const ScopedSwapStateCommandRecordingContext* commandContext,
+                     bool isRenderPass);
     ~BindGroupTracker();
     MaybeError Apply();
 
@@ -47,7 +48,7 @@
     MaybeError ApplyBindGroup(BindGroupIndex index);
     void UnApplyBindGroup(BindGroupIndex index);
 
-    CommandRecordingContext* const mCommandContext;
+    const ScopedSwapStateCommandRecordingContext* mCommandContext;
     const bool mIsRenderPass;
     const wgpu::ShaderStage mVisibleStages;
 };
diff --git a/src/dawn/native/d3d11/BufferD3D11.cpp b/src/dawn/native/d3d11/BufferD3D11.cpp
index b9a83a3..65a6e85 100644
--- a/src/dawn/native/d3d11/BufferD3D11.cpp
+++ b/src/dawn/native/d3d11/BufferD3D11.cpp
@@ -37,13 +37,15 @@
 #include "dawn/native/CommandBuffer.h"
 #include "dawn/native/DynamicUploader.h"
 #include "dawn/native/d3d/D3DError.h"
-#include "dawn/native/d3d11/CommandRecordingContextD3D11.h"
 #include "dawn/native/d3d11/DeviceD3D11.h"
 #include "dawn/native/d3d11/UtilsD3D11.h"
 #include "dawn/platform/DawnPlatform.h"
 #include "dawn/platform/tracing/TraceEvent.h"
 
 namespace dawn::native::d3d11 {
+
+class ScopedCommandRecordingContext;
+
 namespace {
 
 constexpr wgpu::BufferUsage kD3D11AllowedUniformBufferUsages =
@@ -148,13 +150,16 @@
 }  // namespace
 
 // static
-ResultOrError<Ref<Buffer>> Buffer::Create(Device* device, const BufferDescriptor* descriptor) {
+ResultOrError<Ref<Buffer>> Buffer::Create(Device* device,
+                                          const BufferDescriptor* descriptor,
+                                          const ScopedCommandRecordingContext* commandContext) {
     Ref<Buffer> buffer = AcquireRef(new Buffer(device, descriptor));
-    DAWN_TRY(buffer->Initialize(descriptor->mappedAtCreation));
+    DAWN_TRY(buffer->Initialize(descriptor->mappedAtCreation, commandContext));
     return buffer;
 }
 
-MaybeError Buffer::Initialize(bool mappedAtCreation) {
+MaybeError Buffer::Initialize(bool mappedAtCreation,
+                              const ScopedCommandRecordingContext* commandContext) {
     // TODO(dawn:1705): handle mappedAtCreation for NonzeroClearResourcesOnCreationForTesting
 
     // Allocate at least 4 bytes so clamped accesses are always in bounds.
@@ -217,7 +222,14 @@
 
     if (!mappedAtCreation) {
         if (GetDevice()->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting)) {
-            DAWN_TRY(ClearInternal(ToBackend(GetDevice())->GetPendingCommandContext(), 1u));
+            if (commandContext) {
+                DAWN_TRY(ClearInternal(commandContext, 1u));
+            } else {
+                auto tmpCommandContext =
+                    ToBackend(GetDevice())
+                        ->GetScopedPendingCommandContext(Device::SubmitMode::Normal);
+                DAWN_TRY(ClearInternal(&tmpCommandContext, 1u));
+            }
         }
 
         // Initialize the padding bytes to zero.
@@ -226,8 +238,15 @@
             if (paddingBytes > 0) {
                 uint32_t clearSize = paddingBytes;
                 uint64_t clearOffset = GetSize();
-                DAWN_TRY(ClearInternal(ToBackend(GetDevice())->GetPendingCommandContext(), 0,
-                                       clearOffset, clearSize));
+                if (commandContext) {
+                    DAWN_TRY(ClearInternal(commandContext, 0, clearOffset, clearSize));
+
+                } else {
+                    auto tmpCommandContext =
+                        ToBackend(GetDevice())
+                            ->GetScopedPendingCommandContext(Device::SubmitMode::Normal);
+                    DAWN_TRY(ClearInternal(&tmpCommandContext, 0, clearOffset, clearSize));
+                }
             }
         }
     }
@@ -241,48 +260,47 @@
     return IsMappable(GetUsage());
 }
 
-MaybeError Buffer::MapInternal() {
+MaybeError Buffer::MapInternal(const ScopedCommandRecordingContext* commandContext) {
     DAWN_ASSERT(IsMappable(GetUsage()));
     DAWN_ASSERT(!mMappedData);
 
-    CommandRecordingContext* commandContext = ToBackend(GetDevice())->GetPendingCommandContext();
-
     // Always map buffer with D3D11_MAP_READ_WRITE even for mapping wgpu::MapMode:Read, because we
     // need write permission to initialize the buffer.
     // TODO(dawn:1705): investigate the performance impact of mapping with D3D11_MAP_READ_WRITE.
     D3D11_MAPPED_SUBRESOURCE mappedResource;
-    DAWN_TRY(CheckHRESULT(
-        commandContext->GetD3D11DeviceContext4()->Map(mD3d11NonConstantBuffer.Get(),
-                                                      /*Subresource=*/0, D3D11_MAP_READ_WRITE,
-                                                      /*MapFlags=*/0, &mappedResource),
-        "ID3D11DeviceContext::Map"));
+    DAWN_TRY(CheckHRESULT(commandContext->Map(mD3d11NonConstantBuffer.Get(),
+                                              /*Subresource=*/0, D3D11_MAP_READ_WRITE,
+                                              /*MapFlags=*/0, &mappedResource),
+                          "ID3D11DeviceContext::Map"));
     mMappedData = reinterpret_cast<uint8_t*>(mappedResource.pData);
 
     return {};
 }
 
-void Buffer::UnmapInternal() {
+void Buffer::UnmapInternal(const ScopedCommandRecordingContext* commandContext) {
     DAWN_ASSERT(mMappedData);
-
-    CommandRecordingContext* commandContext = ToBackend(GetDevice())->GetPendingCommandContext();
-    commandContext->GetD3D11DeviceContext4()->Unmap(mD3d11NonConstantBuffer.Get(),
-                                                    /*Subresource=*/0);
+    commandContext->Unmap(mD3d11NonConstantBuffer.Get(),
+                          /*Subresource=*/0);
     mMappedData = nullptr;
 }
 
 MaybeError Buffer::MapAtCreationImpl() {
     DAWN_ASSERT(IsMappable(GetUsage()));
-    return MapInternal();
+    auto commandContext =
+        ToBackend(GetDevice())->GetScopedPendingCommandContext(Device::SubmitMode::Normal);
+    return MapInternal(&commandContext);
 }
 
 MaybeError Buffer::MapAsyncImpl(wgpu::MapMode mode, size_t offset, size_t size) {
     DAWN_ASSERT(mD3d11NonConstantBuffer);
 
-    // TODO(dawn:1705): make sure the map call is not blocked by the GPU operations.
-    DAWN_TRY(MapInternal());
+    auto commandContext =
+        ToBackend(GetDevice())->GetScopedPendingCommandContext(Device::SubmitMode::Normal);
 
-    CommandRecordingContext* commandContext = ToBackend(GetDevice())->GetPendingCommandContext();
-    DAWN_TRY(EnsureDataInitialized(commandContext));
+    // TODO(dawn:1705): make sure the map call is not blocked by the GPU operations.
+    DAWN_TRY(MapInternal(&commandContext));
+
+    DAWN_TRY(EnsureDataInitialized(&commandContext));
 
     return {};
 }
@@ -290,7 +308,9 @@
 void Buffer::UnmapImpl() {
     DAWN_ASSERT(mD3d11NonConstantBuffer);
     DAWN_ASSERT(mMappedData);
-    UnmapInternal();
+    auto commandContext =
+        ToBackend(GetDevice())->GetScopedPendingCommandContext(Device::SubmitMode::Normal);
+    UnmapInternal(&commandContext);
 }
 
 void* Buffer::GetMappedPointer() {
@@ -309,7 +329,7 @@
     //   other threads using the buffer since there are no other live refs.
     BufferBase::DestroyImpl();
     if (mMappedData) {
-        UnmapInternal();
+        UnmapImpl();
     }
     mD3d11NonConstantBuffer = nullptr;
 }
@@ -320,7 +340,7 @@
                  GetLabel());
 }
 
-MaybeError Buffer::EnsureDataInitialized(CommandRecordingContext* commandContext) {
+MaybeError Buffer::EnsureDataInitialized(const ScopedCommandRecordingContext* commandContext) {
     if (!NeedsInitialization()) {
         return {};
     }
@@ -329,9 +349,10 @@
     return {};
 }
 
-MaybeError Buffer::EnsureDataInitializedAsDestination(CommandRecordingContext* commandContext,
-                                                      uint64_t offset,
-                                                      uint64_t size) {
+MaybeError Buffer::EnsureDataInitializedAsDestination(
+    const ScopedCommandRecordingContext* commandContext,
+    uint64_t offset,
+    uint64_t size) {
     if (!NeedsInitialization()) {
         return {};
     }
@@ -345,8 +366,9 @@
     return {};
 }
 
-MaybeError Buffer::EnsureDataInitializedAsDestination(CommandRecordingContext* commandContext,
-                                                      const CopyTextureToBufferCmd* copy) {
+MaybeError Buffer::EnsureDataInitializedAsDestination(
+    const ScopedCommandRecordingContext* commandContext,
+    const CopyTextureToBufferCmd* copy) {
     if (!NeedsInitialization()) {
         return {};
     }
@@ -360,7 +382,7 @@
     return {};
 }
 
-MaybeError Buffer::InitializeToZero(CommandRecordingContext* commandContext) {
+MaybeError Buffer::InitializeToZero(const ScopedCommandRecordingContext* commandContext) {
     DAWN_ASSERT(NeedsInitialization());
 
     DAWN_TRY(ClearInternal(commandContext, uint8_t(0u)));
@@ -374,15 +396,14 @@
     mConstantBufferIsUpdated = false;
 }
 
-void Buffer::EnsureConstantBufferIsUpdated(CommandRecordingContext* commandContext) {
+void Buffer::EnsureConstantBufferIsUpdated(const ScopedCommandRecordingContext* commandContext) {
     if (mConstantBufferIsUpdated) {
         return;
     }
 
     DAWN_ASSERT(mD3d11NonConstantBuffer);
     DAWN_ASSERT(mD3d11ConstantBuffer);
-    commandContext->GetD3D11DeviceContext4()->CopyResource(mD3d11ConstantBuffer.Get(),
-                                                           mD3d11NonConstantBuffer.Get());
+    commandContext->CopyResource(mD3d11ConstantBuffer.Get(), mD3d11NonConstantBuffer.Get());
     mConstantBufferIsUpdated = true;
 }
 
@@ -435,7 +456,7 @@
     return uav;
 }
 
-MaybeError Buffer::Clear(CommandRecordingContext* commandContext,
+MaybeError Buffer::Clear(const ScopedCommandRecordingContext* commandContext,
                          uint8_t clearValue,
                          uint64_t offset,
                          uint64_t size) {
@@ -448,14 +469,14 @@
     // Map the buffer if it is possible, so EnsureDataInitializedAsDestination() and ClearInternal()
     // can write the mapped memory directly.
     ScopedMap scopedMap;
-    DAWN_TRY_ASSIGN(scopedMap, ScopedMap::Create(this));
+    DAWN_TRY_ASSIGN(scopedMap, ScopedMap::Create(commandContext, this));
 
     // For non-staging buffers, we can use UpdateSubresource to write the data.
     DAWN_TRY(EnsureDataInitializedAsDestination(commandContext, offset, size));
     return ClearInternal(commandContext, clearValue, offset, size);
 }
 
-MaybeError Buffer::ClearInternal(CommandRecordingContext* commandContext,
+MaybeError Buffer::ClearInternal(const ScopedCommandRecordingContext* commandContext,
                                  uint8_t clearValue,
                                  uint64_t offset,
                                  uint64_t size) {
@@ -477,7 +498,7 @@
     return WriteInternal(commandContext, offset, clearData.data(), size);
 }
 
-MaybeError Buffer::Write(CommandRecordingContext* commandContext,
+MaybeError Buffer::Write(const ScopedCommandRecordingContext* commandContext,
                          uint64_t offset,
                          const void* data,
                          size_t size) {
@@ -487,14 +508,14 @@
     // Map the buffer if it is possible, so EnsureDataInitializedAsDestination() and WriteInternal()
     // can write the mapped memory directly.
     ScopedMap scopedMap;
-    DAWN_TRY_ASSIGN(scopedMap, ScopedMap::Create(this));
+    DAWN_TRY_ASSIGN(scopedMap, ScopedMap::Create(commandContext, this));
 
     // For non-staging buffers, we can use UpdateSubresource to write the data.
     DAWN_TRY(EnsureDataInitializedAsDestination(commandContext, offset, size));
     return WriteInternal(commandContext, offset, data, size);
 }
 
-MaybeError Buffer::WriteInternal(CommandRecordingContext* commandContext,
+MaybeError Buffer::WriteInternal(const ScopedCommandRecordingContext* commandContext,
                                  uint64_t offset,
                                  const void* data,
                                  size_t size) {
@@ -504,7 +525,7 @@
 
     // Map the buffer if it is possible, so WriteInternal() can write the mapped memory directly.
     ScopedMap scopedMap;
-    DAWN_TRY_ASSIGN(scopedMap, ScopedMap::Create(this));
+    DAWN_TRY_ASSIGN(scopedMap, ScopedMap::Create(commandContext, this));
 
     if (scopedMap.GetMappedData()) {
         memcpy(scopedMap.GetMappedData() + offset, data, size);
@@ -516,8 +537,6 @@
     // UpdateSubresource can only be used to update non-mappable buffers.
     DAWN_ASSERT(!IsMappable(GetUsage()));
 
-    auto* d3d11DeviceContext = commandContext->GetD3D11DeviceContext4();
-
     if (mD3d11NonConstantBuffer) {
         D3D11_BOX box;
         box.left = offset;
@@ -526,10 +545,10 @@
         box.bottom = 1;
         box.front = 0;
         box.back = 1;
-        d3d11DeviceContext->UpdateSubresource(mD3d11NonConstantBuffer.Get(), /*DstSubresource=*/0,
-                                              &box, data,
-                                              /*SrcRowPitch=*/0,
-                                              /*SrcDepthPitch*/ 0);
+        commandContext->UpdateSubresource(mD3d11NonConstantBuffer.Get(), /*DstSubresource=*/0, &box,
+                                          data,
+                                          /*SrcRowPitch=*/0,
+                                          /*SrcDepthPitch*/ 0);
         if (!mD3d11ConstantBuffer) {
             return {};
         }
@@ -541,7 +560,7 @@
         }
 
         // Copy the modified part of the mD3d11NonConstantBuffer to mD3d11ConstantBuffer.
-        d3d11DeviceContext->CopySubresourceRegion(
+        commandContext->CopySubresourceRegion(
             mD3d11ConstantBuffer.Get(), /*DstSubresource=*/0, /*DstX=*/offset,
             /*DstY=*/0,
             /*DstZ=*/0, mD3d11NonConstantBuffer.Get(), /*SrcSubresource=*/0, &box);
@@ -554,17 +573,17 @@
     // For a full size write, UpdateSubresource() can be used to update mD3d11ConstantBuffer.
     if (size == GetSize() && offset == 0) {
         if (size == mAllocatedSize) {
-            d3d11DeviceContext->UpdateSubresource(mD3d11ConstantBuffer.Get(), /*DstSubresource=*/0,
-                                                  nullptr, data,
-                                                  /*SrcRowPitch=*/size,
-                                                  /*SrcDepthPitch*/ 0);
+            commandContext->UpdateSubresource(mD3d11ConstantBuffer.Get(), /*DstSubresource=*/0,
+                                              nullptr, data,
+                                              /*SrcRowPitch=*/size,
+                                              /*SrcDepthPitch*/ 0);
         } else {
             std::vector<uint8_t> allocatedData(mAllocatedSize, 0);
             std::memcpy(allocatedData.data(), data, size);
-            d3d11DeviceContext->UpdateSubresource(mD3d11ConstantBuffer.Get(), /*DstSubresource=*/0,
-                                                  nullptr, allocatedData.data(),
-                                                  /*SrcRowPitch=*/mAllocatedSize,
-                                                  /*SrcDepthPitch*/ 0);
+            commandContext->UpdateSubresource(mD3d11ConstantBuffer.Get(), /*DstSubresource=*/0,
+                                              nullptr, allocatedData.data(),
+                                              /*SrcRowPitch=*/mAllocatedSize,
+                                              /*SrcDepthPitch*/ 0);
         }
         return {};
     }
@@ -577,7 +596,8 @@
     descriptor.mappedAtCreation = false;
     descriptor.label = "DawnWriteStagingBuffer";
     Ref<BufferBase> stagingBuffer;
-    DAWN_TRY_ASSIGN(stagingBuffer, GetDevice()->CreateBuffer(&descriptor));
+    DAWN_TRY_ASSIGN(stagingBuffer,
+                    Buffer::Create(ToBackend(GetDevice()), &descriptor, commandContext));
 
     DAWN_TRY(ToBackend(stagingBuffer)->WriteInternal(commandContext, 0, data, size));
 
@@ -586,7 +606,7 @@
 }
 
 // static
-MaybeError Buffer::Copy(CommandRecordingContext* commandContext,
+MaybeError Buffer::Copy(const ScopedCommandRecordingContext* commandContext,
                         Buffer* source,
                         uint64_t sourceOffset,
                         size_t size,
@@ -601,7 +621,7 @@
 }
 
 // static
-MaybeError Buffer::CopyInternal(CommandRecordingContext* commandContext,
+MaybeError Buffer::CopyInternal(const ScopedCommandRecordingContext* commandContext,
                                 Buffer* source,
                                 uint64_t sourceOffset,
                                 size_t size,
@@ -620,7 +640,7 @@
     DAWN_ASSERT(d3d11SourceBuffer);
 
     if (destination->mD3d11NonConstantBuffer) {
-        commandContext->GetD3D11DeviceContext4()->CopySubresourceRegion(
+        commandContext->CopySubresourceRegion(
             destination->mD3d11NonConstantBuffer.Get(), /*DstSubresource=*/0,
             /*DstX=*/destinationOffset,
             /*DstY=*/0,
@@ -634,7 +654,7 @@
     }
 
     if (destination->mD3d11ConstantBuffer) {
-        commandContext->GetD3D11DeviceContext4()->CopySubresourceRegion(
+        commandContext->CopySubresourceRegion(
             destination->mD3d11ConstantBuffer.Get(), /*DstSubresource=*/0,
             /*DstX=*/destinationOffset,
             /*DstY=*/0,
@@ -644,23 +664,27 @@
     return {};
 }
 
-ResultOrError<Buffer::ScopedMap> Buffer::ScopedMap::Create(Buffer* buffer) {
+ResultOrError<Buffer::ScopedMap> Buffer::ScopedMap::Create(
+    const ScopedCommandRecordingContext* commandContext,
+    Buffer* buffer) {
     if (!IsMappable(buffer->GetUsage())) {
-        return ScopedMap(nullptr, /*needsUnmap=*/false);
+        return ScopedMap();
     }
 
     if (buffer->mMappedData) {
-        return ScopedMap(buffer, /*needsUnmap=*/false);
+        return ScopedMap(commandContext, buffer, /*needsUnmap=*/false);
     }
 
-    DAWN_TRY(buffer->MapInternal());
-    return ScopedMap(buffer, /*needsUnmap=*/true);
+    DAWN_TRY(buffer->MapInternal(commandContext));
+    return ScopedMap(commandContext, buffer, /*needsUnmap=*/true);
 }
 
 Buffer::ScopedMap::ScopedMap() = default;
 
-Buffer::ScopedMap::ScopedMap(Buffer* buffer, bool needsUnmap)
-    : mBuffer(buffer), mNeedsUnmap(needsUnmap) {}
+Buffer::ScopedMap::ScopedMap(const ScopedCommandRecordingContext* commandContext,
+                             Buffer* buffer,
+                             bool needsUnmap)
+    : mCommandContext(commandContext), mBuffer(buffer), mNeedsUnmap(needsUnmap) {}
 
 Buffer::ScopedMap::~ScopedMap() {
     Reset();
@@ -672,6 +696,7 @@
 
 Buffer::ScopedMap& Buffer::ScopedMap::operator=(Buffer::ScopedMap&& other) {
     Reset();
+    mCommandContext = other.mCommandContext;
     mBuffer = other.mBuffer;
     mNeedsUnmap = other.mNeedsUnmap;
     other.mBuffer = nullptr;
@@ -681,8 +706,9 @@
 
 void Buffer::ScopedMap::Reset() {
     if (mNeedsUnmap) {
-        mBuffer->UnmapInternal();
+        mBuffer->UnmapInternal(mCommandContext);
     }
+    mCommandContext = nullptr;
     mBuffer = nullptr;
     mNeedsUnmap = false;
 }
diff --git a/src/dawn/native/d3d11/BufferD3D11.h b/src/dawn/native/d3d11/BufferD3D11.h
index 3e5f205..36e577c 100644
--- a/src/dawn/native/d3d11/BufferD3D11.h
+++ b/src/dawn/native/d3d11/BufferD3D11.h
@@ -36,19 +36,23 @@
 
 namespace dawn::native::d3d11 {
 
-class CommandRecordingContext;
 class Device;
+class ScopedCommandRecordingContext;
 
 class Buffer final : public BufferBase {
   public:
-    static ResultOrError<Ref<Buffer>> Create(Device* device, const BufferDescriptor* descriptor);
+    static ResultOrError<Ref<Buffer>> Create(Device* device,
+                                             const BufferDescriptor* descriptor,
+                                             const ScopedCommandRecordingContext* commandContext);
 
-    MaybeError EnsureDataInitialized(CommandRecordingContext* commandContext);
-    MaybeError EnsureDataInitializedAsDestination(CommandRecordingContext* commandContext,
-                                                  uint64_t offset,
-                                                  uint64_t size);
-    MaybeError EnsureDataInitializedAsDestination(CommandRecordingContext* commandContext,
-                                                  const CopyTextureToBufferCmd* copy);
+    MaybeError EnsureDataInitialized(const ScopedCommandRecordingContext* commandContext);
+    MaybeError EnsureDataInitializedAsDestination(
+        const ScopedCommandRecordingContext* commandContext,
+        uint64_t offset,
+        uint64_t size);
+    MaybeError EnsureDataInitializedAsDestination(
+        const ScopedCommandRecordingContext* commandContext,
+        const CopyTextureToBufferCmd* copy);
 
     // Dawn API
     void SetLabelImpl() override;
@@ -59,23 +63,23 @@
     // it will be synced with mD3d11NonConstantBuffer before binding it to the constant buffer slot.
     void MarkMutated();
     // Update content of the mD3d11ConstantBuffer from mD3d11NonConstantBuffer if needed.
-    void EnsureConstantBufferIsUpdated(CommandRecordingContext* commandContext);
+    void EnsureConstantBufferIsUpdated(const ScopedCommandRecordingContext* commandContext);
     ResultOrError<ComPtr<ID3D11ShaderResourceView>> CreateD3D11ShaderResourceView(
         uint64_t offset,
         uint64_t size) const;
     ResultOrError<ComPtr<ID3D11UnorderedAccessView1>> CreateD3D11UnorderedAccessView1(
         uint64_t offset,
         uint64_t size) const;
-    MaybeError Clear(CommandRecordingContext* commandContext,
+    MaybeError Clear(const ScopedCommandRecordingContext* commandContext,
                      uint8_t clearValue,
                      uint64_t offset,
                      uint64_t size);
-    MaybeError Write(CommandRecordingContext* commandContext,
+    MaybeError Write(const ScopedCommandRecordingContext* commandContext,
                      uint64_t offset,
                      const void* data,
                      size_t size);
 
-    static MaybeError Copy(CommandRecordingContext* commandContext,
+    static MaybeError Copy(const ScopedCommandRecordingContext* commandContext,
                            Buffer* source,
                            uint64_t sourceOffset,
                            size_t size,
@@ -86,7 +90,8 @@
       public:
         // Map buffer and return a ScopedMap object. If the buffer is not mappable,
         // scopedMap.GetMappedData() will return nullptr.
-        static ResultOrError<ScopedMap> Create(Buffer* buffer);
+        static ResultOrError<ScopedMap> Create(const ScopedCommandRecordingContext* commandContext,
+                                               Buffer* buffer);
 
         ScopedMap();
         ~ScopedMap();
@@ -99,10 +104,12 @@
         void Reset();
 
       private:
-        ScopedMap(Buffer* buffer, bool needsUnmap);
+        ScopedMap(const ScopedCommandRecordingContext* commandContext,
+                  Buffer* buffer,
+                  bool needsUnmap);
 
+        const ScopedCommandRecordingContext* mCommandContext = nullptr;
         Buffer* mBuffer = nullptr;
-
         // Whether the buffer needs to be unmapped when the ScopedMap object is destroyed.
         bool mNeedsUnmap = false;
     };
@@ -112,7 +119,8 @@
 
     ~Buffer() override;
 
-    MaybeError Initialize(bool mappedAtCreation);
+    MaybeError Initialize(bool mappedAtCreation,
+                          const ScopedCommandRecordingContext* commandContext);
     MaybeError MapAsyncImpl(wgpu::MapMode mode, size_t offset, size_t size) override;
     void UnmapImpl() override;
     void DestroyImpl() override;
@@ -120,22 +128,22 @@
     MaybeError MapAtCreationImpl() override;
     void* GetMappedPointer() override;
 
-    MaybeError MapInternal();
-    void UnmapInternal();
+    MaybeError MapInternal(const ScopedCommandRecordingContext* commandContext);
+    void UnmapInternal(const ScopedCommandRecordingContext* commandContext);
 
-    MaybeError InitializeToZero(CommandRecordingContext* commandContext);
+    MaybeError InitializeToZero(const ScopedCommandRecordingContext* commandContext);
     // Clear the buffer without checking if the buffer is initialized.
-    MaybeError ClearInternal(CommandRecordingContext* commandContext,
+    MaybeError ClearInternal(const ScopedCommandRecordingContext* commandContext,
                              uint8_t clearValue,
                              uint64_t offset = 0,
                              uint64_t size = 0);
     // Write the buffer without checking if the buffer is initialized.
-    MaybeError WriteInternal(CommandRecordingContext* commandContext,
+    MaybeError WriteInternal(const ScopedCommandRecordingContext* commandContext,
                              uint64_t bufferOffset,
                              const void* data,
                              size_t size);
     // Copy the buffer without checking if the buffer is initialized.
-    static MaybeError CopyInternal(CommandRecordingContext* commandContext,
+    static MaybeError CopyInternal(const ScopedCommandRecordingContext* commandContext,
                                    Buffer* source,
                                    uint64_t sourceOffset,
                                    size_t size,
diff --git a/src/dawn/native/d3d11/CommandBufferD3D11.cpp b/src/dawn/native/d3d11/CommandBufferD3D11.cpp
index 065baac..bc12b05 100644
--- a/src/dawn/native/d3d11/CommandBufferD3D11.cpp
+++ b/src/dawn/native/d3d11/CommandBufferD3D11.cpp
@@ -41,6 +41,7 @@
 #include "dawn/native/d3d/D3DError.h"
 #include "dawn/native/d3d11/BindGroupTrackerD3D11.h"
 #include "dawn/native/d3d11/BufferD3D11.h"
+#include "dawn/native/d3d11/CommandRecordingContextD3D11.h"
 #include "dawn/native/d3d11/ComputePipelineD3D11.h"
 #include "dawn/native/d3d11/DeviceD3D11.h"
 #include "dawn/native/d3d11/Forward.h"
@@ -67,7 +68,7 @@
 
 class VertexBufferTracker {
   public:
-    explicit VertexBufferTracker(CommandRecordingContext* commandContext)
+    explicit VertexBufferTracker(const ScopedSwapStateCommandRecordingContext* commandContext)
         : mCommandContext(commandContext) {}
 
     ~VertexBufferTracker() {
@@ -100,14 +101,16 @@
     }
 
   private:
-    CommandRecordingContext* const mCommandContext;
+    const ScopedSwapStateCommandRecordingContext* mCommandContext;
     const RenderPipeline* mLastAppliedRenderPipeline = nullptr;
     ityp::array<VertexBufferSlot, ID3D11Buffer*, kMaxVertexBuffers> mD3D11Buffers = {};
     ityp::array<VertexBufferSlot, UINT, kMaxVertexBuffers> mStrides = {};
     ityp::array<VertexBufferSlot, UINT, kMaxVertexBuffers> mOffsets = {};
 };
 
-MaybeError SynchronizeTextureBeforeUse(Texture* texture, CommandRecordingContext* commandContext) {
+MaybeError SynchronizeTextureBeforeUse(
+    Texture* texture,
+    const ScopedSwapStateCommandRecordingContext* commandContext) {
     SharedTextureMemoryBase::PendingFenceList fences;
     SharedTextureMemoryContents* contents = texture->GetSharedTextureMemoryContents();
     if (contents == nullptr) {
@@ -133,14 +136,8 @@
     return AcquireRef(new CommandBuffer(encoder, descriptor));
 }
 
-MaybeError CommandBuffer::Execute() {
-    CommandRecordingContext* commandContext = ToBackend(GetDevice())->GetPendingCommandContext();
-
-    // Mark a critical section for this entire scope to minimize the cost of mutex acquire/release
-    // when ID3D11Multithread protection is enabled.
-    auto scopedCriticalSection = commandContext->EnterScopedCriticalSection();
-
-    auto LazyClearSyncScope = [commandContext](const SyncScopeResourceUsage& scope) -> MaybeError {
+MaybeError CommandBuffer::Execute(const ScopedSwapStateCommandRecordingContext* commandContext) {
+    auto LazyClearSyncScope = [&commandContext](const SyncScopeResourceUsage& scope) -> MaybeError {
         for (size_t i = 0; i < scope.textures.size(); i++) {
             Texture* texture = ToBackend(scope.textures[i]);
 
@@ -252,7 +249,8 @@
                                     ComputeRequiredBytesInCopy(blockInfo, copy->copySize,
                                                                src.bytesPerRow, src.rowsPerImage));
                     desc.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::MapRead;
-                    DAWN_TRY_ASSIGN(stagingBuffer, GetDevice()->CreateBuffer(&desc));
+                    DAWN_TRY_ASSIGN(stagingBuffer,
+                                    Buffer::Create(ToBackend(GetDevice()), &desc, commandContext));
 
                     DAWN_TRY(Buffer::Copy(commandContext, buffer, src.offset,
                                           stagingBuffer->GetSize(), ToBackend(stagingBuffer.Get()),
@@ -262,7 +260,7 @@
                 }
 
                 Buffer::ScopedMap scopedMap;
-                DAWN_TRY_ASSIGN(scopedMap, Buffer::ScopedMap::Create(buffer));
+                DAWN_TRY_ASSIGN(scopedMap, Buffer::ScopedMap::Create(commandContext, buffer));
                 DAWN_TRY(buffer->EnsureDataInitialized(commandContext));
 
                 Texture* texture = ToBackend(dst.texture.Get());
@@ -297,7 +295,7 @@
 
                 Buffer* buffer = ToBackend(dst.buffer.Get());
                 Buffer::ScopedMap scopedDstMap;
-                DAWN_TRY_ASSIGN(scopedDstMap, Buffer::ScopedMap::Create(buffer));
+                DAWN_TRY_ASSIGN(scopedDstMap, Buffer::ScopedMap::Create(commandContext, buffer));
 
                 DAWN_TRY(buffer->EnsureDataInitializedAsDestination(commandContext, copy));
 
@@ -392,7 +390,8 @@
     return {};
 }
 
-MaybeError CommandBuffer::ExecuteComputePass(CommandRecordingContext* commandContext) {
+MaybeError CommandBuffer::ExecuteComputePass(
+    const ScopedSwapStateCommandRecordingContext* commandContext) {
     ComputePipeline* lastPipeline = nullptr;
     BindGroupTracker bindGroupTracker(commandContext, /*isRenderPass=*/false);
 
@@ -478,8 +477,9 @@
     DAWN_UNREACHABLE();
 }
 
-MaybeError CommandBuffer::ExecuteRenderPass(BeginRenderPassCmd* renderPass,
-                                            CommandRecordingContext* commandContext) {
+MaybeError CommandBuffer::ExecuteRenderPass(
+    BeginRenderPassCmd* renderPass,
+    const ScopedSwapStateCommandRecordingContext* commandContext) {
     auto* d3d11DeviceContext = commandContext->GetD3D11DeviceContext4();
 
     // Hold ID3D11RenderTargetView ComPtr to make attachments alive.
@@ -814,9 +814,10 @@
     DAWN_UNREACHABLE();
 }
 
-void CommandBuffer::HandleDebugCommands(CommandRecordingContext* commandContext,
-                                        CommandIterator* iter,
-                                        Command command) {
+void CommandBuffer::HandleDebugCommands(
+    const ScopedSwapStateCommandRecordingContext* commandContext,
+    CommandIterator* iter,
+    Command command) {
     switch (command) {
         case Command::InsertDebugMarker: {
             InsertDebugMarkerCmd* cmd = iter->NextCommand<InsertDebugMarkerCmd>();
@@ -842,10 +843,11 @@
     }
 }
 
-MaybeError CommandBuffer::RecordFirstIndexOffset(RenderPipeline* renderPipeline,
-                                                 CommandRecordingContext* commandContext,
-                                                 uint32_t firstVertex,
-                                                 uint32_t firstInstance) {
+MaybeError CommandBuffer::RecordFirstIndexOffset(
+    RenderPipeline* renderPipeline,
+    const ScopedSwapStateCommandRecordingContext* commandContext,
+    uint32_t firstVertex,
+    uint32_t firstInstance) {
     constexpr uint32_t kFirstVertexOffset = 0;
     constexpr uint32_t kFirstInstanceOffset = 1;
 
@@ -859,9 +861,10 @@
     return commandContext->FlushUniformBuffer();
 }
 
-MaybeError CommandBuffer::RecordNumWorkgroupsForDispatch(ComputePipeline* computePipeline,
-                                                         CommandRecordingContext* commandContext,
-                                                         DispatchCmd* dispatchCmd) {
+MaybeError CommandBuffer::RecordNumWorkgroupsForDispatch(
+    ComputePipeline* computePipeline,
+    const ScopedSwapStateCommandRecordingContext* commandContext,
+    DispatchCmd* dispatchCmd) {
     if (!computePipeline->UsesNumWorkgroups()) {
         // Workgroup size is not used in shader, so we don't need to update the uniform buffer. The
         // original value in the uniform buffer will not be used, so we don't need to clear it.
diff --git a/src/dawn/native/d3d11/CommandBufferD3D11.h b/src/dawn/native/d3d11/CommandBufferD3D11.h
index c0553d3..beb201a 100644
--- a/src/dawn/native/d3d11/CommandBufferD3D11.h
+++ b/src/dawn/native/d3d11/CommandBufferD3D11.h
@@ -38,33 +38,34 @@
 
 namespace dawn::native::d3d11 {
 
-class CommandRecordingContext;
 class ComputePipeline;
 class RenderPipeline;
+class ScopedSwapStateCommandRecordingContext;
 
 class CommandBuffer final : public CommandBufferBase {
   public:
     static Ref<CommandBuffer> Create(CommandEncoder* encoder,
                                      const CommandBufferDescriptor* descriptor);
-    MaybeError Execute();
+    MaybeError Execute(const ScopedSwapStateCommandRecordingContext* commandContext);
 
   private:
     using CommandBufferBase::CommandBufferBase;
 
-    MaybeError ExecuteComputePass(CommandRecordingContext* commandContext);
+    MaybeError ExecuteComputePass(const ScopedSwapStateCommandRecordingContext* commandContext);
     MaybeError ExecuteRenderPass(BeginRenderPassCmd* renderPass,
-                                 CommandRecordingContext* commandContext);
-    void HandleDebugCommands(CommandRecordingContext* commandContext,
+                                 const ScopedSwapStateCommandRecordingContext* commandContext);
+    void HandleDebugCommands(const ScopedSwapStateCommandRecordingContext* commandContext,
                              CommandIterator* iter,
                              Command command);
 
     MaybeError RecordFirstIndexOffset(RenderPipeline* renderPipeline,
-                                      CommandRecordingContext* commandContext,
+                                      const ScopedSwapStateCommandRecordingContext* commandContext,
                                       uint32_t firstVertex,
                                       uint32_t firstInstance);
-    MaybeError RecordNumWorkgroupsForDispatch(ComputePipeline* computePipeline,
-                                              CommandRecordingContext* commandContext,
-                                              DispatchCmd* dispatchCmd);
+    MaybeError RecordNumWorkgroupsForDispatch(
+        ComputePipeline* computePipeline,
+        const ScopedSwapStateCommandRecordingContext* commandContext,
+        DispatchCmd* dispatchCmd);
 };
 
 }  // namespace dawn::native::d3d11
diff --git a/src/dawn/native/d3d11/CommandRecordingContextD3D11.cpp b/src/dawn/native/d3d11/CommandRecordingContextD3D11.cpp
index 475e2aa..6db437b 100644
--- a/src/dawn/native/d3d11/CommandRecordingContextD3D11.cpp
+++ b/src/dawn/native/d3d11/CommandRecordingContextD3D11.cpp
@@ -34,19 +34,167 @@
 #include "dawn/native/d3d11/BufferD3D11.h"
 #include "dawn/native/d3d11/DeviceD3D11.h"
 #include "dawn/native/d3d11/Forward.h"
+#include "dawn/native/d3d11/PhysicalDeviceD3D11.h"
 #include "dawn/native/d3d11/PipelineLayoutD3D11.h"
 #include "dawn/platform/DawnPlatform.h"
 #include "dawn/platform/tracing/TraceEvent.h"
 
 namespace dawn::native::d3d11 {
 
+ScopedCommandRecordingContext::ScopedCommandRecordingContext(
+    CommandRecordingContext* commandContext)
+    : mCommandContext(commandContext), mD3D11Multithread(mCommandContext->mD3D11Multithread) {
+    DAWN_ASSERT(!mCommandContext->mScopedAccessed);
+    mCommandContext->mScopedAccessed = true;
+
+    if (mD3D11Multithread) {
+        mD3D11Multithread->Enter();
+    }
+}
+
+ScopedCommandRecordingContext::~ScopedCommandRecordingContext() {
+    DAWN_ASSERT(mCommandContext->mScopedAccessed);
+    mCommandContext->mScopedAccessed = false;
+
+    if (mD3D11Multithread) {
+        mD3D11Multithread->Leave();
+    }
+}
+
+Device* ScopedCommandRecordingContext::GetDevice() const {
+    return mCommandContext->mDevice.Get();
+}
+
+void ScopedCommandRecordingContext::UpdateSubresource(ID3D11Resource* pDstResource,
+                                                      UINT DstSubresource,
+                                                      const D3D11_BOX* pDstBox,
+                                                      const void* pSrcData,
+                                                      UINT SrcRowPitch,
+                                                      UINT SrcDepthPitch) const {
+    mCommandContext->mD3D11DeviceContext4->UpdateSubresource(pDstResource, DstSubresource, pDstBox,
+                                                             pSrcData, SrcRowPitch, SrcDepthPitch);
+}
+
+void ScopedCommandRecordingContext::CopyResource(ID3D11Resource* pDstResource,
+                                                 ID3D11Resource* pSrcResource) const {
+    mCommandContext->mD3D11DeviceContext4->CopyResource(pDstResource, pSrcResource);
+}
+
+void ScopedCommandRecordingContext::CopySubresourceRegion(ID3D11Resource* pDstResource,
+                                                          UINT DstSubresource,
+                                                          UINT DstX,
+                                                          UINT DstY,
+                                                          UINT DstZ,
+                                                          ID3D11Resource* pSrcResource,
+                                                          UINT SrcSubresource,
+                                                          const D3D11_BOX* pSrcBox) const {
+    mCommandContext->mD3D11DeviceContext4->CopySubresourceRegion(
+        pDstResource, DstSubresource, DstX, DstY, DstZ, pSrcResource, SrcSubresource, pSrcBox);
+}
+
+void ScopedCommandRecordingContext::ClearRenderTargetView(ID3D11RenderTargetView* pRenderTargetView,
+                                                          const FLOAT ColorRGBA[4]) const {
+    mCommandContext->mD3D11DeviceContext4->ClearRenderTargetView(pRenderTargetView, ColorRGBA);
+}
+
+void ScopedCommandRecordingContext::ClearDepthStencilView(ID3D11DepthStencilView* pDepthStencilView,
+                                                          UINT ClearFlags,
+                                                          FLOAT Depth,
+                                                          UINT8 Stencil) const {
+    mCommandContext->mD3D11DeviceContext4->ClearDepthStencilView(pDepthStencilView, ClearFlags,
+                                                                 Depth, Stencil);
+}
+
+HRESULT ScopedCommandRecordingContext::Map(ID3D11Resource* pResource,
+                                           UINT Subresource,
+                                           D3D11_MAP MapType,
+                                           UINT MapFlags,
+                                           D3D11_MAPPED_SUBRESOURCE* pMappedResource) const {
+    return mCommandContext->mD3D11DeviceContext4->Map(pResource, Subresource, MapType, MapFlags,
+                                                      pMappedResource);
+}
+
+void ScopedCommandRecordingContext::Unmap(ID3D11Resource* pResource, UINT Subresource) const {
+    mCommandContext->mD3D11DeviceContext4->Unmap(pResource, Subresource);
+}
+
+HRESULT ScopedCommandRecordingContext::Signal(ID3D11Fence* pFence, UINT64 Value) const {
+    return mCommandContext->mD3D11DeviceContext4->Signal(pFence, Value);
+}
+
+HRESULT ScopedCommandRecordingContext::Wait(ID3D11Fence* pFence, UINT64 Value) const {
+    return mCommandContext->mD3D11DeviceContext4->Wait(pFence, Value);
+}
+
+void ScopedCommandRecordingContext::WriteUniformBuffer(uint32_t offset, uint32_t element) const {
+    DAWN_ASSERT(offset < CommandRecordingContext::kMaxNumBuiltinElements);
+    if (mCommandContext->mUniformBufferData[offset] != element) {
+        mCommandContext->mUniformBufferData[offset] = element;
+        mCommandContext->mUniformBufferDirty = true;
+    }
+}
+
+MaybeError ScopedCommandRecordingContext::FlushUniformBuffer() const {
+    if (mCommandContext->mUniformBufferDirty) {
+        DAWN_TRY(mCommandContext->mUniformBuffer->Write(
+            this, 0, mCommandContext->mUniformBufferData.data(),
+            mCommandContext->mUniformBufferData.size() * sizeof(uint32_t)));
+        mCommandContext->mUniformBufferDirty = false;
+    }
+    return {};
+}
+
+ScopedSwapStateCommandRecordingContext::ScopedSwapStateCommandRecordingContext(
+    CommandRecordingContext* commandContext)
+    : ScopedCommandRecordingContext(commandContext),
+      mSwapContextState(
+          ToBackend(mCommandContext->mDevice->GetPhysicalDevice())->IsSharedD3D11Device()) {
+    if (mSwapContextState) {
+        mCommandContext->mD3D11DeviceContext4->SwapDeviceContextState(
+            mCommandContext->mD3D11DeviceContextState.Get(), &mPreviousState);
+    }
+}
+
+ScopedSwapStateCommandRecordingContext::~ScopedSwapStateCommandRecordingContext() {
+    if (mSwapContextState) {
+        mCommandContext->mD3D11DeviceContext4->SwapDeviceContextState(mPreviousState.Get(),
+                                                                      nullptr);
+    }
+}
+
+ID3D11Device* ScopedSwapStateCommandRecordingContext::GetD3D11Device() const {
+    return mCommandContext->mD3D11Device.Get();
+}
+
+ID3D11DeviceContext4* ScopedSwapStateCommandRecordingContext::GetD3D11DeviceContext4() const {
+    return mCommandContext->mD3D11DeviceContext4.Get();
+}
+
+ID3DUserDefinedAnnotation* ScopedSwapStateCommandRecordingContext::GetD3DUserDefinedAnnotation()
+    const {
+    return mCommandContext->mD3DUserDefinedAnnotation.Get();
+}
+
+Buffer* ScopedSwapStateCommandRecordingContext::GetUniformBuffer() const {
+    return mCommandContext->mUniformBuffer.Get();
+}
+
 MaybeError CommandRecordingContext::Intialize(Device* device) {
     DAWN_ASSERT(!IsOpen());
     DAWN_ASSERT(device);
     mDevice = device;
     mNeedsSubmit = false;
 
-    ID3D11Device* d3d11Device = device->GetD3D11Device();
+    ID3D11Device5* d3d11Device = device->GetD3D11Device5();
+
+    if (ToBackend(device->GetPhysicalDevice())->IsSharedD3D11Device()) {
+        const D3D_FEATURE_LEVEL featureLevels[] = {D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0};
+        DAWN_TRY(CheckHRESULT(
+            d3d11Device->CreateDeviceContextState(
+                /*Flags=*/0, featureLevels, std::size(featureLevels), D3D11_SDK_VERSION,
+                __uuidof(ID3D11Device5), nullptr, &mD3D11DeviceContextState),
+            "D3D11: create device context state"));
+    }
 
     ComPtr<ID3D11DeviceContext> d3d11DeviceContext;
     device->GetD3D11Device()->GetImmediateContext(&d3d11DeviceContext);
@@ -101,28 +249,6 @@
     return {};
 }
 
-ID3D11Device* CommandRecordingContext::GetD3D11Device() const {
-    return mD3D11Device.Get();
-}
-
-ID3D11DeviceContext4* CommandRecordingContext::GetD3D11DeviceContext4() const {
-    DAWN_ASSERT(mDevice->IsLockedByCurrentThreadIfNeeded());
-    return mD3D11DeviceContext4.Get();
-}
-
-ID3DUserDefinedAnnotation* CommandRecordingContext::GetD3DUserDefinedAnnotation() const {
-    return mD3DUserDefinedAnnotation.Get();
-}
-
-Buffer* CommandRecordingContext::GetUniformBuffer() const {
-    return mUniformBuffer.Get();
-}
-
-Device* CommandRecordingContext::GetDevice() const {
-    DAWN_ASSERT(mDevice.Get());
-    return mDevice.Get();
-}
-
 void CommandRecordingContext::Release() {
     if (mIsOpen) {
         DAWN_ASSERT(mDevice->IsLockedByCurrentThreadIfNeeded());
@@ -135,57 +261,10 @@
                                                    &nullBuffer);
         mD3D11DeviceContext4->CSSetConstantBuffers(PipelineLayout::kReservedConstantBufferSlot, 1,
                                                    &nullBuffer);
+        mD3D11DeviceContextState = nullptr;
         mD3D11DeviceContext4 = nullptr;
         mD3D11Device = nullptr;
     }
 }
 
-bool CommandRecordingContext::IsOpen() const {
-    return mIsOpen;
-}
-
-bool CommandRecordingContext::NeedsSubmit() const {
-    return mNeedsSubmit;
-}
-
-void CommandRecordingContext::SetNeedsSubmit() {
-    mNeedsSubmit = true;
-}
-
-CommandRecordingContext::ScopedCriticalSection::ScopedCriticalSection(
-    ComPtr<ID3D11Multithread> d3d11Multithread)
-    : mD3D11Multithread(std::move(d3d11Multithread)) {
-    if (mD3D11Multithread) {
-        mD3D11Multithread->Enter();
-    }
-}
-
-CommandRecordingContext::ScopedCriticalSection::~ScopedCriticalSection() {
-    if (mD3D11Multithread) {
-        mD3D11Multithread->Leave();
-    }
-}
-
-CommandRecordingContext::ScopedCriticalSection
-CommandRecordingContext::EnterScopedCriticalSection() {
-    return ScopedCriticalSection(mD3D11Multithread);
-}
-
-void CommandRecordingContext::WriteUniformBuffer(uint32_t offset, uint32_t element) {
-    DAWN_ASSERT(offset < kMaxNumBuiltinElements);
-    if (mUniformBufferData[offset] != element) {
-        mUniformBufferData[offset] = element;
-        mUniformBufferDirty = true;
-    }
-}
-
-MaybeError CommandRecordingContext::FlushUniformBuffer() {
-    if (mUniformBufferDirty) {
-        DAWN_TRY(mUniformBuffer->Write(this, 0, mUniformBufferData.data(),
-                                       mUniformBufferData.size() * sizeof(uint32_t)));
-        mUniformBufferDirty = false;
-    }
-    return {};
-}
-
 }  // namespace dawn::native::d3d11
diff --git a/src/dawn/native/d3d11/CommandRecordingContextD3D11.h b/src/dawn/native/d3d11/CommandRecordingContextD3D11.h
index 005813e..b92ad4f 100644
--- a/src/dawn/native/d3d11/CommandRecordingContextD3D11.h
+++ b/src/dawn/native/d3d11/CommandRecordingContextD3D11.h
@@ -41,43 +41,22 @@
 class CommandRecordingContext {
   public:
     MaybeError Intialize(Device* device);
-
     void Release();
-    bool IsOpen() const;
-    bool NeedsSubmit() const;
-    void SetNeedsSubmit();
-
     MaybeError ExecuteCommandList(Device* device);
 
-    ID3D11Device* GetD3D11Device() const;
-    ID3D11DeviceContext4* GetD3D11DeviceContext4() const;
-    ID3DUserDefinedAnnotation* GetD3DUserDefinedAnnotation() const;
-    Buffer* GetUniformBuffer() const;
-    Device* GetDevice() const;
-
-    struct ScopedCriticalSection : NonMovable {
-        explicit ScopedCriticalSection(ComPtr<ID3D11Multithread>);
-        ~ScopedCriticalSection();
-
-      private:
-        ComPtr<ID3D11Multithread> mD3D11Multithread;
-    };
-    // Returns a scoped object that marks a critical section using the
-    // ID3D11Multithread Enter and Leave methods. This allows minimizing the
-    // cost of D3D11 multithread protection by allowing a single mutex Acquire
-    // and Release call for an entire set of operations on the immediate context
-    // e.g. when executing command buffers. This only has an effect if the
-    // ImplicitDeviceSynchronization feature is enabled.
-    ScopedCriticalSection EnterScopedCriticalSection();
-
-    // Write the built-in variable value to the uniform buffer.
-    void WriteUniformBuffer(uint32_t offset, uint32_t element);
-    MaybeError FlushUniformBuffer();
+    bool IsOpen() const { return mIsOpen; }
+    bool NeedsSubmit() const { return mNeedsSubmit; }
+    void SetNeedsSubmit() { mNeedsSubmit = true; }
 
   private:
+    friend class ScopedCommandRecordingContext;
+    friend class ScopedSwapStateCommandRecordingContext;
+
+    bool mScopedAccessed = false;
     bool mIsOpen = false;
     bool mNeedsSubmit = false;
     ComPtr<ID3D11Device> mD3D11Device;
+    ComPtr<ID3DDeviceContextState> mD3D11DeviceContextState;
     ComPtr<ID3D11DeviceContext4> mD3D11DeviceContext4;
     ComPtr<ID3D11Multithread> mD3D11Multithread;
     ComPtr<ID3DUserDefinedAnnotation> mD3DUserDefinedAnnotation;
@@ -92,6 +71,71 @@
     Ref<Device> mDevice;
 };
 
+// For using ID3D11DeviceContext methods which don't change device context state.
+class ScopedCommandRecordingContext : NonMovable {
+  public:
+    explicit ScopedCommandRecordingContext(CommandRecordingContext* commandContext);
+    ~ScopedCommandRecordingContext();
+
+    Device* GetDevice() const;
+
+    // Wrapper method which don't depend on context state.
+    void UpdateSubresource(ID3D11Resource* pDstResource,
+                           UINT DstSubresource,
+                           const D3D11_BOX* pDstBox,
+                           const void* pSrcData,
+                           UINT SrcRowPitch,
+                           UINT SrcDepthPitch) const;
+    void CopyResource(ID3D11Resource* pDstResource, ID3D11Resource* pSrcResource) const;
+    void CopySubresourceRegion(ID3D11Resource* pDstResource,
+                               UINT DstSubresource,
+                               UINT DstX,
+                               UINT DstY,
+                               UINT DstZ,
+                               ID3D11Resource* pSrcResource,
+                               UINT SrcSubresource,
+                               const D3D11_BOX* pSrcBox) const;
+    void ClearRenderTargetView(ID3D11RenderTargetView* pRenderTargetView,
+                               const FLOAT ColorRGBA[4]) const;
+    void ClearDepthStencilView(ID3D11DepthStencilView* pDepthStencilView,
+                               UINT ClearFlags,
+                               FLOAT Depth,
+                               UINT8 Stencil) const;
+    HRESULT Map(ID3D11Resource* pResource,
+                UINT Subresource,
+                D3D11_MAP MapType,
+                UINT MapFlags,
+                D3D11_MAPPED_SUBRESOURCE* pMappedResource) const;
+    void Unmap(ID3D11Resource* pResource, UINT Subresource) const;
+    HRESULT Signal(ID3D11Fence* pFence, UINT64 Value) const;
+    HRESULT Wait(ID3D11Fence* pFence, UINT64 Value) const;
+
+    // Write the built-in variable value to the uniform buffer.
+    void WriteUniformBuffer(uint32_t offset, uint32_t element) const;
+    MaybeError FlushUniformBuffer() const;
+
+  protected:
+    CommandRecordingContext* const mCommandContext;
+    ComPtr<ID3D11Multithread> mD3D11Multithread;
+};
+
+// For using ID3D11DeviceContext directly. It swaps and resets ID3DDeviceContextState of
+// ID3D11DeviceContext for a scope. It is needed for sharing ID3D11Device between dawn and ANGLE.
+class ScopedSwapStateCommandRecordingContext : public ScopedCommandRecordingContext {
+  public:
+    explicit ScopedSwapStateCommandRecordingContext(CommandRecordingContext* commandContext);
+    ~ScopedSwapStateCommandRecordingContext();
+
+    ID3D11Device* GetD3D11Device() const;
+    ID3D11DeviceContext4* GetD3D11DeviceContext4() const;
+    ID3DUserDefinedAnnotation* GetD3DUserDefinedAnnotation() const;
+    Buffer* GetUniformBuffer() const;
+
+  private:
+    const bool mSwapContextState;
+    ComPtr<ID3DDeviceContextState> mPreviousState;
+};
+
 }  // namespace dawn::native::d3d11
 
 #endif  // SRC_DAWN_NATIVE_D3D11_COMMANDRECORDINGCONTEXT_D3D11_H_
diff --git a/src/dawn/native/d3d11/ComputePipelineD3D11.cpp b/src/dawn/native/d3d11/ComputePipelineD3D11.cpp
index 774caa3..36ddd90 100644
--- a/src/dawn/native/d3d11/ComputePipelineD3D11.cpp
+++ b/src/dawn/native/d3d11/ComputePipelineD3D11.cpp
@@ -87,7 +87,7 @@
     SetDebugName(ToBackend(GetDevice()), mComputeShader.Get(), "Dawn_ComputePipeline", GetLabel());
 }
 
-void ComputePipeline::ApplyNow(CommandRecordingContext* commandContext) {
+void ComputePipeline::ApplyNow(const ScopedSwapStateCommandRecordingContext* commandContext) {
     auto* d3dDeviceContext = commandContext->GetD3D11DeviceContext4();
     d3dDeviceContext->CSSetShader(mComputeShader.Get(), nullptr, 0);
 }
diff --git a/src/dawn/native/d3d11/ComputePipelineD3D11.h b/src/dawn/native/d3d11/ComputePipelineD3D11.h
index d577196..079cf5e 100644
--- a/src/dawn/native/d3d11/ComputePipelineD3D11.h
+++ b/src/dawn/native/d3d11/ComputePipelineD3D11.h
@@ -34,8 +34,8 @@
 
 namespace dawn::native::d3d11 {
 
-class CommandRecordingContext;
 class Device;
+class ScopedSwapStateCommandRecordingContext;
 
 class ComputePipeline final : public ComputePipelineBase {
   public:
@@ -45,7 +45,7 @@
                                 WGPUCreateComputePipelineAsyncCallback callback,
                                 void* userdata);
 
-    void ApplyNow(CommandRecordingContext* commandContext);
+    void ApplyNow(const ScopedSwapStateCommandRecordingContext* commandContext);
 
     MaybeError Initialize() override;
 
diff --git a/src/dawn/native/d3d11/DeviceD3D11.cpp b/src/dawn/native/d3d11/DeviceD3D11.cpp
index 57480c6..01aa883 100644
--- a/src/dawn/native/d3d11/DeviceD3D11.cpp
+++ b/src/dawn/native/d3d11/DeviceD3D11.cpp
@@ -155,7 +155,7 @@
     return mD3d11Device5.Get();
 }
 
-CommandRecordingContext* Device::GetPendingCommandContext(Device::SubmitMode submitMode) {
+ScopedCommandRecordingContext Device::GetScopedPendingCommandContext(SubmitMode submitMode) {
     // 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
     DAWN_ASSERT(mPendingCommands.IsOpen());
@@ -163,7 +163,21 @@
     if (submitMode == SubmitMode::Normal) {
         mPendingCommands.SetNeedsSubmit();
     }
-    return &mPendingCommands;
+
+    return ScopedCommandRecordingContext(&mPendingCommands);
+}
+
+ScopedSwapStateCommandRecordingContext Device::GetScopedSwapStatePendingCommandContext(
+    SubmitMode submitMode) {
+    // 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
+    DAWN_ASSERT(mPendingCommands.IsOpen());
+
+    if (submitMode == SubmitMode::Normal) {
+        mPendingCommands.SetNeedsSubmit();
+    }
+
+    return ScopedSwapStateCommandRecordingContext(&mPendingCommands);
 }
 
 MaybeError Device::TickImpl() {
@@ -188,11 +202,10 @@
     TRACE_EVENT1(GetPlatform(), General, "D3D11Device::SignalFence", "serial",
                  uint64_t(GetLastSubmittedCommandSerial()));
 
-    CommandRecordingContext* commandContext =
-        GetPendingCommandContext(DeviceBase::SubmitMode::Passive);
-    DAWN_TRY(CheckHRESULT(commandContext->GetD3D11DeviceContext4()->Signal(
-                              mFence.Get(), uint64_t(GetLastSubmittedCommandSerial())),
-                          "D3D11 command queue signal fence"));
+    auto commandContext = GetScopedPendingCommandContext(DeviceBase::SubmitMode::Passive);
+    DAWN_TRY(
+        CheckHRESULT(commandContext.Signal(mFence.Get(), uint64_t(GetLastSubmittedCommandSerial())),
+                     "D3D11 command queue signal fence"));
 
     return {};
 }
@@ -251,7 +264,7 @@
 }
 
 ResultOrError<Ref<BufferBase>> Device::CreateBufferImpl(const BufferDescriptor* descriptor) {
-    return Buffer::Create(this, descriptor);
+    return Buffer::Create(this, descriptor, /*commandContext=*/nullptr);
 }
 
 ResultOrError<Ref<CommandBufferBase>> Device::CreateCommandBuffer(
@@ -380,8 +393,8 @@
     // D3D11 requires that buffers are unmapped before being used in a copy.
     DAWN_TRY(source->Unmap());
 
-    CommandRecordingContext* commandContext = GetPendingCommandContext();
-    return Buffer::Copy(commandContext, ToBackend(source), sourceOffset, size,
+    auto commandContext = GetScopedPendingCommandContext(Device::SubmitMode::Normal);
+    return Buffer::Copy(&commandContext, ToBackend(source), sourceOffset, size,
                         ToBackend(destination), destinationOffset);
 }
 
diff --git a/src/dawn/native/d3d11/DeviceD3D11.h b/src/dawn/native/d3d11/DeviceD3D11.h
index f47a349..d4b1f33 100644
--- a/src/dawn/native/d3d11/DeviceD3D11.h
+++ b/src/dawn/native/d3d11/DeviceD3D11.h
@@ -54,7 +54,9 @@
     ID3D11Device* GetD3D11Device() const;
     ID3D11Device5* GetD3D11Device5() const;
 
-    CommandRecordingContext* GetPendingCommandContext(SubmitMode submitMode = SubmitMode::Normal);
+    ScopedCommandRecordingContext GetScopedPendingCommandContext(SubmitMode submitMode);
+    ScopedSwapStateCommandRecordingContext GetScopedSwapStatePendingCommandContext(
+        SubmitMode submitMode);
 
     const DeviceInfo& GetDeviceInfo() const;
 
diff --git a/src/dawn/native/d3d11/PhysicalDeviceD3D11.cpp b/src/dawn/native/d3d11/PhysicalDeviceD3D11.cpp
index a53a4c5..ecb6524 100644
--- a/src/dawn/native/d3d11/PhysicalDeviceD3D11.cpp
+++ b/src/dawn/native/d3d11/PhysicalDeviceD3D11.cpp
@@ -76,8 +76,12 @@
 
 }  // namespace
 
-PhysicalDevice::PhysicalDevice(Backend* backend, ComPtr<IDXGIAdapter3> hardwareAdapter)
-    : Base(backend, std::move(hardwareAdapter), wgpu::BackendType::D3D11) {}
+PhysicalDevice::PhysicalDevice(Backend* backend,
+                               ComPtr<IDXGIAdapter3> hardwareAdapter,
+                               ComPtr<ID3D11Device> d3d11Device)
+    : Base(backend, std::move(hardwareAdapter), wgpu::BackendType::D3D11),
+      mIsSharedD3D11Device(!!d3d11Device),
+      mD3D11Device(std::move(d3d11Device)) {}
 
 PhysicalDevice::~PhysicalDevice() = default;
 
@@ -102,7 +106,12 @@
 }
 
 ResultOrError<ComPtr<ID3D11Device>> PhysicalDevice::CreateD3D11Device() {
-    ComPtr<ID3D11Device> device = std::move(mD3d11Device);
+    ComPtr<ID3D11Device> device = mD3D11Device;
+
+    if (!mIsSharedD3D11Device) {
+        mD3D11Device = nullptr;
+    }
+
     if (!device) {
         const PlatformFunctions* functions = static_cast<Backend*>(GetBackend())->GetFunctions();
         const D3D_FEATURE_LEVEL featureLevels[] = {D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0};
@@ -123,6 +132,7 @@
             DAWN_TRY(InitializeDebugLayerFilters(device));
         }
     }
+
     return device;
 }
 
@@ -131,10 +141,10 @@
     // D3D11 cannot check for feature support without a device.
     // Create the device to populate the adapter properties then reuse it when needed for actual
     // rendering.
-    DAWN_TRY_ASSIGN(mD3d11Device, CreateD3D11Device());
+    DAWN_TRY_ASSIGN(mD3D11Device, CreateD3D11Device());
 
-    mFeatureLevel = mD3d11Device->GetFeatureLevel();
-    DAWN_TRY_ASSIGN(mDeviceInfo, GatherDeviceInfo(mD3d11Device));
+    mFeatureLevel = mD3D11Device->GetFeatureLevel();
+    DAWN_TRY_ASSIGN(mDeviceInfo, GatherDeviceInfo(mD3D11Device));
 
     // Base::InitializeImpl() cannot distinguish between discrete and integrated GPUs, so we need to
     // overwrite it.
@@ -274,7 +284,7 @@
 // and the subequent call to CreateDevice will return a handle the existing device instead of
 // creating a new one.
 MaybeError PhysicalDevice::ResetInternalDeviceForTestingImpl() {
-    [[maybe_unused]] auto refCount = mD3d11Device.Reset();
+    [[maybe_unused]] auto refCount = mD3D11Device.Reset();
     DAWN_ASSERT(refCount == 0);
     DAWN_TRY(Initialize());
 
diff --git a/src/dawn/native/d3d11/PhysicalDeviceD3D11.h b/src/dawn/native/d3d11/PhysicalDeviceD3D11.h
index 81655bc..ec400bf 100644
--- a/src/dawn/native/d3d11/PhysicalDeviceD3D11.h
+++ b/src/dawn/native/d3d11/PhysicalDeviceD3D11.h
@@ -39,7 +39,9 @@
 
 class PhysicalDevice : public d3d::PhysicalDevice {
   public:
-    PhysicalDevice(Backend* backend, ComPtr<IDXGIAdapter3> hardwareAdapter);
+    PhysicalDevice(Backend* backend,
+                   ComPtr<IDXGIAdapter3> hardwareAdapter,
+                   ComPtr<ID3D11Device> d3d11Device);
     ~PhysicalDevice() override;
 
     // PhysicalDeviceBase Implementation
@@ -51,6 +53,7 @@
     ResultOrError<ComPtr<ID3D11Device>> CreateD3D11Device();
 
     uint32_t GetUAVSlotCount() const { return mUAVSlotCount; }
+    bool IsSharedD3D11Device() const { return mIsSharedD3D11Device; }
 
   private:
     using Base = d3d::PhysicalDevice;
@@ -71,7 +74,9 @@
     FeatureValidationResult ValidateFeatureSupportedWithTogglesImpl(
         wgpu::FeatureName feature,
         const TogglesState& toggles) const override;
-    ComPtr<ID3D11Device> mD3d11Device;
+
+    const bool mIsSharedD3D11Device;
+    ComPtr<ID3D11Device> mD3D11Device;
     D3D_FEATURE_LEVEL mFeatureLevel;
     DeviceInfo mDeviceInfo = {};
     uint32_t mUAVSlotCount;
diff --git a/src/dawn/native/d3d11/QuerySetD3D11.cpp b/src/dawn/native/d3d11/QuerySetD3D11.cpp
index 655d4ca..38cddce 100644
--- a/src/dawn/native/d3d11/QuerySetD3D11.cpp
+++ b/src/dawn/native/d3d11/QuerySetD3D11.cpp
@@ -86,7 +86,7 @@
     d3d11DeviceContext->End(mPredicates[query].Get());
 }
 
-MaybeError QuerySet::Resolve(CommandRecordingContext* commandContext,
+MaybeError QuerySet::Resolve(const ScopedSwapStateCommandRecordingContext* commandContext,
                              uint32_t firstQuery,
                              uint32_t queryCount,
                              Buffer* destination,
diff --git a/src/dawn/native/d3d11/QuerySetD3D11.h b/src/dawn/native/d3d11/QuerySetD3D11.h
index 36a61fd..c366beb 100644
--- a/src/dawn/native/d3d11/QuerySetD3D11.h
+++ b/src/dawn/native/d3d11/QuerySetD3D11.h
@@ -37,7 +37,7 @@
 
 class Device;
 class Buffer;
-class CommandRecordingContext;
+class ScopedSwapStateCommandRecordingContext;
 
 class QuerySet final : public QuerySetBase {
   public:
@@ -46,7 +46,7 @@
 
     void BeginQuery(ID3D11DeviceContext* d3d11DeviceContext, uint32_t query);
     void EndQuery(ID3D11DeviceContext* d3d11DeviceContext, uint32_t query);
-    MaybeError Resolve(CommandRecordingContext* commandContext,
+    MaybeError Resolve(const ScopedSwapStateCommandRecordingContext* commandContext,
                        uint32_t firstQuery,
                        uint32_t queryCount,
                        Buffer* destination,
diff --git a/src/dawn/native/d3d11/QueueD3D11.cpp b/src/dawn/native/d3d11/QueueD3D11.cpp
index a2f7d2f..fae81cd 100644
--- a/src/dawn/native/d3d11/QueueD3D11.cpp
+++ b/src/dawn/native/d3d11/QueueD3D11.cpp
@@ -48,10 +48,14 @@
     // TODO(dawn:1770): figure how if we need to track and restore the state of the immediate device
     // context.
     TRACE_EVENT_BEGIN0(GetDevice()->GetPlatform(), Recording, "CommandBufferD3D11::Execute");
-    for (uint32_t i = 0; i < commandCount; ++i) {
-        DAWN_TRY(ToBackend(commands[i])->Execute());
+    {
+        auto commandContext =
+            device->GetScopedSwapStatePendingCommandContext(Device::SubmitMode::Normal);
+        for (uint32_t i = 0; i < commandCount; ++i) {
+            DAWN_TRY(ToBackend(commands[i])->Execute(&commandContext));
+        }
+        DAWN_TRY(device->ExecutePendingCommandContext());
     }
-    DAWN_TRY(device->ExecutePendingCommandContext());
     TRACE_EVENT_END0(GetDevice()->GetPlatform(), Recording, "CommandBufferD3D11::Execute");
 
     DAWN_TRY(device->NextSerial());
@@ -68,8 +72,9 @@
         return {};
     }
 
-    CommandRecordingContext* commandContext = ToBackend(GetDevice())->GetPendingCommandContext();
-    return ToBackend(buffer)->Write(commandContext, bufferOffset, data, size);
+    Device* device = ToBackend(GetDevice());
+    auto commandContext = device->GetScopedPendingCommandContext(Device::SubmitMode::Normal);
+    return ToBackend(buffer)->Write(&commandContext, bufferOffset, data, size);
 }
 
 MaybeError Queue::WriteTextureImpl(const ImageCopyTexture& destination,
@@ -81,8 +86,8 @@
         return {};
     }
 
-    CommandRecordingContext* commandContext = ToBackend(GetDevice())->GetPendingCommandContext();
-
+    Device* device = ToBackend(GetDevice());
+    auto commandContext = device->GetScopedPendingCommandContext(Device::SubmitMode::Normal);
     TextureCopy textureCopy;
     textureCopy.texture = destination.texture;
     textureCopy.mipLevel = destination.mipLevel;
@@ -93,7 +98,7 @@
 
     Texture* texture = ToBackend(destination.texture);
 
-    return texture->Write(commandContext, subresources, destination.origin, writeSizePixel,
+    return texture->Write(&commandContext, subresources, destination.origin, writeSizePixel,
                           static_cast<const uint8_t*>(data) + dataLayout.offset,
                           dataLayout.bytesPerRow, dataLayout.rowsPerImage);
 }
diff --git a/src/dawn/native/d3d11/RenderPipelineD3D11.cpp b/src/dawn/native/d3d11/RenderPipelineD3D11.cpp
index 440cf2f..8ef9ca4 100644
--- a/src/dawn/native/d3d11/RenderPipelineD3D11.cpp
+++ b/src/dawn/native/d3d11/RenderPipelineD3D11.cpp
@@ -248,7 +248,7 @@
 
 RenderPipeline::~RenderPipeline() = default;
 
-void RenderPipeline::ApplyNow(CommandRecordingContext* commandContext,
+void RenderPipeline::ApplyNow(const ScopedSwapStateCommandRecordingContext* commandContext,
                               const std::array<float, 4>& blendColor,
                               uint32_t stencilReference) {
     auto* d3d11DeviceContext = commandContext->GetD3D11DeviceContext4();
@@ -264,14 +264,15 @@
     ApplyDepthStencilState(commandContext, stencilReference);
 }
 
-void RenderPipeline::ApplyBlendState(CommandRecordingContext* commandContext,
+void RenderPipeline::ApplyBlendState(const ScopedSwapStateCommandRecordingContext* commandContext,
                                      const std::array<float, 4>& blendColor) {
     auto* d3d11DeviceContext = commandContext->GetD3D11DeviceContext4();
     d3d11DeviceContext->OMSetBlendState(mBlendState.Get(), blendColor.data(), GetSampleMask());
 }
 
-void RenderPipeline::ApplyDepthStencilState(CommandRecordingContext* commandContext,
-                                            uint32_t stencilReference) {
+void RenderPipeline::ApplyDepthStencilState(
+    const ScopedSwapStateCommandRecordingContext* commandContext,
+    uint32_t stencilReference) {
     auto* d3d11DeviceContext = commandContext->GetD3D11DeviceContext4();
     d3d11DeviceContext->OMSetDepthStencilState(mDepthStencilState.Get(), stencilReference);
 }
diff --git a/src/dawn/native/d3d11/RenderPipelineD3D11.h b/src/dawn/native/d3d11/RenderPipelineD3D11.h
index c25f4d3..1062346 100644
--- a/src/dawn/native/d3d11/RenderPipelineD3D11.h
+++ b/src/dawn/native/d3d11/RenderPipelineD3D11.h
@@ -36,9 +36,9 @@
 
 namespace dawn::native::d3d11 {
 
-class CommandRecordingContext;
 class Device;
 class PersistentPipelineState;
+class ScopedSwapStateCommandRecordingContext;
 
 class RenderPipeline final : public RenderPipelineBase {
   public:
@@ -49,12 +49,13 @@
                                 WGPUCreateRenderPipelineAsyncCallback callback,
                                 void* userdata);
 
-    void ApplyNow(CommandRecordingContext* commandContext,
+    void ApplyNow(const ScopedSwapStateCommandRecordingContext* commandContext,
                   const std::array<float, 4>& blendColor,
                   uint32_t stencilReference);
-    void ApplyBlendState(CommandRecordingContext* commandContext,
+    void ApplyBlendState(const ScopedSwapStateCommandRecordingContext* commandContext,
                          const std::array<float, 4>& blendColor);
-    void ApplyDepthStencilState(CommandRecordingContext* commandContext, uint32_t stencilReference);
+    void ApplyDepthStencilState(const ScopedSwapStateCommandRecordingContext* commandContext,
+                                uint32_t stencilReference);
 
     bool UsesVertexIndex() const { return mUsesVertexIndex; }
     bool UsesInstanceIndex() const { return mUsesInstanceIndex; }
diff --git a/src/dawn/native/d3d11/TextureD3D11.cpp b/src/dawn/native/d3d11/TextureD3D11.cpp
index 65bd28e..014bdde 100644
--- a/src/dawn/native/d3d11/TextureD3D11.cpp
+++ b/src/dawn/native/d3d11/TextureD3D11.cpp
@@ -338,8 +338,8 @@
     // Staging texture is used internally, so we don't need to clear it.
     if (device->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting) &&
         mKind == Kind::Normal) {
-        CommandRecordingContext* commandContext = device->GetPendingCommandContext();
-        DAWN_TRY(Clear(commandContext, GetAllSubresources(), TextureBase::ClearValue::NonZero));
+        auto commandContext = device->GetScopedPendingCommandContext(Device::SubmitMode::Normal);
+        DAWN_TRY(Clear(&commandContext, GetAllSubresources(), TextureBase::ClearValue::NonZero));
     }
 
     SetLabelImpl();
@@ -360,13 +360,12 @@
     ComPtr<ID3D11Resource> d3d11Texture;
     DAWN_TRY(CheckHRESULT(d3dTexture.As(&d3d11Texture), "Query ID3D11Resource from IUnknown"));
 
-    CommandRecordingContext* commandContext = ToBackend(GetDevice())->GetPendingCommandContext();
-    auto* d3d11DeviceContext = commandContext->GetD3D11DeviceContext4();
+    Device* device = ToBackend(GetDevice());
+    auto commandContext = device->GetScopedPendingCommandContext(Device::SubmitMode::Normal);
     for (Ref<d3d::Fence>& fence : waitFences) {
-        DAWN_TRY(
-            CheckHRESULT(d3d11DeviceContext->Wait(static_cast<Fence*>(fence.Get())->GetD3D11Fence(),
+        DAWN_TRY(CheckHRESULT(commandContext.Wait(static_cast<Fence*>(fence.Get())->GetD3D11Fence(),
                                                   fence->GetFenceValue()),
-                         "ID3D11DeviceContext4::Wait"));
+                              "ID3D11DeviceContext4::Wait"));
     }
     mD3d11Resource = std::move(d3d11Texture);
     SetLabelHelper("Dawn_ExternalTexture");
@@ -479,7 +478,7 @@
     return dsv;
 }
 
-MaybeError Texture::Clear(CommandRecordingContext* commandContext,
+MaybeError Texture::Clear(const ScopedCommandRecordingContext* commandContext,
                           const SubresourceRange& range,
                           TextureBase::ClearValue clearValue) {
     bool isRenderable = GetInternalUsage() & wgpu::TextureUsage::RenderAttachment;
@@ -504,12 +503,10 @@
     return {};
 }
 
-MaybeError Texture::ClearRenderable(CommandRecordingContext* commandContext,
+MaybeError Texture::ClearRenderable(const ScopedCommandRecordingContext* commandContext,
                                     const SubresourceRange& range,
                                     TextureBase::ClearValue clearValue,
                                     const D3D11ClearValue& d3d11ClearValue) {
-    ID3D11DeviceContext* d3d11DeviceContext = commandContext->GetD3D11DeviceContext4();
-
     UINT clearFlags = 0;
     if (GetFormat().HasDepth() && range.aspects & Aspect::Depth) {
         clearFlags |= D3D11_CLEAR_DEPTH;
@@ -539,12 +536,12 @@
                 DAWN_TRY_ASSIGN(d3d11DSV, CreateD3D11DepthStencilView(clearRange,
                                                                       /*depthReadOnly=*/false,
                                                                       /*stencilReadOnly=*/false));
-                d3d11DeviceContext->ClearDepthStencilView(
+                commandContext->ClearDepthStencilView(
                     d3d11DSV.Get(), clearFlags, d3d11ClearValue.depth, d3d11ClearValue.stencil);
             } else {
                 ComPtr<ID3D11RenderTargetView> d3d11RTV;
                 DAWN_TRY_ASSIGN(d3d11RTV, CreateD3D11RenderTargetView(GetFormat(), clearRange));
-                d3d11DeviceContext->ClearRenderTargetView(d3d11RTV.Get(), d3d11ClearValue.color);
+                commandContext->ClearRenderTargetView(d3d11RTV.Get(), d3d11ClearValue.color);
             }
         }
     }
@@ -552,7 +549,7 @@
     return {};
 }
 
-MaybeError Texture::ClearNonRenderable(CommandRecordingContext* commandContext,
+MaybeError Texture::ClearNonRenderable(const ScopedCommandRecordingContext* commandContext,
                                        const SubresourceRange& range,
                                        TextureBase::ClearValue clearValue) {
     const TexelBlockInfo& blockInfo = GetFormat().GetAspectInfo(range.aspects).block;
@@ -595,7 +592,7 @@
     return {};
 }
 
-MaybeError Texture::ClearCompressed(CommandRecordingContext* commandContext,
+MaybeError Texture::ClearCompressed(const ScopedCommandRecordingContext* commandContext,
                                     const SubresourceRange& range,
                                     TextureBase::ClearValue clearValue) {
     DAWN_ASSERT(range.aspects == Aspect::Color);
@@ -667,9 +664,9 @@
             //     RESOURCE_MANIPULATION ERROR #280:COPYSUBRESOURCEREGION_INVALIDSOURCEBOX]
             srcBox.right = physicalSize.width / blockInfo.width;
             srcBox.bottom = physicalSize.height / blockInfo.height;
-            commandContext->GetD3D11DeviceContext4()->CopySubresourceRegion(
-                GetD3D11Resource(), dstSubresource, 0, 0, 0, interimTexture->GetD3D11Resource(),
-                srcSubresource, &srcBox);
+            commandContext->CopySubresourceRegion(GetD3D11Resource(), dstSubresource, 0, 0, 0,
+                                                  interimTexture->GetD3D11Resource(),
+                                                  srcSubresource, &srcBox);
         }
     }
 
@@ -684,8 +681,9 @@
     SetLabelHelper("Dawn_InternalTexture");
 }
 
-MaybeError Texture::EnsureSubresourceContentInitialized(CommandRecordingContext* commandContext,
-                                                        const SubresourceRange& range) {
+MaybeError Texture::EnsureSubresourceContentInitialized(
+    const ScopedCommandRecordingContext* commandContext,
+    const SubresourceRange& range) {
     if (!ToBackend(GetDevice())->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) {
         return {};
     }
@@ -697,7 +695,7 @@
     return {};
 }
 
-MaybeError Texture::Write(CommandRecordingContext* commandContext,
+MaybeError Texture::Write(const ScopedCommandRecordingContext* commandContext,
                           const SubresourceRange& subresources,
                           const Origin3D& origin,
                           const Extent3D& size,
@@ -719,7 +717,7 @@
     return {};
 }
 
-MaybeError Texture::WriteInternal(CommandRecordingContext* commandContext,
+MaybeError Texture::WriteInternal(const ScopedCommandRecordingContext* commandContext,
                                   const SubresourceRange& subresources,
                                   const Origin3D& origin,
                                   const Extent3D& size,
@@ -746,9 +744,8 @@
         dstBox.back = origin.z + size.depthOrArrayLayers;
         uint32_t subresource =
             GetSubresourceIndex(subresources.baseMipLevel, 0, D3D11Aspect(subresources.aspects));
-        commandContext->GetD3D11DeviceContext4()->UpdateSubresource(GetD3D11Resource(), subresource,
-                                                                    &dstBox, data, bytesPerRow,
-                                                                    bytesPerRow * rowsPerImage);
+        commandContext->UpdateSubresource(GetD3D11Resource(), subresource, &dstBox, data,
+                                          bytesPerRow, bytesPerRow * rowsPerImage);
     } else {
         dstBox.front = 0;
         dstBox.back = 1;
@@ -757,8 +754,8 @@
                 GetSubresourceIndex(subresources.baseMipLevel, subresources.baseArrayLayer + layer,
                                     D3D11Aspect(subresources.aspects));
             D3D11_BOX* pDstBox = GetFormat().HasDepthOrStencil() ? nullptr : &dstBox;
-            commandContext->GetD3D11DeviceContext4()->UpdateSubresource(
-                GetD3D11Resource(), subresource, pDstBox, data, bytesPerRow, 0);
+            commandContext->UpdateSubresource(GetD3D11Resource(), subresource, pDstBox, data,
+                                              bytesPerRow, 0);
             data += rowsPerImage * bytesPerRow;
         }
     }
@@ -766,7 +763,7 @@
     return {};
 }
 
-MaybeError Texture::WriteDepthStencilInternal(CommandRecordingContext* commandContext,
+MaybeError Texture::WriteDepthStencilInternal(const ScopedCommandRecordingContext* commandContext,
                                               const SubresourceRange& subresources,
                                               const Origin3D& origin,
                                               const Extent3D& size,
@@ -816,12 +813,11 @@
         DepthStencilAspectLayout(d3d::DXGITextureFormat(GetFormat().format), subresources.aspects);
 
     // Map and write to the staging texture.
-    auto* d3d11DeviceContext = commandContext->GetD3D11DeviceContext4();
     D3D11_MAPPED_SUBRESOURCE mappedResource;
     const uint8_t* pSrcData = data;
     for (uint32_t layer = 0; layer < size.depthOrArrayLayers; ++layer) {
-        DAWN_TRY(CheckHRESULT(d3d11DeviceContext->Map(stagingTexture->GetD3D11Resource(), layer,
-                                                      D3D11_MAP_READ, 0, &mappedResource),
+        DAWN_TRY(CheckHRESULT(commandContext->Map(stagingTexture->GetD3D11Resource(), layer,
+                                                  D3D11_MAP_READ, 0, &mappedResource),
                               "D3D11 map staging texture"));
         uint8_t* pDstData = static_cast<uint8_t*>(mappedResource.pData);
         for (uint32_t y = 0; y < size.height; ++y) {
@@ -836,7 +832,7 @@
             pDstData += mappedResource.RowPitch;
             pSrcData += bytesPerRow;
         }
-        d3d11DeviceContext->Unmap(stagingTexture->GetD3D11Resource(), layer);
+        commandContext->Unmap(stagingTexture->GetD3D11Resource(), layer);
         DAWN_ASSERT(size.height <= rowsPerImage);
         // Skip the padding rows.
         pSrcData += (rowsPerImage - size.height) * bytesPerRow;
@@ -858,7 +854,7 @@
     return {};
 }
 
-MaybeError Texture::ReadStaging(CommandRecordingContext* commandContext,
+MaybeError Texture::ReadStaging(const ScopedCommandRecordingContext* commandContext,
                                 const SubresourceRange& subresources,
                                 const Origin3D& origin,
                                 Extent3D size,
@@ -870,7 +866,6 @@
     DAWN_ASSERT(subresources.baseArrayLayer == 0);
     DAWN_ASSERT(origin.z == 0);
 
-    auto* d3d11DeviceContext = commandContext->GetD3D11DeviceContext4();
     const TexelBlockInfo& blockInfo = GetFormat().GetAspectInfo(subresources.aspects).block;
     const bool hasStencil = GetFormat().HasStencil();
     DAWN_ASSERT(size.width % blockInfo.width == 0);
@@ -884,9 +879,9 @@
             // The Map() will block until the GPU is done with the texture.
             // TODO(dawn:1705): avoid blocking the CPU.
             D3D11_MAPPED_SUBRESOURCE mappedResource;
-            DAWN_TRY(CheckHRESULT(d3d11DeviceContext->Map(GetD3D11Resource(), layer, D3D11_MAP_READ,
-                                                          0, &mappedResource),
-                                  "D3D11 map staging texture"));
+            DAWN_TRY(CheckHRESULT(
+                commandContext->Map(GetD3D11Resource(), layer, D3D11_MAP_READ, 0, &mappedResource),
+                "D3D11 map staging texture"));
 
             uint8_t* pSrcData = static_cast<uint8_t*>(mappedResource.pData);
             uint64_t dstOffset = dstBytesPerRow * dstRowsPerImage * layer;
@@ -922,7 +917,7 @@
                     pSrcData += mappedResource.RowPitch;
                 }
             }
-            d3d11DeviceContext->Unmap(GetD3D11Resource(), layer);
+            commandContext->Unmap(GetD3D11Resource(), layer);
         }
         return {};
     }
@@ -932,9 +927,9 @@
     // The Map() will block until the GPU is done with the texture.
     // TODO(dawn:1705): avoid blocking the CPU.
     D3D11_MAPPED_SUBRESOURCE mappedResource;
-    DAWN_TRY(CheckHRESULT(
-        d3d11DeviceContext->Map(GetD3D11Resource(), 0, D3D11_MAP_READ, 0, &mappedResource),
-        "D3D11 map staging texture"));
+    DAWN_TRY(
+        CheckHRESULT(commandContext->Map(GetD3D11Resource(), 0, D3D11_MAP_READ, 0, &mappedResource),
+                     "D3D11 map staging texture"));
 
     for (uint32_t z = 0; z < size.depthOrArrayLayers; ++z) {
         uint64_t dstOffset = dstBytesPerRow * dstRowsPerImage * z;
@@ -953,12 +948,12 @@
             }
         }
     }
-    d3d11DeviceContext->Unmap(GetD3D11Resource(), 0);
+    commandContext->Unmap(GetD3D11Resource(), 0);
 
     return {};
 }
 
-MaybeError Texture::Read(CommandRecordingContext* commandContext,
+MaybeError Texture::Read(const ScopedCommandRecordingContext* commandContext,
                          const SubresourceRange& subresources,
                          const Origin3D& origin,
                          Extent3D size,
@@ -1000,7 +995,8 @@
 }
 
 // static
-MaybeError Texture::Copy(CommandRecordingContext* commandContext, CopyTextureToTextureCmd* copy) {
+MaybeError Texture::Copy(const ScopedCommandRecordingContext* commandContext,
+                         CopyTextureToTextureCmd* copy) {
     DAWN_ASSERT(copy->copySize.width != 0 && copy->copySize.height != 0 &&
                 copy->copySize.depthOrArrayLayers != 0);
 
@@ -1035,7 +1031,7 @@
 }
 
 // static
-MaybeError Texture::CopyInternal(CommandRecordingContext* commandContext,
+MaybeError Texture::CopyInternal(const ScopedCommandRecordingContext* commandContext,
                                  CopyTextureToTextureCmd* copy) {
     auto& src = copy->source;
     auto& dst = copy->destination;
@@ -1075,7 +1071,7 @@
         uint32_t dstSubresource =
             dst.texture->GetSubresourceIndex(dst.mipLevel, dstSubresources.baseArrayLayer + layer,
                                              D3D11Aspect(dstSubresources.aspects));
-        commandContext->GetD3D11DeviceContext4()->CopySubresourceRegion(
+        commandContext->CopySubresourceRegion(
             ToBackend(dst.texture)->GetD3D11Resource(), dstSubresource, dst.origin.x, dst.origin.y,
             dst.texture->GetDimension() == wgpu::TextureDimension::e3D ? dst.origin.z : 0,
             ToBackend(src.texture)->GetD3D11Resource(), srcSubresource,
@@ -1091,7 +1087,7 @@
 }
 
 ResultOrError<ComPtr<ID3D11ShaderResourceView>> Texture::GetStencilSRV(
-    CommandRecordingContext* commandContext,
+    const ScopedCommandRecordingContext* commandContext,
     const TextureView* view) {
     DAWN_ASSERT(GetFormat().HasStencil());
 
@@ -1138,8 +1134,7 @@
             };
 
             // TODO(dawn:1705): Work out a way of GPU-GPU copy, rather than the CPU-GPU round trip.
-            commandContext->GetDevice()->EmitWarningOnce(
-                "Sampling the stencil component is rather slow now.");
+            GetDevice()->EmitWarningOnce("Sampling the stencil component is rather slow now.");
             DAWN_TRY(Read(commandContext, singleRange, {0, 0, 0}, size, bytesPerRow, rowsPerImage,
                           callback));
 
diff --git a/src/dawn/native/d3d11/TextureD3D11.h b/src/dawn/native/d3d11/TextureD3D11.h
index 8d38484..b21f70e 100644
--- a/src/dawn/native/d3d11/TextureD3D11.h
+++ b/src/dawn/native/d3d11/TextureD3D11.h
@@ -47,9 +47,9 @@
 
 namespace dawn::native::d3d11 {
 
-class CommandRecordingContext;
 class Device;
 class TextureView;
+class ScopedCommandRecordingContext;
 class SharedTextureMemory;
 
 MaybeError ValidateTextureCanBeWrapped(ID3D11Resource* d3d11Resource,
@@ -80,10 +80,11 @@
         const SubresourceRange& singleLevelRange,
         bool depthReadOnly,
         bool stencilReadOnly) const;
-    MaybeError EnsureSubresourceContentInitialized(CommandRecordingContext* commandContext,
-                                                   const SubresourceRange& range);
+    MaybeError EnsureSubresourceContentInitialized(
+        const ScopedCommandRecordingContext* commandContext,
+        const SubresourceRange& range);
 
-    MaybeError Write(CommandRecordingContext* commandContext,
+    MaybeError Write(const ScopedCommandRecordingContext* commandContext,
                      const SubresourceRange& subresources,
                      const Origin3D& origin,
                      const Extent3D& size,
@@ -91,14 +92,15 @@
                      uint32_t bytesPerRow,
                      uint32_t rowsPerImage);
     using ReadCallback = std::function<MaybeError(const uint8_t* data, size_t offset, size_t size)>;
-    MaybeError Read(CommandRecordingContext* commandContext,
+    MaybeError Read(const ScopedCommandRecordingContext* commandContext,
                     const SubresourceRange& subresources,
                     const Origin3D& origin,
                     Extent3D size,
                     uint32_t bytesPerRow,
                     uint32_t rowsPerImage,
                     ReadCallback callback);
-    static MaybeError Copy(CommandRecordingContext* commandContext, CopyTextureToTextureCmd* copy);
+    static MaybeError Copy(const ScopedCommandRecordingContext* commandContext,
+                           CopyTextureToTextureCmd* copy);
 
     ResultOrError<ExecutionSerial> EndAccess() override;
 
@@ -106,7 +108,7 @@
     // sample the stencil component directly. As a workaround we create an internal R8Uint texture,
     // holding the copy of its stencil data, and use the internal texture's SRV instead.
     ResultOrError<ComPtr<ID3D11ShaderResourceView>> GetStencilSRV(
-        CommandRecordingContext* commandContext,
+        const ScopedCommandRecordingContext* commandContext,
         const TextureView* view);
 
   private:
@@ -141,21 +143,21 @@
     void SetLabelImpl() override;
     void DestroyImpl() override;
 
-    MaybeError Clear(CommandRecordingContext* commandContext,
+    MaybeError Clear(const ScopedCommandRecordingContext* commandContext,
                      const SubresourceRange& range,
                      TextureBase::ClearValue clearValue);
-    MaybeError ClearRenderable(CommandRecordingContext* commandContext,
+    MaybeError ClearRenderable(const ScopedCommandRecordingContext* commandContext,
                                const SubresourceRange& range,
                                TextureBase::ClearValue clearValue,
                                const D3D11ClearValue& d3d11ClearValue);
-    MaybeError ClearNonRenderable(CommandRecordingContext* commandContext,
+    MaybeError ClearNonRenderable(const ScopedCommandRecordingContext* commandContext,
                                   const SubresourceRange& range,
                                   TextureBase::ClearValue clearValue);
-    MaybeError ClearCompressed(CommandRecordingContext* commandContext,
+    MaybeError ClearCompressed(const ScopedCommandRecordingContext* commandContext,
                                const SubresourceRange& range,
                                TextureBase::ClearValue clearValue);
 
-    MaybeError ReadStaging(CommandRecordingContext* commandContext,
+    MaybeError ReadStaging(const ScopedCommandRecordingContext* commandContext,
                            const SubresourceRange& subresources,
                            const Origin3D& origin,
                            Extent3D size,
@@ -164,7 +166,7 @@
                            ReadCallback callback);
 
     // Write the texture without the content initialization bookkeeping.
-    MaybeError WriteInternal(CommandRecordingContext* commandContext,
+    MaybeError WriteInternal(const ScopedCommandRecordingContext* commandContext,
                              const SubresourceRange& subresources,
                              const Origin3D& origin,
                              const Extent3D& size,
@@ -173,7 +175,7 @@
                              uint32_t rowsPerImage);
 
     // Write the depth-stencil texture without the content initialization bookkeeping.
-    MaybeError WriteDepthStencilInternal(CommandRecordingContext* commandContext,
+    MaybeError WriteDepthStencilInternal(const ScopedCommandRecordingContext* commandContext,
                                          const SubresourceRange& subresources,
                                          const Origin3D& origin,
                                          const Extent3D& size,
@@ -182,7 +184,7 @@
                                          uint32_t rowsPerImage);
 
     // Copy the textures without the content initialization bookkeeping.
-    static MaybeError CopyInternal(CommandRecordingContext* commandContext,
+    static MaybeError CopyInternal(const ScopedCommandRecordingContext* commandContext,
                                    CopyTextureToTextureCmd* copy);
 
     const Kind mKind = Kind::Normal;