Move ExecutionQueueBase to be a parent of QueueBase.

This changes which class must implement the virtual methods for
ExecutionQueueBase from Device* to Queue*. It adds a couple proxy
methods on the Device for getting serials and changes some other calls
to ExecutionQueueBase to explicitly go through the Queue.

Moving code from backend::Device to backend::Queue will be done in
follow-up CLs.

No functional changes intended.

Bug: dawn:1413
Change-Id: I6cd2b1297897bed28cd183717f4e92d602445ba4
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/136207
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
diff --git a/src/dawn/native/Device.cpp b/src/dawn/native/Device.cpp
index 1bf41ea..1a4d006 100644
--- a/src/dawn/native/Device.cpp
+++ b/src/dawn/native/Device.cpp
@@ -462,8 +462,8 @@
             // Alive is the only state which can have GPU work happening. Wait for all of it to
             // complete before proceeding with destruction.
             // Ignore errors so that we can continue with destruction
-            IgnoreErrors(WaitForIdleForDestruction());
-            AssumeCommandsComplete();
+            IgnoreErrors(mQueue->WaitForIdleForDestruction());
+            mQueue->AssumeCommandsComplete();
             break;
 
         case State::BeingDisconnected:
@@ -481,7 +481,7 @@
             UNREACHABLE();
             break;
     }
-    ASSERT(GetCompletedCommandSerial() == GetLastSubmittedCommandSerial());
+    ASSERT(mQueue->GetCompletedCommandSerial() == GetLastSubmittedCommandSerial());
 
     if (mState != State::BeingCreated) {
         // The GPU timeline is finished.
@@ -489,7 +489,7 @@
         // since they should be complete. This must be done before DestroyImpl() it may
         // relinquish resources that will be freed by backends in the DestroyImpl() call.
         DestroyObjects();
-        mQueue->Tick(GetCompletedCommandSerial());
+        mQueue->Tick(mQueue->GetCompletedCommandSerial());
         // Call TickImpl once last time to clean up resources
         // Ignore errors so that we can continue with destruction
         IgnoreErrors(TickImpl());
@@ -508,7 +508,7 @@
     mInternalPipelineStore = nullptr;
     mExternalTexturePlaceholderView = nullptr;
 
-    AssumeCommandsComplete();
+    mQueue->AssumeCommandsComplete();
 
     // Now that the GPU timeline is empty, destroy the backend device.
     DestroyImpl();
@@ -535,12 +535,12 @@
         // still be executing commands. Force a wait for idle in this case, with State being
         // Disconnected so we can detect this case in WaitForIdleForDestruction.
         if (ErrorInjectorEnabled()) {
-            IgnoreErrors(WaitForIdleForDestruction());
+            IgnoreErrors(mQueue->WaitForIdleForDestruction());
         }
 
         // A real device lost happened. Set the state to disconnected as the device cannot be
         // used. Also tags all commands as completed since the device stopped running.
-        AssumeCommandsComplete();
+        mQueue->AssumeCommandsComplete();
     } else if (!(allowedErrors & type)) {
         // If we receive an error which we did not explicitly allow, assume the backend can't
         // recover and proceed with device destruction. We first wait for all previous commands to
@@ -556,9 +556,9 @@
 
         // Ignore errors so that we can continue with destruction
         // Assume all commands are complete after WaitForIdleForDestruction (because they were)
-        IgnoreErrors(WaitForIdleForDestruction());
+        IgnoreErrors(mQueue->WaitForIdleForDestruction());
         IgnoreErrors(TickImpl());
-        AssumeCommandsComplete();
+        mQueue->AssumeCommandsComplete();
         mState = State::Disconnected;
 
         // Now everything is as if the device was lost.
@@ -771,7 +771,7 @@
     if (HasPendingTasks()) {
         return false;
     }
-    return !HasScheduledCommands();
+    return !mQueue->HasScheduledCommands();
 }
 
 ResultOrError<const Format*> DeviceBase::GetInternalFormat(wgpu::TextureFormat format) const {
@@ -1315,21 +1315,21 @@
 }
 
 MaybeError DeviceBase::Tick() {
-    if (IsLost() || !HasScheduledCommands()) {
+    if (IsLost() || !mQueue->HasScheduledCommands()) {
         return {};
     }
 
     // To avoid overly ticking, we only want to tick when:
     // 1. the last submitted serial has moved beyond the completed serial
     // 2. or the backend still has pending commands to submit.
-    DAWN_TRY(CheckPassedSerials());
+    DAWN_TRY(mQueue->CheckPassedSerials());
     DAWN_TRY(TickImpl());
 
     // TODO(crbug.com/dawn/833): decouple TickImpl from updating the serial so that we can
     // tick the dynamic uploader before the backend resource allocators. This would allow
     // reclaiming resources one tick earlier.
-    mDynamicUploader->Deallocate(GetCompletedCommandSerial());
-    mQueue->Tick(GetCompletedCommandSerial());
+    mDynamicUploader->Deallocate(mQueue->GetCompletedCommandSerial());
+    mQueue->Tick(mQueue->GetCompletedCommandSerial());
 
     return {};
 }
@@ -1994,6 +1994,14 @@
 
 void DeviceBase::SetLabelImpl() {}
 
+ExecutionSerial DeviceBase::GetLastSubmittedCommandSerial() const {
+    return mQueue->GetLastSubmittedCommandSerial();
+}
+
+ExecutionSerial DeviceBase::GetPendingCommandSerial() const {
+    return mQueue->GetPendingCommandSerial();
+}
+
 bool DeviceBase::ShouldDuplicateNumWorkgroupsForDispatchIndirect(
     ComputePipelineBase* computePipeline) const {
     return false;
@@ -2026,7 +2034,7 @@
     DAWN_TRY(
         CopyFromStagingToBufferImpl(source, sourceOffset, destination, destinationOffset, size));
     if (GetDynamicUploader()->ShouldFlush()) {
-        ForceEventualFlushOfCommands();
+        mQueue->ForceEventualFlushOfCommands();
     }
     return {};
 }
@@ -2051,7 +2059,7 @@
     }
 
     if (GetDynamicUploader()->ShouldFlush()) {
-        ForceEventualFlushOfCommands();
+        mQueue->ForceEventualFlushOfCommands();
     }
     return {};
 }
diff --git a/src/dawn/native/Device.h b/src/dawn/native/Device.h
index 827e32c..e294e76 100644
--- a/src/dawn/native/Device.h
+++ b/src/dawn/native/Device.h
@@ -62,7 +62,7 @@
 
 using WGSLExtensionSet = std::unordered_set<std::string>;
 
