Adds a check and message for when encoding on a finished encoder.

Bug: dawn:1736
Change-Id: I1b968823deb23b1b047f9430b3410b8b5279eb52
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/131504
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Loko Kung <lokokung@google.com>
Reviewed-by: Austin Eng <enga@chromium.org>
diff --git a/src/dawn/native/EncodingContext.h b/src/dawn/native/EncodingContext.h
index 4a44eec..17bfe32 100644
--- a/src/dawn/native/EncodingContext.h
+++ b/src/dawn/native/EncodingContext.h
@@ -24,6 +24,7 @@
 #include "dawn/native/Error.h"
 #include "dawn/native/ErrorData.h"
 #include "dawn/native/IndirectDrawMetadata.h"
+#include "dawn/native/ObjectType_autogen.h"
 #include "dawn/native/PassResourceUsageTracker.h"
 #include "dawn/native/dawn_platform.h"
 
@@ -89,6 +90,16 @@
                 HandleError(DAWN_VALIDATION_ERROR(
                     "Command cannot be recorded while %s is locked and %s is currently open.",
                     mTopLevelEncoder, mCurrentEncoder));
+            } else if (mTopLevelEncoder == nullptr) {
+                // Note: mTopLevelEncoder == nullptr is used as a flag for if Finish() has been
+                // called.
+                if (encoder->GetType() == ObjectType::CommandEncoder ||
+                    encoder->GetType() == ObjectType::RenderBundleEncoder) {
+                    HandleError(DAWN_VALIDATION_ERROR("%s is already finished.", encoder));
+                } else {
+                    HandleError(DAWN_VALIDATION_ERROR("Parent encoder of %s is already finished.",
+                                                      encoder));
+                }
             } else {
                 HandleError(DAWN_VALIDATION_ERROR("Recording in an error %s.", encoder));
             }
diff --git a/src/dawn/tests/unittests/validation/CommandBufferValidationTests.cpp b/src/dawn/tests/unittests/validation/CommandBufferValidationTests.cpp
index cc8ca34..ab139ed 100644
--- a/src/dawn/tests/unittests/validation/CommandBufferValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/CommandBufferValidationTests.cpp
@@ -58,7 +58,8 @@
         ASSERT_DEVICE_ERROR(
             encoder.Finish(),
             HasSubstr("Command buffer recording ended before [RenderPassEncoder] was ended."));
-        ASSERT_DEVICE_ERROR(pass.End(), HasSubstr("Recording in an error [RenderPassEncoder]."));
+        ASSERT_DEVICE_ERROR(
+            pass.End(), HasSubstr("Parent encoder of [RenderPassEncoder] is already finished."));
     }
 }
 
@@ -89,7 +90,8 @@
         ASSERT_DEVICE_ERROR(
             encoder.Finish(),
             HasSubstr("Command buffer recording ended before [ComputePassEncoder] was ended."));
-        ASSERT_DEVICE_ERROR(pass.End(), HasSubstr("Recording in an error [ComputePassEncoder]."));
+        ASSERT_DEVICE_ERROR(
+            pass.End(), HasSubstr("Parent encoder of [ComputePassEncoder] is already finished."));
     }
 }
 
@@ -223,7 +225,8 @@
     wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
     encoder.Finish();
 
-    ASSERT_DEVICE_ERROR(encoder.CopyBufferToBuffer(copyBuffer, 0, copyBuffer, 0, 0));
+    ASSERT_DEVICE_ERROR(encoder.CopyBufferToBuffer(copyBuffer, 0, copyBuffer, 0, 0),
+                        HasSubstr("[CommandEncoder] is already finished."));
 }
 
 // Test that encoding command after a failed finish produces an error
@@ -244,7 +247,8 @@
     encoder.CopyBufferToBuffer(buffer, 0, buffer, 0, 0);
     ASSERT_DEVICE_ERROR(encoder.Finish());
 
-    ASSERT_DEVICE_ERROR(encoder.CopyBufferToBuffer(copyBuffer, 0, copyBuffer, 0, 0));
+    ASSERT_DEVICE_ERROR(encoder.CopyBufferToBuffer(copyBuffer, 0, copyBuffer, 0, 0),
+                        HasSubstr("[CommandEncoder] is already finished."));
 }
 
 // Test that passes which are de-referenced prior to ending still allow the correct errors to be