Validate if device is alive in EncodeFunction of EncodingContext::TryEncode

GetOrCreateAttachmentState access mCaches in Device even if device is
already destroyed. Gate the function by checking if encoder is
destroyed.

Bug: chromium:1323310
Change-Id: I8151c5ec696e4da28b8296d9142f3120379782ef
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/89860
Reviewed-by: Austin Eng <enga@chromium.org>
Reviewed-by: Loko Kung <lokokung@google.com>
Commit-Queue: Shrek Shao <shrekshao@google.com>
diff --git a/src/dawn/native/EncodingContext.cpp b/src/dawn/native/EncodingContext.cpp
index 1a91a8e..d222927 100644
--- a/src/dawn/native/EncodingContext.cpp
+++ b/src/dawn/native/EncodingContext.cpp
@@ -25,7 +25,10 @@
 namespace dawn::native {
 
 EncodingContext::EncodingContext(DeviceBase* device, const ApiObjectBase* initialEncoder)
-    : mDevice(device), mTopLevelEncoder(initialEncoder), mCurrentEncoder(initialEncoder) {}
+    : mDevice(device),
+      mTopLevelEncoder(initialEncoder),
+      mCurrentEncoder(initialEncoder),
+      mDestroyed(device->IsLost()) {}
 
 EncodingContext::~EncodingContext() {
     Destroy();
diff --git a/src/dawn/native/EncodingContext.h b/src/dawn/native/EncodingContext.h
index 360790a..020132e 100644
--- a/src/dawn/native/EncodingContext.h
+++ b/src/dawn/native/EncodingContext.h
@@ -79,10 +79,13 @@
     }
 
     inline bool CheckCurrentEncoder(const ApiObjectBase* encoder) {
+        if (mDestroyed) {
+            HandleError(
+                DAWN_FORMAT_VALIDATION_ERROR("Recording in a destroyed %s.", mCurrentEncoder));
+            return false;
+        }
         if (DAWN_UNLIKELY(encoder != mCurrentEncoder)) {
-            if (mDestroyed) {
-                HandleError(DAWN_FORMAT_VALIDATION_ERROR("Recording in a destroyed %s.", encoder));
-            } else if (mCurrentEncoder != mTopLevelEncoder) {
+            if (mCurrentEncoder != mTopLevelEncoder) {
                 // The top level encoder was used when a pass encoder was current.
                 HandleError(DAWN_FORMAT_VALIDATION_ERROR(
                     "Command cannot be recorded while %s is active.", mCurrentEncoder));
diff --git a/src/dawn/tests/unittests/validation/CommandBufferValidationTests.cpp b/src/dawn/tests/unittests/validation/CommandBufferValidationTests.cpp
index 0b74c72..dc45bc0 100644
--- a/src/dawn/tests/unittests/validation/CommandBufferValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/CommandBufferValidationTests.cpp
@@ -31,12 +31,12 @@
 
 // Test that a command buffer cannot be ended mid render pass
 TEST_F(CommandBufferValidationTest, EndedMidRenderPass) {
-    PlaceholderRenderPass PlaceholderRenderPass(device);
+    PlaceholderRenderPass placeholderRenderPass(device);
 
     // Control case, command buffer ended after the pass is ended.
     {
         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
-        wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&PlaceholderRenderPass);
+        wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&placeholderRenderPass);
         pass.End();
         encoder.Finish();
     }
@@ -44,7 +44,7 @@
     // Error case, command buffer ended mid-pass.
     {
         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
-        wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&PlaceholderRenderPass);
+        wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&placeholderRenderPass);
         ASSERT_DEVICE_ERROR(
             encoder.Finish(),
             HasSubstr("Command buffer recording ended before [RenderPassEncoder] was ended."));
@@ -54,7 +54,7 @@
     // should fail too.
     {
         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
-        wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&PlaceholderRenderPass);
+        wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&placeholderRenderPass);
         ASSERT_DEVICE_ERROR(
             encoder.Finish(),
             HasSubstr("Command buffer recording ended before [RenderPassEncoder] was ended."));
@@ -97,12 +97,12 @@
 
 // Test that a render pass cannot be ended twice
 TEST_F(CommandBufferValidationTest, RenderPassEndedTwice) {
-    PlaceholderRenderPass PlaceholderRenderPass(device);
+    PlaceholderRenderPass placeholderRenderPass(device);
 
     // Control case, pass is ended once
     {
         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
-        wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&PlaceholderRenderPass);
+        wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&placeholderRenderPass);
         pass.End();
         encoder.Finish();
     }
@@ -110,7 +110,7 @@
     // Error case, pass ended twice
     {
         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
-        wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&PlaceholderRenderPass);
+        wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&placeholderRenderPass);
         pass.End();
         pass.End();
         ASSERT_DEVICE_ERROR(
@@ -143,12 +143,12 @@
 
 // Test that beginning a compute pass before ending the previous pass causes an error.
 TEST_F(CommandBufferValidationTest, BeginComputePassBeforeEndPreviousPass) {
-    PlaceholderRenderPass PlaceholderRenderPass(device);
+    PlaceholderRenderPass placeholderRenderPass(device);
 
     // Beginning a compute pass before ending a render pass causes an error.
     {
         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
-        wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&PlaceholderRenderPass);
+        wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&placeholderRenderPass);
         wgpu::ComputePassEncoder computePass = encoder.BeginComputePass();
         computePass.End();
         renderPass.End();
@@ -168,13 +168,13 @@
 
 // Test that beginning a render pass before ending the previous pass causes an error.
 TEST_F(CommandBufferValidationTest, BeginRenderPassBeforeEndPreviousPass) {
-    PlaceholderRenderPass PlaceholderRenderPass(device);
+    PlaceholderRenderPass placeholderRenderPass(device);
 
     // Beginning a render pass before ending the render pass causes an error.
     {
         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
-        wgpu::RenderPassEncoder renderPass1 = encoder.BeginRenderPass(&PlaceholderRenderPass);
-        wgpu::RenderPassEncoder renderPass2 = encoder.BeginRenderPass(&PlaceholderRenderPass);
+        wgpu::RenderPassEncoder renderPass1 = encoder.BeginRenderPass(&placeholderRenderPass);
+        wgpu::RenderPassEncoder renderPass2 = encoder.BeginRenderPass(&placeholderRenderPass);
         renderPass2.End();
         renderPass1.End();
         ASSERT_DEVICE_ERROR(encoder.Finish());
@@ -184,7 +184,7 @@
     {
         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
         wgpu::ComputePassEncoder computePass = encoder.BeginComputePass();
-        wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&PlaceholderRenderPass);
+        wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&placeholderRenderPass);
         renderPass.End();
         computePass.End();
         ASSERT_DEVICE_ERROR(encoder.Finish());
@@ -229,12 +229,12 @@
 // Test that passes which are de-referenced prior to ending still allow the correct errors to be
 // produced.
 TEST_F(CommandBufferValidationTest, PassDereferenced) {
-    PlaceholderRenderPass PlaceholderRenderPass(device);
+    PlaceholderRenderPass placeholderRenderPass(device);
 
     // Control case, command buffer ended after the pass is ended.
     {
         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
-        wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&PlaceholderRenderPass);
+        wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&placeholderRenderPass);
         pass.End();
         encoder.Finish();
     }
@@ -242,7 +242,7 @@
     // Error case, no reference is kept to a render pass.
     {
         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
-        encoder.BeginRenderPass(&PlaceholderRenderPass);
+        encoder.BeginRenderPass(&placeholderRenderPass);
         ASSERT_DEVICE_ERROR(
             encoder.Finish(),
             HasSubstr("Command buffer recording ended before [RenderPassEncoder] was ended."));
@@ -260,7 +260,7 @@
     // Error case, beginning a new pass after failing to end a de-referenced pass.
     {
         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
-        encoder.BeginRenderPass(&PlaceholderRenderPass);
+        encoder.BeginRenderPass(&placeholderRenderPass);
         wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
         pass.End();
         ASSERT_DEVICE_ERROR(
@@ -301,12 +301,12 @@
     // only way to trigger the destroy call is by losing all references which means we cannot
     // call finish.
     DAWN_SKIP_TEST_IF(UsesWire());
-    PlaceholderRenderPass PlaceholderRenderPass(device);
+    PlaceholderRenderPass placeholderRenderPass(device);
 
     // Control case, command buffer ended after the pass is ended.
     {
         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
-        wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&PlaceholderRenderPass);
+        wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&placeholderRenderPass);
         pass.End();
         encoder.Finish();
     }
@@ -314,7 +314,7 @@
     // Destroyed encoder with encoded commands should emit error on finish.
     {
         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
-        wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&PlaceholderRenderPass);
+        wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&placeholderRenderPass);
         pass.End();
         dawn::native::FromAPI(encoder.Get())->Destroy();
         ASSERT_DEVICE_ERROR(encoder.Finish(), HasSubstr("Destroyed encoder cannot be finished."));
@@ -323,7 +323,7 @@
     // Destroyed encoder with encoded commands shouldn't emit an error if never finished.
     {
         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
-        wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&PlaceholderRenderPass);
+        wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&placeholderRenderPass);
         pass.End();
         dawn::native::FromAPI(encoder.Get())->Destroy();
     }
