Device Loss handle GetBindGroupLayout and test

This includes moving the destruction of vkDevice from Destroy to the
Device Destructor since we need vkDevice to destroy child objects.

Bug: dawn:68
Change-Id: Id477206b2e3f80138b3708eedcee073303f1b696
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/15220
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Natasha Lee <natlee@microsoft.com>
diff --git a/src/dawn_native/Pipeline.cpp b/src/dawn_native/Pipeline.cpp
index ad819d1..4c2439b 100644
--- a/src/dawn_native/Pipeline.cpp
+++ b/src/dawn_native/Pipeline.cpp
@@ -67,6 +67,7 @@
     }
 
     MaybeError PipelineBase::ValidateGetBindGroupLayout(uint32_t group) {
+        DAWN_TRY(GetDevice()->ValidateIsAlive());
         DAWN_TRY(GetDevice()->ValidateObject(this));
         DAWN_TRY(GetDevice()->ValidateObject(mLayout.Get()));
         if (group >= kMaxBindGroups) {
diff --git a/src/dawn_native/vulkan/DeviceVk.cpp b/src/dawn_native/vulkan/DeviceVk.cpp
index 6b44d49..13a7824 100644
--- a/src/dawn_native/vulkan/DeviceVk.cpp
+++ b/src/dawn_native/vulkan/DeviceVk.cpp
@@ -88,6 +88,25 @@
 
     Device::~Device() {
         BaseDestructor();
+
+        mDescriptorSetService = nullptr;
+
+        // We still need to properly handle Vulkan object deletion even if the device has been lost,
+        // so the Deleter and vkDevice cannot be destroyed in Device::Destroy().
+        // 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.
+        mCompletedSerial = std::numeric_limits<Serial>::max();
+        mDeleter->Tick(mCompletedSerial);
+        mDeleter = nullptr;
+
+        // VkQueues are destroyed when the VkDevice is destroyed
+        // The VkDevice is needed to destroy child objects, so it must be destroyed last after all
+        // child objects have been deleted.
+        if (mVkDevice != VK_NULL_HANDLE) {
+            fn.DestroyDevice(mVkDevice, nullptr);
+            mVkDevice = VK_NULL_HANDLE;
+        }
     }
 
     ResultOrError<BindGroupBase*> Device::CreateBindGroupImpl(
@@ -766,24 +785,16 @@
 
         // Free services explicitly so that they can free Vulkan objects before vkDestroyDevice
         mDynamicUploader = nullptr;
-        mDescriptorSetService = nullptr;
 
         // Releasing the uploader enqueues buffers to be released.
         // Call Tick() again to clear them before releasing the deleter.
         mDeleter->Tick(mCompletedSerial);
 
-        mDeleter = nullptr;
         mMapRequestTracker = nullptr;
 
         // The VkRenderPasses in the cache can be destroyed immediately since all commands referring
         // to them are guaranteed to be finished executing.
         mRenderPassCache = nullptr;
-
-        // VkQueues are destroyed when the VkDevice is destroyed
-        if (mVkDevice != VK_NULL_HANDLE) {
-            fn.DestroyDevice(mVkDevice, nullptr);
-            mVkDevice = VK_NULL_HANDLE;
-        }
     }
 
 }}  // namespace dawn_native::vulkan
diff --git a/src/tests/end2end/DeviceLostTests.cpp b/src/tests/end2end/DeviceLostTests.cpp
index ab8f260..722e5d0 100644
--- a/src/tests/end2end/DeviceLostTests.cpp
+++ b/src/tests/end2end/DeviceLostTests.cpp
@@ -81,6 +81,28 @@
     ASSERT_DEVICE_ERROR(device.CreateBindGroupLayout(&descriptor));
 }
 
+// Test that GetBindGroupLayout fails when device is lost
+TEST_P(DeviceLostTest, GetBindGroupLayoutFails) {
+    wgpu::ShaderModule csModule =
+        utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"(
+    #version 450
+    layout(set = 0, binding = 0) uniform UniformBuffer {
+        vec4 pos;
+    };
+    void main() {
+    })");
+
+    wgpu::ComputePipelineDescriptor descriptor;
+    descriptor.layout = nullptr;
+    descriptor.computeStage.module = csModule;
+    descriptor.computeStage.entryPoint = "main";
+
+    wgpu::ComputePipeline pipeline = device.CreateComputePipeline(&descriptor);
+
+    SetCallbackAndLoseForTesting();
+    ASSERT_DEVICE_ERROR(pipeline.GetBindGroupLayout(0).Get());
+}
+
 // Test that CreateBindGroup fails when device is lost
 TEST_P(DeviceLostTest, CreateBindGroupFails) {
     SetCallbackAndLoseForTesting();
@@ -189,4 +211,5 @@
     SetCallbackAndLoseForTesting();
     ASSERT_DEVICE_ERROR(device.Tick());
 }
+
 DAWN_INSTANTIATE_TEST(DeviceLostTest, D3D12Backend, VulkanBackend);
\ No newline at end of file