-class DeviceBase : public RefCountedWithExternalCount, public ExecutionQueueBase {
+class DeviceBase : public RefCountedWithExternalCount {
   public:
     DeviceBase(AdapterBase* adapter,
                const DeviceDescriptor* descriptor,
@@ -447,6 +447,10 @@
     // as late as possible, for example, until the client has explictly issued a submission.
     enum class SubmitMode { Normal, Passive };
 
+    // TODO(dawn:1413): Remove this proxy methods in favor of using the ExecutionQueue directly.
+    ExecutionSerial GetLastSubmittedCommandSerial() const;
+    ExecutionSerial GetPendingCommandSerial() const;
+
   protected:
     // Constructor used only for mocking and testing.
     DeviceBase();
diff --git a/src/dawn/native/ExecutionQueue.h b/src/dawn/native/ExecutionQueue.h
index 2c5c062..4914ef2 100644
--- a/src/dawn/native/ExecutionQueue.h
+++ b/src/dawn/native/ExecutionQueue.h
@@ -47,7 +47,6 @@
     // make all commands look completed.
     void AssumeCommandsComplete();
 
-  protected:
     // Increment mLastSubmittedSerial when we submit the next serial
     void IncrementLastSubmittedCommandSerial();
 
@@ -65,8 +64,8 @@
     // mLastSubmittedSerial tracks the last submitted command serial.
     // During device removal, the serials could be artificially incremented
     // to make it appear as if commands have been compeleted.
-    ExecutionSerial mCompletedSerial = ExecutionSerial(0);
-    ExecutionSerial mLastSubmittedSerial = ExecutionSerial(0);
+    ExecutionSerial mCompletedSerial = kBeginningOfGPUTime;
+    ExecutionSerial mLastSubmittedSerial = kBeginningOfGPUTime;
 
     // Indicates whether the backend has pending commands to be submitted as soon as possible.
     virtual bool HasPendingCommands() const = 0;
diff --git a/src/dawn/native/IntegerTypes.h b/src/dawn/native/IntegerTypes.h
index 18c6ea8..b4645fd 100644
--- a/src/dawn/native/IntegerTypes.h
+++ b/src/dawn/native/IntegerTypes.h
@@ -64,6 +64,7 @@
 // compare its serial with the currently completed serial.
 using ExecutionSerial = TypedInteger<struct QueueSerialT, uint64_t>;
 constexpr ExecutionSerial kMaxExecutionSerial = ExecutionSerial(~uint64_t(0));
+constexpr ExecutionSerial kBeginningOfGPUTime = ExecutionSerial(0);
 
 // An identifier that indicates which Pipeline a BindGroupLayout is compatible with. Pipelines
 // created with a default layout will produce BindGroupLayouts with a non-zero compatibility
diff --git a/src/dawn/native/Queue.cpp b/src/dawn/native/Queue.cpp
index 58e12d8..c31e92d 100644
--- a/src/dawn/native/Queue.cpp
+++ b/src/dawn/native/Queue.cpp
@@ -167,6 +167,10 @@
     MaybeError SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) override {
         UNREACHABLE();
     }
+    bool HasPendingCommands() const override { UNREACHABLE(); }
+    ResultOrError<ExecutionSerial> CheckAndUpdateCompletedSerials() override { UNREACHABLE(); }
+    void ForceEventualFlushOfCommands() override { UNREACHABLE(); }
+    MaybeError WaitForIdleForDestruction() override { UNREACHABLE(); }
 };
 }  // namespace
 