@@ -332,7 +332,7 @@
     {
         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
         dawn::native::FromAPI(encoder.Get())->Destroy();
-        wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&PlaceholderRenderPass);
+        wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&placeholderRenderPass);
         pass.End();
         ASSERT_DEVICE_ERROR(encoder.Finish(), HasSubstr("Destroyed encoder cannot be finished."));
     }
@@ -341,14 +341,14 @@
     {
         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
         dawn::native::FromAPI(encoder.Get())->Destroy();
-        wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&PlaceholderRenderPass);
+        wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&placeholderRenderPass);
         pass.End();
     }
 
     // Destroying a finished encoder should not emit any errors.
     {
         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
-        wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&PlaceholderRenderPass);
+        wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&placeholderRenderPass);
         pass.End();
         encoder.Finish();
         dawn::native::FromAPI(encoder.Get())->Destroy();
@@ -369,3 +369,32 @@
         ASSERT_DEVICE_ERROR(encoder.Finish(), HasSubstr("Destroyed encoder cannot be finished."));
     }
 }
+
+TEST_F(CommandBufferValidationTest, EncodeAfterDeviceDestroyed) {
+    PlaceholderRenderPass placeholderRenderPass(device);
+
+    // Device destroyed before encoding.
+    {
+        wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+        ExpectDeviceDestruction();
+        device.Destroy();
+        // The encoder should not accessing any device info if device is destroyed when try
+        // encoding.
+        wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&placeholderRenderPass);
+        pass.End();
+        ASSERT_DEVICE_ERROR(encoder.Finish(), HasSubstr("Destroyed encoder cannot be finished."));
+    }
+
+    // Device destroyed after encoding.
+    {
+        ExpectDeviceDestruction();
+        device.Destroy();
+        ASSERT_DEVICE_ERROR(wgpu::CommandEncoder encoder = device.CreateCommandEncoder(),
+                            HasSubstr("[Device] is lost"));
+        // The encoder should not accessing any device info if device is destroyed when try
+        // encoding.
+        wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&placeholderRenderPass);
+        pass.End();
+        ASSERT_DEVICE_ERROR(encoder.Finish(), HasSubstr("[Invalid CommandEncoder] is invalid."));
+    }
+}