Release D3D12 command queue in Device::DestroyImpl

Repeatedly creating and destroying WebGPU devices in a loop causes
large amounts of memory to pile up in the GPU process. Much of this
memory comes from the D3D12 command queue.

Releasing the command queue early in DestroyImpl before the destructor
runs goes a long way towards relieving the memory pressure.

Bug: chromium:1377789
Change-Id: I3ff9a5f6cb3ea3136e41079343532cbe732b6cc4
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/111280
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Rafael Cintron <rafael.cintron@microsoft.com>
diff --git a/src/dawn/native/d3d12/DeviceD3D12.cpp b/src/dawn/native/d3d12/DeviceD3D12.cpp
index 8c79f06..b007649 100644
--- a/src/dawn/native/d3d12/DeviceD3D12.cpp
+++ b/src/dawn/native/d3d12/DeviceD3D12.cpp
@@ -859,6 +859,10 @@
 
     ASSERT(mUsedComObjectRefs.Empty());
     ASSERT(!mPendingCommands.IsOpen());
+
+    // Now that we've cleared out pending work from the queue, we can safely release it and reclaim
+    // memory.
+    mCommandQueue.Reset();
 }
 
 ShaderVisibleDescriptorAllocator* Device::GetViewShaderVisibleDescriptorAllocator() const {
diff --git a/src/dawn/tests/end2end/DestroyTests.cpp b/src/dawn/tests/end2end/DestroyTests.cpp
index fcf53b2..a6ec75a 100644
--- a/src/dawn/tests/end2end/DestroyTests.cpp
+++ b/src/dawn/tests/end2end/DestroyTests.cpp
@@ -156,7 +156,7 @@
 }
 
 // Attempting to set an object label after it has been destroyed should not cause an error.
-TEST_P(DestroyTest, DestroyThenSetLabel) {
+TEST_P(DestroyTest, DestroyObjectThenSetLabel) {
     DAWN_TEST_UNSUPPORTED_IF(UsesWire());
     std::string label = "test";
     wgpu::BufferDescriptor descriptor;
@@ -167,6 +167,14 @@
     buffer.SetLabel(label.c_str());
 }
 
+// Attempting to set a device label after it has been destroyed should not cause an error.
+TEST_P(DestroyTest, DestroyDeviceThenSetLabel) {
+    DAWN_TEST_UNSUPPORTED_IF(UsesWire());
+    std::string label = "test";
+    device.Destroy();
+    device.SetLabel(label.c_str());
+}
+
 // Device destroy before buffer submit will result in error.
 TEST_P(DestroyTest, DestroyDeviceBeforeSubmit) {
     // TODO(crbug.com/dawn/628) Add more comprehensive tests with destroy and backends.