@@ -236,16 +240,16 @@
 
 void QueueBase::TrackTask(std::unique_ptr<TrackTaskCallback> task, ExecutionSerial serial) {
     // If the task depends on a serial which is not submitted yet, force a flush.
-    if (serial > GetDevice()->GetLastSubmittedCommandSerial()) {
-        GetDevice()->ForceEventualFlushOfCommands();
+    if (serial > GetLastSubmittedCommandSerial()) {
+        ForceEventualFlushOfCommands();
     }
 
-    ASSERT(serial <= GetDevice()->GetScheduledWorkDoneSerial());
+    ASSERT(serial <= GetScheduledWorkDoneSerial());
 
     // If the serial indicated command has been completed, the task will be moved to callback task
     // manager.
-    if (serial <= GetDevice()->GetCompletedCommandSerial()) {
-        task->SetFinishedSerial(GetDevice()->GetCompletedCommandSerial());
+    if (serial <= GetCompletedCommandSerial()) {
+        task->SetFinishedSerial(GetCompletedCommandSerial());
         GetDevice()->GetCallbackTaskManager()->AddCallbackTask(std::move(task));
     } else {
         mTasksInFlight.Enqueue(std::move(task), serial);
@@ -253,8 +257,8 @@
 }
 
 void QueueBase::TrackTaskAfterEventualFlush(std::unique_ptr<TrackTaskCallback> task) {
-    GetDevice()->ForceEventualFlushOfCommands();
-    TrackTask(std::move(task), GetDevice()->GetScheduledWorkDoneSerial());
+    ForceEventualFlushOfCommands();
+    TrackTask(std::move(task), GetScheduledWorkDoneSerial());
 }
 
 void QueueBase::Tick(ExecutionSerial finishedSerial) {
diff --git a/src/dawn/native/Queue.h b/src/dawn/native/Queue.h
index 30192a9..d7aa81f 100644
--- a/src/dawn/native/Queue.h
+++ b/src/dawn/native/Queue.h
@@ -20,6 +20,7 @@
 #include "dawn/common/SerialMap.h"
 #include "dawn/native/CallbackTaskManager.h"
 #include "dawn/native/Error.h"
+#include "dawn/native/ExecutionQueue.h"
 #include "dawn/native/Forward.h"
 #include "dawn/native/IntegerTypes.h"
 #include "dawn/native/ObjectBase.h"
@@ -45,7 +46,7 @@
     ExecutionSerial mSerial = kMaxExecutionSerial;
 };
 
-class QueueBase : public ApiObjectBase {
+class QueueBase : public ApiObjectBase, public ExecutionQueueBase {
   public:
     ~QueueBase() override;
 
diff --git a/src/dawn/native/d3d11/DeviceD3D11.cpp b/src/dawn/native/d3d11/DeviceD3D11.cpp
index 05a0ede..740c805 100644
--- a/src/dawn/native/d3d11/DeviceD3D11.cpp
+++ b/src/dawn/native/d3d11/DeviceD3D11.cpp
@@ -152,7 +152,7 @@
 
 MaybeError Device::TickImpl() {
     // Perform cleanup operations to free unused objects
-    [[maybe_unused]] ExecutionSerial completedSerial = GetCompletedCommandSerial();
+    [[maybe_unused]] ExecutionSerial completedSerial = GetQueue()->GetCompletedCommandSerial();
 
     // Check for debug layer messages before executing the command context in case we encounter an
     // error during execution and early out as a result.
@@ -167,7 +167,7 @@
 }
 
 MaybeError Device::NextSerial() {
-    IncrementLastSubmittedCommandSerial();
+    GetQueue()->IncrementLastSubmittedCommandSerial();
 
     TRACE_EVENT1(GetPlatform(), General, "D3D11Device::SignalFence", "serial",
                  uint64_t(GetLastSubmittedCommandSerial()));
@@ -182,12 +182,12 @@
 }
 
 MaybeError Device::WaitForSerial(ExecutionSerial serial) {
-    DAWN_TRY(CheckPassedSerials());
-    if (GetCompletedCommandSerial() < serial) {
+    DAWN_TRY(GetQueue()->CheckPassedSerials());
+    if (GetQueue()->GetCompletedCommandSerial() < serial) {
         DAWN_TRY(CheckHRESULT(mFence->SetEventOnCompletion(uint64_t(serial), mFenceEvent),
                               "D3D11 set event on completion"));
         WaitForSingleObject(mFenceEvent, INFINITE);
-        DAWN_TRY(CheckPassedSerials());
+        DAWN_TRY(GetQueue()->CheckPassedSerials());
     }
     return {};
 }
@@ -203,7 +203,7 @@
         return DAWN_DEVICE_LOST_ERROR("Device lost");
     }
 
-    if (completedSerial <= GetCompletedCommandSerial()) {
+    if (completedSerial <= GetQueue()->GetCompletedCommandSerial()) {
         return ExecutionSerial(0);
     }
 
diff --git a/src/dawn/native/d3d11/DeviceD3D11.h b/src/dawn/native/d3d11/DeviceD3D11.h
index d455079..e3832b9 100644
--- a/src/dawn/native/d3d11/DeviceD3D11.h
+++ b/src/dawn/native/d3d11/DeviceD3D11.h
@@ -60,7 +60,6 @@
         CommandEncoder* encoder,
         const CommandBufferDescriptor* descriptor) override;
     MaybeError TickImpl() override;
-    void ForceEventualFlushOfCommands() override;
     MaybeError CopyFromStagingToBufferImpl(BufferBase* source,
                                            uint64_t sourceOffset,
                                            BufferBase* destination,
@@ -85,6 +84,12 @@
 
     uint32_t GetUAVSlotCount() const;
 
+    // TODO(dawn:1413) move these methods to the d3d11::Queue.
+    void ForceEventualFlushOfCommands();
+    bool HasPendingCommands() const;
+    ResultOrError<ExecutionSerial> CheckAndUpdateCompletedSerials();
+    MaybeError WaitForIdleForDestruction();
+
   private:
     using Base = d3d::Device;
     using Base::Base;
@@ -122,11 +127,8 @@
                                            WGPUCreateRenderPipelineAsyncCallback callback,
                                            void* userdata) override;
     void DestroyImpl() override;
-    MaybeError WaitForIdleForDestruction() override;
-    bool HasPendingCommands() const override;
     MaybeError CheckDebugLayerAndGenerateErrors();
     void AppendDebugLayerMessages(ErrorData* error) override;
-    ResultOrError<ExecutionSerial> CheckAndUpdateCompletedSerials() override;
 
     ComPtr<ID3D11Fence> mFence;
     HANDLE mFenceEvent = nullptr;
diff --git a/src/dawn/native/d3d11/QueueD3D11.cpp b/src/dawn/native/d3d11/QueueD3D11.cpp
index fc1134e..91dae1c 100644
--- a/src/dawn/native/d3d11/QueueD3D11.cpp
+++ b/src/dawn/native/d3d11/QueueD3D11.cpp
@@ -85,4 +85,20 @@
                           dataLayout.bytesPerRow, dataLayout.rowsPerImage);
 }
 
+bool Queue::HasPendingCommands() const {
+    return ToBackend(GetDevice())->HasPendingCommands();
+}
+
+ResultOrError<ExecutionSerial> Queue::CheckAndUpdateCompletedSerials() {
+    return ToBackend(GetDevice())->CheckAndUpdateCompletedSerials();
+}
+
+void Queue::ForceEventualFlushOfCommands() {
+    return ToBackend(GetDevice())->ForceEventualFlushOfCommands();
+}
+
+MaybeError Queue::WaitForIdleForDestruction() {
+    return ToBackend(GetDevice())->WaitForIdleForDestruction();
+}
+
 }  // namespace dawn::native::d3d11
diff --git a/src/dawn/native/d3d11/QueueD3D11.h b/src/dawn/native/d3d11/QueueD3D11.h
index e134dee..c2223ad 100644
--- a/src/dawn/native/d3d11/QueueD3D11.h
+++ b/src/dawn/native/d3d11/QueueD3D11.h
@@ -39,6 +39,11 @@
                                 const void* data,
                                 const TextureDataLayout& dataLayout,
                                 const Extent3D& writeSizePixel) override;
+
+    bool HasPendingCommands() const override;
+    ResultOrError<ExecutionSerial> CheckAndUpdateCompletedSerials() override;
+    void ForceEventualFlushOfCommands() override;
+    MaybeError WaitForIdleForDestruction() override;
 };
 
 }  // namespace dawn::native::d3d11
diff --git a/src/dawn/native/d3d12/DeviceD3D12.cpp b/src/dawn/native/d3d12/DeviceD3D12.cpp
index 7835b1b..6516319 100644
--- a/src/dawn/native/d3d12/DeviceD3D12.cpp
+++ b/src/dawn/native/d3d12/DeviceD3D12.cpp
@@ -100,7 +100,7 @@
     // value.
     mCommandQueue.As(&mD3d12SharingContract);
 
