vulkan::Device: Don't rely on the queue during DestroyImpl

It was only used to get the completed serial, which is basically
kMaxExecutionSerial, so just use that. DestroyImpl's full logic might
need to run without a queue if the following happens:

 - DeviceVk::Initialize runs partially, enough to have a VkDevice, and
   fails before the call to DeviceBase::Initialize.
 - DeviceBase::Initialize IS NOT called and so mQueue IS NOT set.
 - DeviceBase::~DeviceBase calls DeviceBase::Destroy which calls
   Device::DestroyImpl.
 - Device::DestroyImpl sees there is a VkDevice so full deinitialization
   is needed, and that used to call GetQueue(), which this CL fixes.

Bug: chromium:1484300
Change-Id: I66ae8a2c7eeccffb4212323ef21b1670efcc762f
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/152404
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Loko Kung <lokokung@google.com>
diff --git a/src/dawn/native/Device.cpp b/src/dawn/native/Device.cpp
index 99db74d..ffa524f 100644
--- a/src/dawn/native/Device.cpp
+++ b/src/dawn/native/Device.cpp
@@ -244,10 +244,10 @@
 }
 
 MaybeError DeviceBase::Initialize(Ref<QueueBase> defaultQueue) {
-    SetWGSLExtensionAllowList();
-
     mQueue = std::move(defaultQueue);
 
+    SetWGSLExtensionAllowList();
+
 #if defined(DAWN_ENABLE_ASSERTS)
     mUncapturedErrorCallback = [](WGPUErrorType, char const*, void*) {
         static bool calledOnce = false;
diff --git a/src/dawn/native/vulkan/DeviceVk.cpp b/src/dawn/native/vulkan/DeviceVk.cpp
index d831c31..8731241 100644
--- a/src/dawn/native/vulkan/DeviceVk.cpp
+++ b/src/dawn/native/vulkan/DeviceVk.cpp
@@ -224,9 +224,8 @@
 }
 
 MaybeError Device::TickImpl() {
-    RecycleCompletedCommands();
-
     ExecutionSerial completedSerial = GetQueue()->GetCompletedCommandSerial();
+    RecycleCompletedCommands(completedSerial);
 
     for (Ref<DescriptorSetAllocator>& allocator :
          mDescriptorAllocatorsPendingDeallocation.IterateUpTo(completedSerial)) {
@@ -745,11 +744,11 @@
     return commands;
 }
 
-void Device::RecycleCompletedCommands() {
-    for (auto& commands : mCommandsInFlight.IterateUpTo(GetQueue()->GetCompletedCommandSerial())) {
+void Device::RecycleCompletedCommands(ExecutionSerial completedSerial) {
+    for (auto& commands : mCommandsInFlight.IterateUpTo(completedSerial)) {
         mUnusedCommands.push_back(commands);
     }
-    mCommandsInFlight.ClearUpTo(GetQueue()->GetCompletedCommandSerial());
+    mCommandsInFlight.ClearUpTo(completedSerial);
 }
 
 MaybeError Device::CopyFromStagingToBufferImpl(BufferBase* source,
@@ -1084,7 +1083,7 @@
 
     // Some commands might still be marked as in-flight if we shut down because of a device
     // loss. Recycle them as unused so that we free them below.
-    RecycleCompletedCommands();
+    RecycleCompletedCommands(kMaxExecutionSerial);
     DAWN_ASSERT(mCommandsInFlight.Empty());
 
     for (const CommandPoolAndBuffer& commands : mUnusedCommands) {
@@ -1104,17 +1103,15 @@
     }
     mUnusedFences.clear();
 
-    ExecutionSerial completedSerial = GetQueue()->GetCompletedCommandSerial();
     for (Ref<DescriptorSetAllocator>& allocator :
-         mDescriptorAllocatorsPendingDeallocation.IterateUpTo(completedSerial)) {
-        allocator->FinishDeallocation(completedSerial);
+         mDescriptorAllocatorsPendingDeallocation.IterateUpTo(kMaxExecutionSerial)) {
+        allocator->FinishDeallocation(kMaxExecutionSerial);
     }
 
     // Releasing the uploader enqueues buffers to be released.
     // Call Tick() again to clear them before releasing the deleter.
-    mResourceMemoryAllocator->Tick(completedSerial);
-    mDeleter->Tick(completedSerial);
-    mDescriptorAllocatorsPendingDeallocation.ClearUpTo(completedSerial);
+    mResourceMemoryAllocator->Tick(kMaxExecutionSerial);
+    mDescriptorAllocatorsPendingDeallocation.ClearUpTo(kMaxExecutionSerial);
 
     // Allow recycled memory to be deleted.
     mResourceMemoryAllocator->DestroyPool();
@@ -1123,10 +1120,8 @@
     // to them are guaranteed to be finished executing.
     mRenderPassCache = nullptr;
 
-    // We need handle deleting all child objects by calling Tick() again with a large serial to
-    // force all operations to look as if they were completed, and delete all objects before
-    // destroying the Deleter and vkDevice.
-    DAWN_ASSERT(mDeleter != nullptr);
+    // Delete all the remaining VkDevice child objects immediately since the GPU timeline is
+    // finished.
     mDeleter->Tick(kMaxExecutionSerial);
     mDeleter = nullptr;
 
diff --git a/src/dawn/native/vulkan/DeviceVk.h b/src/dawn/native/vulkan/DeviceVk.h
index dcae126..9376d76 100644
--- a/src/dawn/native/vulkan/DeviceVk.h
+++ b/src/dawn/native/vulkan/DeviceVk.h
@@ -206,7 +206,7 @@
 
     MaybeError PrepareRecordingContext();
     ResultOrError<CommandPoolAndBuffer> BeginVkCommandBuffer();
-    void RecycleCompletedCommands();
+    void RecycleCompletedCommands(ExecutionSerial completedSerial);
 
     SerialQueue<ExecutionSerial, CommandPoolAndBuffer> mCommandsInFlight;
     // Command pools in the unused list haven't been reset yet.