CommandEncoder: prevent calls even after a failed finish

Finish validation acquires the command allocator but didn't set the
finished state so further top-level would still try to record in the
allocator, causing an ASSERT to fire.

BUG=chromium:939969

Change-Id: I334878098e6b824c2c4cef4fccb75472d3b63bbe
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/6041
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Kai Ninomiya <kainino@chromium.org>
diff --git a/src/dawn_native/CommandEncoder.cpp b/src/dawn_native/CommandEncoder.cpp
index 1ac0790..21b9992 100644
--- a/src/dawn_native/CommandEncoder.cpp
+++ b/src/dawn_native/CommandEncoder.cpp
@@ -808,6 +808,9 @@
 
     CommandBufferBase* CommandEncoderBase::Finish() {
         if (GetDevice()->ConsumedError(ValidateFinish())) {
+            // Even if finish validation fails, it is now invalid to call any encoding commands on
+            // this object, so we set its state to finished.
+            mEncodingState = EncodingState::Finished;
             return CommandBufferBase::MakeError(GetDevice());
         }
         ASSERT(!IsError());
@@ -833,6 +836,11 @@
     }
 
     void CommandEncoderBase::PassEnded() {
+        // This function may still be called when the command encoder is finished, just do nothing.
+        if (mEncodingState == EncodingState::Finished) {
+            return;
+        }
+
         if (mEncodingState == EncodingState::ComputePass) {
             mAllocator.Allocate<EndComputePassCmd>(Command::EndComputePass);
         } else {
diff --git a/src/tests/unittests/validation/CommandBufferValidationTests.cpp b/src/tests/unittests/validation/CommandBufferValidationTests.cpp
index da2a246..3353390 100644
--- a/src/tests/unittests/validation/CommandBufferValidationTests.cpp
+++ b/src/tests/unittests/validation/CommandBufferValidationTests.cpp
@@ -155,6 +155,22 @@
     }
 }
 
+// Test that beginning a compute pass before ending the previous pass causes an error.
+TEST_F(CommandBufferValidationTest, CallsAfterAFailedFinish) {
+    // A buffer that can't be used in CopyBufferToBuffer
+    dawn::BufferDescriptor bufferDesc;
+    bufferDesc.size = 16;
+    bufferDesc.usage = dawn::BufferUsageBit::Uniform;
+    dawn::Buffer buffer = device.CreateBuffer(&bufferDesc);
+
+    dawn::CommandEncoder encoder = device.CreateCommandEncoder();
+    encoder.CopyBufferToBuffer(buffer, 0, buffer, 0, 0);
+    ASSERT_DEVICE_ERROR(encoder.Finish());
+
+    // TODO(cwallez@chromium.org): Currently this does nothing, should it be a device error?
+    encoder.CopyBufferToBuffer(buffer, 0, buffer, 0, 0);
+}
+
 // Test that using a single buffer in multiple read usages in the same pass is allowed.
 TEST_F(CommandBufferValidationTest, BufferWithMultipleReadUsage) {
     // Create a buffer used as both vertex and index buffer.