-    DAWN_TRY(CheckHRESULT(mD3d12Device->CreateFence(uint64_t(GetLastSubmittedCommandSerial()),
+    DAWN_TRY(CheckHRESULT(mD3d12Device->CreateFence(uint64_t(kBeginningOfGPUTime),
                                                     D3D12_FENCE_FLAG_SHARED, IID_PPV_ARGS(&mFence)),
                           "D3D12 create fence"));
 
@@ -310,7 +310,7 @@
 
 MaybeError Device::TickImpl() {
     // Perform cleanup operations to free unused objects
-    ExecutionSerial completedSerial = GetCompletedCommandSerial();
+    ExecutionSerial completedSerial = GetQueue()->GetCompletedCommandSerial();
 
     mResourceAllocatorManager->Tick(completedSerial);
     DAWN_TRY(mCommandAllocatorManager->Tick(completedSerial));
@@ -331,7 +331,7 @@
 }
 
 MaybeError Device::NextSerial() {
-    IncrementLastSubmittedCommandSerial();
+    GetQueue()->IncrementLastSubmittedCommandSerial();
 
     TRACE_EVENT1(GetPlatform(), General, "D3D12Device::SignalFence", "serial",
                  uint64_t(GetLastSubmittedCommandSerial()));
@@ -342,12 +342,12 @@
 }
 
 MaybeError Device::WaitForSerial(ExecutionSerial serial) {
-    DAWN_TRY(CheckPassedSerials());
-    if (GetCompletedCommandSerial() < serial) {
+    DAWN_TRY(GetQueue()->CheckPassedSerials());
+    if (GetQueue()->GetCompletedCommandSerial() < serial) {
         DAWN_TRY(CheckHRESULT(mFence->SetEventOnCompletion(uint64_t(serial), mFenceEvent),
                               "D3D12 set event on completion"));
         WaitForSingleObject(mFenceEvent, INFINITE);
-        DAWN_TRY(CheckPassedSerials());
+        DAWN_TRY(GetQueue()->CheckPassedSerials());
     }
     return {};
 }
@@ -363,7 +363,7 @@
         return DAWN_DEVICE_LOST_ERROR("Device lost");
     }
 
-    if (completedSerial <= GetCompletedCommandSerial()) {
+    if (completedSerial <= GetQueue()->GetCompletedCommandSerial()) {
         return ExecutionSerial(0);
     }
 
diff --git a/src/dawn/native/d3d12/DeviceD3D12.h b/src/dawn/native/d3d12/DeviceD3D12.h
index 7db7d37..1db6779 100644
--- a/src/dawn/native/d3d12/DeviceD3D12.h
+++ b/src/dawn/native/d3d12/DeviceD3D12.h
@@ -90,8 +90,6 @@
 
     void ReferenceUntilUnused(ComPtr<IUnknown> object);
 
-    void ForceEventualFlushOfCommands() override;
-
     MaybeError ExecutePendingCommandContext();
 
     MaybeError CopyFromStagingToBufferImpl(BufferBase* source,
@@ -168,6 +166,12 @@
     // Dawn APIs
     void SetLabelImpl() override;
 
+    // TODO(dawn:1413) move these methods to the d3d12::Queue.
+    void ForceEventualFlushOfCommands();
+    bool HasPendingCommands() const;
+    ResultOrError<ExecutionSerial> CheckAndUpdateCompletedSerials();
+    MaybeError WaitForIdleForDestruction();
+
   private:
     using Base = d3d::Device;
 
@@ -209,8 +213,6 @@
                                            void* userdata) override;
 
     void DestroyImpl() override;
-    MaybeError WaitForIdleForDestruction() override;
-    bool HasPendingCommands() const override;
 
     MaybeError CheckDebugLayerAndGenerateErrors();
     void AppendDebugLayerMessages(ErrorData* error) override;
@@ -221,7 +223,6 @@
 
     ComPtr<ID3D12Fence> mFence;
     HANDLE mFenceEvent = nullptr;
-    ResultOrError<ExecutionSerial> CheckAndUpdateCompletedSerials() override;
 
     ComPtr<ID3D12Device> mD3d12Device;  // Device is owned by adapter and will not be outlived.
     ComPtr<ID3D12CommandQueue> mCommandQueue;
diff --git a/src/dawn/native/d3d12/QueueD3D12.cpp b/src/dawn/native/d3d12/QueueD3D12.cpp
index b489a68..ce9d08a 100644
--- a/src/dawn/native/d3d12/QueueD3D12.cpp
+++ b/src/dawn/native/d3d12/QueueD3D12.cpp
@@ -61,6 +61,22 @@
     return {};
 }
 
+bool Queue::HasPendingCommands() const {
+    return ToBackend(GetDevice())->HasPendingCommands();
+}
+
+ResultOrError<ExecutionSerial> Queue::CheckAndUpdateCompletedSerials() {
+    return ToBackend(GetDevice())->CheckAndUpdateCompletedSerials();
+}
+
+void Queue::ForceEventualFlushOfCommands() {
+    return ToBackend(GetDevice())->ForceEventualFlushOfCommands();
+}
+
+MaybeError Queue::WaitForIdleForDestruction() {
+    return ToBackend(GetDevice())->WaitForIdleForDestruction();
+}
+
 void Queue::SetLabelImpl() {
     Device* device = ToBackend(GetDevice());
     // TODO(crbug.com/dawn/1344): When we start using multiple queues this needs to be adjusted
diff --git a/src/dawn/native/d3d12/QueueD3D12.h b/src/dawn/native/d3d12/QueueD3D12.h
index a0f8b46..f853754 100644
--- a/src/dawn/native/d3d12/QueueD3D12.h
+++ b/src/dawn/native/d3d12/QueueD3D12.h
@@ -34,6 +34,10 @@
     void Initialize();
 
     MaybeError SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) override;
+    bool HasPendingCommands() const override;
+    ResultOrError<ExecutionSerial> CheckAndUpdateCompletedSerials() override;
+    void ForceEventualFlushOfCommands() override;
+    MaybeError WaitForIdleForDestruction() override;
 
     // Dawn API
     void SetLabelImpl() override;
diff --git a/src/dawn/native/d3d12/ResidencyManagerD3D12.cpp b/src/dawn/native/d3d12/ResidencyManagerD3D12.cpp
index 00b3a99..6ff6272 100644
--- a/src/dawn/native/d3d12/ResidencyManagerD3D12.cpp
+++ b/src/dawn/native/d3d12/ResidencyManagerD3D12.cpp
@@ -22,6 +22,7 @@
 #include "dawn/native/d3d12/Forward.h"
 #include "dawn/native/d3d12/HeapD3D12.h"
 #include "dawn/native/d3d12/PhysicalDeviceD3D12.h"
+#include "dawn/native/d3d12/QueueD3D12.h"
 
 namespace dawn::native::d3d12 {
 
@@ -169,7 +170,7 @@
 
     // We must ensure that any previous use of a resource has completed before the resource can
     // be evicted.
-    if (lastSubmissionSerial > mDevice->GetCompletedCommandSerial()) {
+    if (lastSubmissionSerial > mDevice->GetQueue()->GetCompletedCommandSerial()) {
         DAWN_TRY(mDevice->WaitForSerial(lastSubmissionSerial));
     }
 
diff --git a/src/dawn/native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.cpp b/src/dawn/native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.cpp
index a7e1ab0..e1b2834 100644
--- a/src/dawn/native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.cpp
+++ b/src/dawn/native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.cpp
@@ -21,6 +21,7 @@
 #include "dawn/native/d3d/D3DError.h"
 #include "dawn/native/d3d12/DeviceD3D12.h"
 #include "dawn/native/d3d12/GPUDescriptorHeapAllocationD3D12.h"
+#include "dawn/native/d3d12/QueueD3D12.h"
 #include "dawn/native/d3d12/ResidencyManagerD3D12.h"
 
 namespace dawn::native::d3d12 {
@@ -189,7 +190,7 @@
             // heaps for heavy users.
             // TODO(dawn:256): Consider periodically triming to avoid OOM.
             mPool.push_back({mDevice->GetPendingCommandSerial(), std::move(mHeap)});
-            if (mPool.front().heapSerial <= mDevice->GetCompletedCommandSerial()) {
+            if (mPool.front().heapSerial <= mDevice->GetQueue()->GetCompletedCommandSerial()) {
                 descriptorHeap = std::move(mPool.front().heap);
                 mPool.pop_front();
             }
@@ -241,7 +242,7 @@
     // re-allocated every submit. For this reason, we view any descriptors allocated prior to the
     // pending submit as invalid. We must also verify the descriptor heap has not switched (because
     // a larger descriptor heap was needed).
-    return (allocation.GetLastUsageSerial() == mDevice->GetPendingCommandSerial() &&
+    return (allocation.GetLastUsageSerial() == mDevice->GetQueue()->GetPendingCommandSerial() &&
             allocation.GetHeapSerial() == mHeapSerial);
 }
 
diff --git a/src/dawn/native/metal/DeviceMTL.h b/src/dawn/native/metal/DeviceMTL.h
index 0e6d84d..30eaf0c 100644
--- a/src/dawn/native/metal/DeviceMTL.h
+++ b/src/dawn/native/metal/DeviceMTL.h
@@ -87,7 +87,11 @@
     // single-byte buffer
     id<MTLBuffer> GetMockBlitMtlBuffer();
 
-    void ForceEventualFlushOfCommands() override;
+    // TODO(dawn:1413) move these methods to the metal::Queue.
+    void ForceEventualFlushOfCommands();
+    bool HasPendingCommands() const;
+    ResultOrError<ExecutionSerial> CheckAndUpdateCompletedSerials();
+    MaybeError WaitForIdleForDestruction();
 
   private:
     Device(AdapterBase* adapter,
@@ -135,9 +139,6 @@
         const Surface* surface) const override;
 
     void DestroyImpl() override;
-    MaybeError WaitForIdleForDestruction() override;
-    bool HasPendingCommands() const override;
-    ResultOrError<ExecutionSerial> CheckAndUpdateCompletedSerials() override;
 
     NSPRef<id<MTLDevice>> mMtlDevice;
     NSPRef<id> mMtlSharedEvent = nil;  // MTLSharedEvent not available until macOS 10.14+.
diff --git a/src/dawn/native/metal/DeviceMTL.mm b/src/dawn/native/metal/DeviceMTL.mm
index de0052d..24db250 100644
--- a/src/dawn/native/metal/DeviceMTL.mm
+++ b/src/dawn/native/metal/DeviceMTL.mm
@@ -249,7 +249,7 @@
 }
 
 ResultOrError<ExecutionSerial> Device::CheckAndUpdateCompletedSerials() {
-    uint64_t frontendCompletedSerial{GetCompletedCommandSerial()};
+    uint64_t frontendCompletedSerial{GetQueue()->GetCompletedCommandSerial()};
     // sometimes we increase the serials, in which case the completed serial in
     // the device base will surpass the completed serial we have in the metal backend, so we
     // must update ours when we see that the completed serial from device base has
@@ -313,7 +313,7 @@
         return {};
     }
 
-    IncrementLastSubmittedCommandSerial();
+    GetQueue()->IncrementLastSubmittedCommandSerial();
 
     // Acquire the pending command buffer, which is retained. It must be released later.
     NSPRef<id<MTLCommandBuffer>> pendingCommands = mCommandContext.AcquireCommands();
@@ -466,12 +466,12 @@
 MaybeError Device::WaitForIdleForDestruction() {
     // Forget all pending commands.
     mCommandContext.AcquireCommands();
-    DAWN_TRY(CheckPassedSerials());
+    DAWN_TRY(GetQueue()->CheckPassedSerials());
 
     // Wait for all commands to be finished so we can free resources
-    while (GetCompletedCommandSerial() != GetLastSubmittedCommandSerial()) {
+    while (GetQueue()->GetCompletedCommandSerial() != GetQueue()->GetLastSubmittedCommandSerial()) {
         usleep(100);
-        DAWN_TRY(CheckPassedSerials());
+        DAWN_TRY(GetQueue()->CheckPassedSerials());
     }
 
     return {};
diff --git a/src/dawn/native/metal/QueueMTL.h b/src/dawn/native/metal/QueueMTL.h
index 5f95921..f8674cb 100644
--- a/src/dawn/native/metal/QueueMTL.h
+++ b/src/dawn/native/metal/QueueMTL.h
@@ -28,6 +28,10 @@
 
   private:
     MaybeError SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) override;
+    bool HasPendingCommands() const override;
+    ResultOrError<ExecutionSerial> CheckAndUpdateCompletedSerials() override;
+    void ForceEventualFlushOfCommands() override;
+    MaybeError WaitForIdleForDestruction() override;
 };
 
 }  // namespace dawn::native::metal
diff --git a/src/dawn/native/metal/QueueMTL.mm b/src/dawn/native/metal/QueueMTL.mm
index 8e9bd66..c097d82 100644
--- a/src/dawn/native/metal/QueueMTL.mm
+++ b/src/dawn/native/metal/QueueMTL.mm
@@ -48,4 +48,20 @@
     }
 }
 
+bool Queue::HasPendingCommands() const {
+    return ToBackend(GetDevice())->HasPendingCommands();
+}
+
+ResultOrError<ExecutionSerial> Queue::CheckAndUpdateCompletedSerials() {
+    return ToBackend(GetDevice())->CheckAndUpdateCompletedSerials();
+}
+
+void Queue::ForceEventualFlushOfCommands() {
+    return ToBackend(GetDevice())->ForceEventualFlushOfCommands();
+}
+
+MaybeError Queue::WaitForIdleForDestruction() {
+    return ToBackend(GetDevice())->WaitForIdleForDestruction();
+}
+
 }  // namespace dawn::native::metal
diff --git a/src/dawn/native/null/DeviceNull.cpp b/src/dawn/native/null/DeviceNull.cpp
index 40b7f15..f248f75 100644
--- a/src/dawn/native/null/DeviceNull.cpp
+++ b/src/dawn/native/null/DeviceNull.cpp
@@ -217,13 +217,8 @@
     ASSERT(mMemoryUsage == 0);
 }
 
-MaybeError Device::WaitForIdleForDestruction() {
+void Device::ForgetPendingOperations() {
     mPendingOperations.clear();
-    return {};
-}
-
-bool Device::HasPendingCommands() const {
-    return false;
 }
 
 MaybeError Device::CopyFromStagingToBufferImpl(BufferBase* source,
@@ -272,10 +267,6 @@
     return SubmitPendingOperations();
 }
 
-ResultOrError<ExecutionSerial> Device::CheckAndUpdateCompletedSerials() {
-    return GetLastSubmittedCommandSerial();
-}
-
 void Device::AddPendingOperation(std::unique_ptr<PendingOperation> operation) {
     mPendingOperations.emplace_back(std::move(operation));
 }
@@ -286,8 +277,8 @@
     }
     mPendingOperations.clear();
 
-    DAWN_TRY(CheckPassedSerials());
-    IncrementLastSubmittedCommandSerial();
+    DAWN_TRY(GetQueue()->CheckPassedSerials());
+    GetQueue()->IncrementLastSubmittedCommandSerial();
 
     return {};
 }
@@ -393,6 +384,21 @@
     return {};
 }
 
+ResultOrError<ExecutionSerial> Queue::CheckAndUpdateCompletedSerials() {
+    return GetLastSubmittedCommandSerial();
+}
+
+void Queue::ForceEventualFlushOfCommands() {}
+
+bool Queue::HasPendingCommands() const {
+    return false;
+}
+
+MaybeError Queue::WaitForIdleForDestruction() {
+    ToBackend(GetDevice())->ForgetPendingOperations();
+    return {};
+}
+
 // ComputePipeline
 MaybeError ComputePipeline::Initialize() {
     const ProgrammableStage& computeStage = GetStage(SingleShaderStage::Compute);
@@ -503,8 +509,6 @@
     return true;
 }
 
-void Device::ForceEventualFlushOfCommands() {}
-
 Texture::Texture(DeviceBase* device, const TextureDescriptor* descriptor)
     : TextureBase(device, descriptor) {}
 
diff --git a/src/dawn/native/null/DeviceNull.h b/src/dawn/native/null/DeviceNull.h
index 9d96941..d3a5a47 100644
--- a/src/dawn/native/null/DeviceNull.h
+++ b/src/dawn/native/null/DeviceNull.h
@@ -103,6 +103,7 @@
 
     void AddPendingOperation(std::unique_ptr<PendingOperation> operation);
     MaybeError SubmitPendingOperations();
+    void ForgetPendingOperations();
 
     MaybeError CopyFromStagingToBufferImpl(BufferBase* source,
                                            uint64_t sourceOffset,
@@ -124,8 +125,6 @@
 
     bool IsResolveTextureBlitWithDrawSupported() const override;
 
-    void ForceEventualFlushOfCommands() override;
-
   private:
     using DeviceBase::DeviceBase;
 
@@ -159,11 +158,7 @@
     ResultOrError<wgpu::TextureUsage> GetSupportedSurfaceUsageImpl(
         const Surface* surface) const override;
 
-    ResultOrError<ExecutionSerial> CheckAndUpdateCompletedSerials() override;
-
     void DestroyImpl() override;
-    MaybeError WaitForIdleForDestruction() override;
-    bool HasPendingCommands() const override;
 
     std::vector<std::unique_ptr<PendingOperation>> mPendingOperations;
 
@@ -272,6 +267,10 @@
                                uint64_t bufferOffset,
                                const void* data,
                                size_t size) override;
+    ResultOrError<ExecutionSerial> CheckAndUpdateCompletedSerials() override;
+    void ForceEventualFlushOfCommands() override;
+    bool HasPendingCommands() const override;
+    MaybeError WaitForIdleForDestruction() override;
 };
 
 class ComputePipeline final : public ComputePipelineBase {
diff --git a/src/dawn/native/opengl/DeviceGL.cpp b/src/dawn/native/opengl/DeviceGL.cpp
index 714cd3e..f627e10 100644
--- a/src/dawn/native/opengl/DeviceGL.cpp
+++ b/src/dawn/native/opengl/DeviceGL.cpp
@@ -266,7 +266,7 @@
 
     const OpenGLFunctions& gl = GetGL();
     GLsync sync = gl.FenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
-    IncrementLastSubmittedCommandSerial();
+    GetQueue()->IncrementLastSubmittedCommandSerial();
     mFencesInFlight.emplace(sync, GetLastSubmittedCommandSerial());
 
     // Reset mHasPendingCommands after GetGL() which will set mHasPendingCommands to true.
@@ -393,7 +393,7 @@
 
         mFencesInFlight.pop();
 
-        ASSERT(fenceSerial > GetCompletedCommandSerial());
+        ASSERT(fenceSerial > GetQueue()->GetCompletedCommandSerial());
     }
     return fenceSerial;
 }
@@ -420,7 +420,7 @@
 MaybeError Device::WaitForIdleForDestruction() {
     const OpenGLFunctions& gl = GetGL();
     gl.Finish();
-    DAWN_TRY(CheckPassedSerials());
+    DAWN_TRY(GetQueue()->CheckPassedSerials());
     ASSERT(mFencesInFlight.empty());
 
     return {};
diff --git a/src/dawn/native/opengl/DeviceGL.h b/src/dawn/native/opengl/DeviceGL.h
index 507379f..4b7bbbe 100644
--- a/src/dawn/native/opengl/DeviceGL.h
+++ b/src/dawn/native/opengl/DeviceGL.h
@@ -84,7 +84,6 @@
     uint64_t GetOptimalBufferToTextureCopyOffsetAlignment() const override;
 
     float GetTimestampPeriodInNS() const override;
-    void ForceEventualFlushOfCommands() override;
 
     class Context {
       public:
@@ -92,6 +91,12 @@
         virtual void MakeCurrent() = 0;
     };
 
+    // TODO(dawn:1413) move these methods to the opengl::Queue.
+    void ForceEventualFlushOfCommands();
+    bool HasPendingCommands() const;
+    ResultOrError<ExecutionSerial> CheckAndUpdateCompletedSerials();
+    MaybeError WaitForIdleForDestruction();
+
   private:
     Device(AdapterBase* adapter,
            const DeviceDescriptor* descriptor,
@@ -130,10 +135,7 @@
         const Surface* surface) const override;
 
     GLenum GetBGRAInternalFormat() const;
-    ResultOrError<ExecutionSerial> CheckAndUpdateCompletedSerials() override;
     void DestroyImpl() override;
-    MaybeError WaitForIdleForDestruction() override;
-    bool HasPendingCommands() const override;
 
     const OpenGLFunctions mGL;
 
diff --git a/src/dawn/native/opengl/QueueGL.cpp b/src/dawn/native/opengl/QueueGL.cpp
index 544ac33..3b0f79b 100644
--- a/src/dawn/native/opengl/QueueGL.cpp
+++ b/src/dawn/native/opengl/QueueGL.cpp
@@ -73,4 +73,20 @@
     return {};
 }
 
+bool Queue::HasPendingCommands() const {
+    return ToBackend(GetDevice())->HasPendingCommands();
+}
+
+ResultOrError<ExecutionSerial> Queue::CheckAndUpdateCompletedSerials() {
+    return ToBackend(GetDevice())->CheckAndUpdateCompletedSerials();
+}
+
+void Queue::ForceEventualFlushOfCommands() {
+    return ToBackend(GetDevice())->ForceEventualFlushOfCommands();
+}
+
+MaybeError Queue::WaitForIdleForDestruction() {
+    return ToBackend(GetDevice())->WaitForIdleForDestruction();
+}
+
 }  // namespace dawn::native::opengl
diff --git a/src/dawn/native/opengl/QueueGL.h b/src/dawn/native/opengl/QueueGL.h
index 962dd21..c1dab97 100644
--- a/src/dawn/native/opengl/QueueGL.h
+++ b/src/dawn/native/opengl/QueueGL.h
@@ -35,6 +35,11 @@
                                 const void* data,
                                 const TextureDataLayout& dataLayout,
                                 const Extent3D& writeSizePixel) override;
+
+    bool HasPendingCommands() const override;
+    ResultOrError<ExecutionSerial> CheckAndUpdateCompletedSerials() override;
+    void ForceEventualFlushOfCommands() override;
+    MaybeError WaitForIdleForDestruction() override;
 };
 
 }  // namespace dawn::native::opengl
diff --git a/src/dawn/native/vulkan/DeviceVk.cpp b/src/dawn/native/vulkan/DeviceVk.cpp
index cda3649..50797ba 100644
--- a/src/dawn/native/vulkan/DeviceVk.cpp
+++ b/src/dawn/native/vulkan/DeviceVk.cpp
@@ -226,7 +226,7 @@
 MaybeError Device::TickImpl() {
     RecycleCompletedCommands();
 
-    ExecutionSerial completedSerial = GetCompletedCommandSerial();
+    ExecutionSerial completedSerial = GetQueue()->GetCompletedCommandSerial();
 
     for (Ref<DescriptorSetAllocator>& allocator :
          mDescriptorAllocatorsPendingDeallocation.IterateUpTo(completedSerial)) {
@@ -265,7 +265,7 @@
     return mQueueFamily;
 }
 
-VkQueue Device::GetQueue() const {
+VkQueue Device::GetVkQueue() const {
     return mQueue;
 }
 
@@ -369,7 +369,7 @@
     for (VkSemaphore semaphore : mRecordingContext.waitSemaphores) {
         mDeleter->DeleteWhenUnused(semaphore);
     }
-    IncrementLastSubmittedCommandSerial();
+    GetQueue()->IncrementLastSubmittedCommandSerial();
     ExecutionSerial lastSubmittedSerial = GetLastSubmittedCommandSerial();
     mFencesInFlight.emplace(fence, lastSubmittedSerial);
 
@@ -652,7 +652,7 @@
 
         mUnusedFences.push_back(fence);
 
-        ASSERT(fenceSerial > GetCompletedCommandSerial());
+        ASSERT(fenceSerial > GetQueue()->GetCompletedCommandSerial());
         mFencesInFlight.pop();
     }
     return fenceSerial;
@@ -744,10 +744,10 @@
 }
 
 void Device::RecycleCompletedCommands() {
-    for (auto& commands : mCommandsInFlight.IterateUpTo(GetCompletedCommandSerial())) {
+    for (auto& commands : mCommandsInFlight.IterateUpTo(GetQueue()->GetCompletedCommandSerial())) {
         mUnusedCommands.push_back(commands);
     }
-    mCommandsInFlight.ClearUpTo(GetCompletedCommandSerial());
+    mCommandsInFlight.ClearUpTo(GetQueue()->GetCompletedCommandSerial());
 }
 
 MaybeError Device::CopyFromStagingToBufferImpl(BufferBase* source,
@@ -1015,7 +1015,7 @@
     while (!mFencesInFlight.empty()) {
         VkFence fence = mFencesInFlight.front().first;
         ExecutionSerial fenceSerial = mFencesInFlight.front().second;
-        ASSERT(fenceSerial > GetCompletedCommandSerial());
+        ASSERT(fenceSerial > GetQueue()->GetCompletedCommandSerial());
 
         VkResult result = VkResult::WrapUnsafe(VK_TIMEOUT);
         do {
@@ -1102,7 +1102,7 @@
     }
     mUnusedFences.clear();
 
-    ExecutionSerial completedSerial = GetCompletedCommandSerial();
+    ExecutionSerial completedSerial = GetQueue()->GetCompletedCommandSerial();
     for (Ref<DescriptorSetAllocator>& allocator :
          mDescriptorAllocatorsPendingDeallocation.IterateUpTo(completedSerial)) {
         allocator->FinishDeallocation(completedSerial);
diff --git a/src/dawn/native/vulkan/DeviceVk.h b/src/dawn/native/vulkan/DeviceVk.h
index ef716f6..dcae126 100644
--- a/src/dawn/native/vulkan/DeviceVk.h
+++ b/src/dawn/native/vulkan/DeviceVk.h
@@ -58,7 +58,7 @@
     const VulkanGlobalInfo& GetGlobalInfo() const;
     VkDevice GetVkDevice() const;
     uint32_t GetGraphicsQueueFamily() const;
-    VkQueue GetQueue() const;
+    VkQueue GetVkQueue() const;
 
     FencedDeleter* GetFencedDeleter() const;
     RenderPassCache* GetRenderPassCache() const;
@@ -115,7 +115,11 @@
     // Used to associate this device with validation layer messages.
     const char* GetDebugPrefix() { return mDebugPrefix.c_str(); }
 
-    void ForceEventualFlushOfCommands() override;
+    // TODO(dawn:1413) move these methods the vulkan::Queue.
+    void ForceEventualFlushOfCommands();
+    bool HasPendingCommands() const;
+    ResultOrError<ExecutionSerial> CheckAndUpdateCompletedSerials();
+    MaybeError WaitForIdleForDestruction();
 
   private:
     Device(AdapterBase* adapter,
@@ -167,8 +171,6 @@
     void CheckDebugMessagesAfterDestruction() const;
 
     void DestroyImpl() override;
-    MaybeError WaitForIdleForDestruction() override;
-    bool HasPendingCommands() const override;
 
     // To make it easier to use fn it is a public const member. However
     // the Device is allowed to mutate them through these private methods.
@@ -189,7 +191,6 @@
     std::unique_ptr<external_semaphore::Service> mExternalSemaphoreService;
 
     ResultOrError<VkFence> GetUnusedFence();
-    ResultOrError<ExecutionSerial> CheckAndUpdateCompletedSerials() override;
 
     // We track which operations are in flight on the GPU with an increasing serial.
     // This works only because we have a single queue. Each submit to a queue is associated
diff --git a/src/dawn/native/vulkan/QueueVk.cpp b/src/dawn/native/vulkan/QueueVk.cpp
index 3b4cd36..c77553e 100644
--- a/src/dawn/native/vulkan/QueueVk.cpp
+++ b/src/dawn/native/vulkan/QueueVk.cpp
@@ -62,7 +62,23 @@
     Device* device = ToBackend(GetDevice());
     // TODO(crbug.com/dawn/1344): When we start using multiple queues this needs to be adjusted
     // so it doesn't always change the default queue's label.
-    SetDebugName(device, VK_OBJECT_TYPE_QUEUE, device->GetQueue(), "Dawn_Queue", GetLabel());
+    SetDebugName(device, VK_OBJECT_TYPE_QUEUE, device->GetVkQueue(), "Dawn_Queue", GetLabel());
+}
+
+bool Queue::HasPendingCommands() const {
+    return ToBackend(GetDevice())->HasPendingCommands();
+}
+
+ResultOrError<ExecutionSerial> Queue::CheckAndUpdateCompletedSerials() {
+    return ToBackend(GetDevice())->CheckAndUpdateCompletedSerials();
+}
+
+void Queue::ForceEventualFlushOfCommands() {
+    return ToBackend(GetDevice())->ForceEventualFlushOfCommands();
+}
+
+MaybeError Queue::WaitForIdleForDestruction() {
+    return ToBackend(GetDevice())->WaitForIdleForDestruction();
 }
 
 }  // namespace dawn::native::vulkan
diff --git a/src/dawn/native/vulkan/QueueVk.h b/src/dawn/native/vulkan/QueueVk.h
index 470efd7..5b7c1c1 100644
--- a/src/dawn/native/vulkan/QueueVk.h
+++ b/src/dawn/native/vulkan/QueueVk.h
@@ -33,6 +33,10 @@
     void Initialize();
 
     MaybeError SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) override;
+    bool HasPendingCommands() const override;
+    ResultOrError<ExecutionSerial> CheckAndUpdateCompletedSerials() override;
+    void ForceEventualFlushOfCommands() override;
+    MaybeError WaitForIdleForDestruction() override;
 
     // Dawn API
     void SetLabelImpl() override;
diff --git a/src/dawn/native/vulkan/SwapChainVk.cpp b/src/dawn/native/vulkan/SwapChainVk.cpp
index 019b705..d129f95 100644
--- a/src/dawn/native/vulkan/SwapChainVk.cpp
+++ b/src/dawn/native/vulkan/SwapChainVk.cpp
@@ -598,7 +598,7 @@
     mTexture = nullptr;
 
     VkResult result =
-        VkResult::WrapUnsafe(device->fn.QueuePresentKHR(device->GetQueue(), &presentInfo));
+        VkResult::WrapUnsafe(device->fn.QueuePresentKHR(device->GetVkQueue(), &presentInfo));
 
     switch (result) {
         case VK_SUCCESS:
diff --git a/src/dawn/tests/unittests/native/mocks/DeviceMock.h b/src/dawn/tests/unittests/native/mocks/DeviceMock.h
index 348f9a3..a4d994a 100644
--- a/src/dawn/tests/unittests/native/mocks/DeviceMock.h
+++ b/src/dawn/tests/unittests/native/mocks/DeviceMock.h
@@ -63,7 +63,6 @@
     MOCK_METHOD(uint64_t, GetOptimalBufferToTextureCopyOffsetAlignment, (), (const, override));
 
     MOCK_METHOD(float, GetTimestampPeriodInNS, (), (const, override));
-    MOCK_METHOD(void, ForceEventualFlushOfCommands, (), (override));
 
     MOCK_METHOD(ResultOrError<Ref<BindGroupBase>>,
                 CreateBindGroupImpl,
@@ -127,10 +126,7 @@
 
     MOCK_METHOD(MaybeError, TickImpl, (), (override));
 
-    MOCK_METHOD(ResultOrError<ExecutionSerial>, CheckAndUpdateCompletedSerials, (), (override));
     MOCK_METHOD(void, DestroyImpl, (), (override));
-    MOCK_METHOD(MaybeError, WaitForIdleForDestruction, (), (override));
-    MOCK_METHOD(bool, HasPendingCommands, (), (const, override));
 
   private:
     Ref<InstanceBase> mInstance;
diff --git a/src/dawn/tests/unittests/native/mocks/QueueMock.h b/src/dawn/tests/unittests/native/mocks/QueueMock.h
index 02b0c17..5fc1ce1 100644
--- a/src/dawn/tests/unittests/native/mocks/QueueMock.h
+++ b/src/dawn/tests/unittests/native/mocks/QueueMock.h
@@ -38,6 +38,11 @@
                 (const ImageCopyTexture&, const void*, const TextureDataLayout&, const Extent3D&),
                 (override));
     MOCK_METHOD(void, DestroyImpl, (), (override));
+
+    MOCK_METHOD(ResultOrError<ExecutionSerial>, CheckAndUpdateCompletedSerials, (), (override));
+    MOCK_METHOD(bool, HasPendingCommands, (), (const, override));
+    MOCK_METHOD(void, ForceEventualFlushOfCommands, (), (override));
+    MOCK_METHOD(MaybeError, WaitForIdleForDestruction, (), (override));
 };
 
 }  // namespace dawn::native
diff --git a/src/dawn/tests/white_box/D3D12DescriptorHeapTests.cpp b/src/dawn/tests/white_box/D3D12DescriptorHeapTests.cpp
index 21bf85d..6ff4771 100644
--- a/src/dawn/tests/white_box/D3D12DescriptorHeapTests.cpp
+++ b/src/dawn/tests/white_box/D3D12DescriptorHeapTests.cpp
@@ -20,6 +20,7 @@
 #include "dawn/native/Toggles.h"
 #include "dawn/native/d3d12/BindGroupLayoutD3D12.h"
 #include "dawn/native/d3d12/DeviceD3D12.h"
+#include "dawn/native/d3d12/QueueD3D12.h"
 #include "dawn/native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h"
 #include "dawn/native/d3d12/StagingDescriptorAllocatorD3D12.h"
 #include "dawn/tests/DawnTest.h"
@@ -41,7 +42,8 @@
     void SetUp() override {
         DawnTest::SetUp();
         DAWN_TEST_UNSUPPORTED_IF(UsesWire());
-        mD3DDevice = reinterpret_cast<Device*>(device.Get());
+        mD3DDevice = ToBackend(FromAPI(device.Get()));
+        mD3DQueue = ToBackend(mD3DDevice->GetQueue());
 
         mSimpleVSModule = utils::CreateShaderModule(device, R"(
 
@@ -95,6 +97,7 @@
     }
 
     Device* mD3DDevice = nullptr;
+    Queue* mD3DQueue = nullptr;
 
     wgpu::ShaderModule mSimpleVSModule;
     wgpu::ShaderModule mSimpleFSModule;
@@ -239,7 +242,7 @@
         EXPECT_TRUE(std::find(heaps.begin(), heaps.end(), heap) == heaps.end());
         heaps.push_back(heap);
         // CheckPassedSerials() will update the last internally completed serial.
-        EXPECT_TRUE(mD3DDevice->CheckPassedSerials().IsSuccess());
+        EXPECT_TRUE(mD3DQueue->CheckPassedSerials().IsSuccess());
         // NextSerial() will increment the last internally submitted serial.
         EXPECT_TRUE(mD3DDevice->NextSerial().IsSuccess());
     }
@@ -252,7 +255,7 @@
         ComPtr<ID3D12DescriptorHeap> heap = allocator->GetShaderVisibleHeap();
         EXPECT_TRUE(heaps.front() == heap);
         heaps.pop_front();
-        EXPECT_TRUE(mD3DDevice->CheckPassedSerials().IsSuccess());
+        EXPECT_TRUE(mD3DQueue->CheckPassedSerials().IsSuccess());
         EXPECT_TRUE(mD3DDevice->NextSerial().IsSuccess());
     }
 
@@ -319,7 +322,7 @@
     EXPECT_EQ(allocator->GetShaderVisiblePoolSizeForTesting(), kNumOfSwitches);
 
     // Ensure switched-over heaps can be recycled by advancing the GPU.
-    mD3DDevice->AssumeCommandsComplete();
+    mD3DQueue->AssumeCommandsComplete();
 
     // Switch-over |kNumOfSwitches| again reusing the same heaps.
     for (uint32_t i = 0; i < kNumOfSwitches; i++) {
@@ -405,7 +408,7 @@
     EXPECT_EQ(allocator->GetShaderVisiblePoolSizeForTesting(), kNumOfPooledHeaps);
 
     // Ensure switched-over heaps can be recycled by advancing the GPU.
-    mD3DDevice->AssumeCommandsComplete();
+    mD3DQueue->AssumeCommandsComplete();
 
     // Switch-over the pool-allocated heaps.
     for (uint32_t i = 0; i < kNumOfPooledHeaps; i++) {