Move zero-size copy skips from the frontend to the backend

Bug: chromium:1217741
Change-Id: Ib4884b5aa80124ed9da48262fcae11cf6d605034
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/53883
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Jiawei Shao <jiawei.shao@intel.com>
diff --git a/src/dawn_native/CommandEncoder.cpp b/src/dawn_native/CommandEncoder.cpp
index 8f9db69..7676a49 100644
--- a/src/dawn_native/CommandEncoder.cpp
+++ b/src/dawn_native/CommandEncoder.cpp
@@ -633,16 +633,13 @@
                 mTopLevelBuffers.insert(destination);
             }
 
-            // Skip noop copies. Some backends validation rules disallow them.
-            if (size != 0) {
-                CopyBufferToBufferCmd* copy =
-                    allocator->Allocate<CopyBufferToBufferCmd>(Command::CopyBufferToBuffer);
-                copy->source = source;
-                copy->sourceOffset = sourceOffset;
-                copy->destination = destination;
-                copy->destinationOffset = destinationOffset;
-                copy->size = size;
-            }
+            CopyBufferToBufferCmd* copy =
+                allocator->Allocate<CopyBufferToBufferCmd>(Command::CopyBufferToBuffer);
+            copy->source = source;
+            copy->sourceOffset = sourceOffset;
+            copy->destination = destination;
+            copy->destinationOffset = destinationOffset;
+            copy->size = size;
 
             return {};
         });
@@ -682,23 +679,18 @@
 
             ApplyDefaultTextureDataLayoutOptions(&srcLayout, blockInfo, *copySize);
 
-            // Skip noop copies.
-            if (copySize->width != 0 && copySize->height != 0 &&
-                copySize->depthOrArrayLayers != 0) {
-                // Record the copy command.
-                CopyBufferToTextureCmd* copy =
-                    allocator->Allocate<CopyBufferToTextureCmd>(Command::CopyBufferToTexture);
-                copy->source.buffer = source->buffer;
-                copy->source.offset = srcLayout.offset;
-                copy->source.bytesPerRow = srcLayout.bytesPerRow;
-                copy->source.rowsPerImage = srcLayout.rowsPerImage;
-                copy->destination.texture = destination->texture;
-                copy->destination.origin = destination->origin;
-                copy->destination.mipLevel = destination->mipLevel;
-                copy->destination.aspect =
-                    ConvertAspect(destination->texture->GetFormat(), destination->aspect);
-                copy->copySize = *copySize;
-            }
+            CopyBufferToTextureCmd* copy =
+                allocator->Allocate<CopyBufferToTextureCmd>(Command::CopyBufferToTexture);
+            copy->source.buffer = source->buffer;
+            copy->source.offset = srcLayout.offset;
+            copy->source.bytesPerRow = srcLayout.bytesPerRow;
+            copy->source.rowsPerImage = srcLayout.rowsPerImage;
+            copy->destination.texture = destination->texture;
+            copy->destination.origin = destination->origin;
+            copy->destination.mipLevel = destination->mipLevel;
+            copy->destination.aspect =
+                ConvertAspect(destination->texture->GetFormat(), destination->aspect);
+            copy->copySize = *copySize;
 
             return {};
         });
@@ -738,22 +730,17 @@
 
             ApplyDefaultTextureDataLayoutOptions(&dstLayout, blockInfo, *copySize);
 
-            // Skip noop copies.
-            if (copySize->width != 0 && copySize->height != 0 &&
-                copySize->depthOrArrayLayers != 0) {
-                // Record the copy command.
-                CopyTextureToBufferCmd* copy =
-                    allocator->Allocate<CopyTextureToBufferCmd>(Command::CopyTextureToBuffer);
-                copy->source.texture = source->texture;
-                copy->source.origin = source->origin;
-                copy->source.mipLevel = source->mipLevel;
-                copy->source.aspect = ConvertAspect(source->texture->GetFormat(), source->aspect);
-                copy->destination.buffer = destination->buffer;
-                copy->destination.offset = dstLayout.offset;
-                copy->destination.bytesPerRow = dstLayout.bytesPerRow;
-                copy->destination.rowsPerImage = dstLayout.rowsPerImage;
-                copy->copySize = *copySize;
-            }
+            CopyTextureToBufferCmd* copy =
+                allocator->Allocate<CopyTextureToBufferCmd>(Command::CopyTextureToBuffer);
+            copy->source.texture = source->texture;
+            copy->source.origin = source->origin;
+            copy->source.mipLevel = source->mipLevel;
+            copy->source.aspect = ConvertAspect(source->texture->GetFormat(), source->aspect);
+            copy->destination.buffer = destination->buffer;
+            copy->destination.offset = dstLayout.offset;
+            copy->destination.bytesPerRow = dstLayout.bytesPerRow;
+            copy->destination.rowsPerImage = dstLayout.rowsPerImage;
+            copy->copySize = *copySize;
 
             return {};
         });
@@ -783,22 +770,18 @@
                 mTopLevelTextures.insert(destination->texture);
             }
 
-            // Skip noop copies.
-            if (copySize->width != 0 && copySize->height != 0 &&
-                copySize->depthOrArrayLayers != 0) {
-                CopyTextureToTextureCmd* copy =
-                    allocator->Allocate<CopyTextureToTextureCmd>(Command::CopyTextureToTexture);
-                copy->source.texture = source->texture;
-                copy->source.origin = source->origin;
-                copy->source.mipLevel = source->mipLevel;
-                copy->source.aspect = ConvertAspect(source->texture->GetFormat(), source->aspect);
-                copy->destination.texture = destination->texture;
-                copy->destination.origin = destination->origin;
-                copy->destination.mipLevel = destination->mipLevel;
-                copy->destination.aspect =
-                    ConvertAspect(destination->texture->GetFormat(), destination->aspect);
-                copy->copySize = *copySize;
-            }
+            CopyTextureToTextureCmd* copy =
+                allocator->Allocate<CopyTextureToTextureCmd>(Command::CopyTextureToTexture);
+            copy->source.texture = source->texture;
+            copy->source.origin = source->origin;
+            copy->source.mipLevel = source->mipLevel;
+            copy->source.aspect = ConvertAspect(source->texture->GetFormat(), source->aspect);
+            copy->destination.texture = destination->texture;
+            copy->destination.origin = destination->origin;
+            copy->destination.mipLevel = destination->mipLevel;
+            copy->destination.aspect =
+                ConvertAspect(destination->texture->GetFormat(), destination->aspect);
+            copy->copySize = *copySize;
 
             return {};
         });
diff --git a/src/dawn_native/d3d12/CommandBufferD3D12.cpp b/src/dawn_native/d3d12/CommandBufferD3D12.cpp
index a2dca9c..29b3a98 100644
--- a/src/dawn_native/d3d12/CommandBufferD3D12.cpp
+++ b/src/dawn_native/d3d12/CommandBufferD3D12.cpp
@@ -626,6 +626,10 @@
 
                 case Command::CopyBufferToBuffer: {
                     CopyBufferToBufferCmd* copy = mCommands.NextCommand<CopyBufferToBufferCmd>();
+                    if (copy->size == 0) {
+                        // Skip no-op copies.
+                        break;
+                    }
                     Buffer* srcBuffer = ToBackend(copy->source.Get());
                     Buffer* dstBuffer = ToBackend(copy->destination.Get());
 
@@ -646,6 +650,11 @@
 
                 case Command::CopyBufferToTexture: {
                     CopyBufferToTextureCmd* copy = mCommands.NextCommand<CopyBufferToTextureCmd>();
+                    if (copy->copySize.width == 0 || copy->copySize.height == 0 ||
+                        copy->copySize.depthOrArrayLayers == 0) {
+                        // Skip no-op copies.
+                        continue;
+                    }
                     Buffer* buffer = ToBackend(copy->source.buffer.Get());
                     Texture* texture = ToBackend(copy->destination.texture.Get());
 
@@ -676,6 +685,11 @@
 
                 case Command::CopyTextureToBuffer: {
                     CopyTextureToBufferCmd* copy = mCommands.NextCommand<CopyTextureToBufferCmd>();
+                    if (copy->copySize.width == 0 || copy->copySize.height == 0 ||
+                        copy->copySize.depthOrArrayLayers == 0) {
+                        // Skip no-op copies.
+                        continue;
+                    }
                     Texture* texture = ToBackend(copy->source.texture.Get());
                     Buffer* buffer = ToBackend(copy->destination.buffer.Get());
 
@@ -700,7 +714,11 @@
                 case Command::CopyTextureToTexture: {
                     CopyTextureToTextureCmd* copy =
                         mCommands.NextCommand<CopyTextureToTextureCmd>();
-
+                    if (copy->copySize.width == 0 || copy->copySize.height == 0 ||
+                        copy->copySize.depthOrArrayLayers == 0) {
+                        // Skip no-op copies.
+                        continue;
+                    }
                     Texture* source = ToBackend(copy->source.texture.Get());
                     Texture* destination = ToBackend(copy->destination.texture.Get());
 
diff --git a/src/dawn_native/metal/CommandBufferMTL.mm b/src/dawn_native/metal/CommandBufferMTL.mm
index a9f1896..b390184 100644
--- a/src/dawn_native/metal/CommandBufferMTL.mm
+++ b/src/dawn_native/metal/CommandBufferMTL.mm
@@ -704,6 +704,10 @@
 
                 case Command::CopyBufferToBuffer: {
                     CopyBufferToBufferCmd* copy = mCommands.NextCommand<CopyBufferToBufferCmd>();
+                    if (copy->size == 0) {
+                        // Skip no-op copies.
+                        break;
+                    }
 
                     ToBackend(copy->source)->EnsureDataInitialized(commandContext);
                     ToBackend(copy->destination)
@@ -721,6 +725,11 @@
 
                 case Command::CopyBufferToTexture: {
                     CopyBufferToTextureCmd* copy = mCommands.NextCommand<CopyBufferToTextureCmd>();
+                    if (copy->copySize.width == 0 || copy->copySize.height == 0 ||
+                        copy->copySize.depthOrArrayLayers == 0) {
+                        // Skip no-op copies.
+                        continue;
+                    }
                     auto& src = copy->source;
                     auto& dst = copy->destination;
                     auto& copySize = copy->copySize;
@@ -739,6 +748,11 @@
 
                 case Command::CopyTextureToBuffer: {
                     CopyTextureToBufferCmd* copy = mCommands.NextCommand<CopyTextureToBufferCmd>();
+                    if (copy->copySize.width == 0 || copy->copySize.height == 0 ||
+                        copy->copySize.depthOrArrayLayers == 0) {
+                        // Skip no-op copies.
+                        continue;
+                    }
                     auto& src = copy->source;
                     auto& dst = copy->destination;
                     auto& copySize = copy->copySize;
@@ -814,6 +828,11 @@
                 case Command::CopyTextureToTexture: {
                     CopyTextureToTextureCmd* copy =
                         mCommands.NextCommand<CopyTextureToTextureCmd>();
+                    if (copy->copySize.width == 0 || copy->copySize.height == 0 ||
+                        copy->copySize.depthOrArrayLayers == 0) {
+                        // Skip no-op copies.
+                        continue;
+                    }
                     Texture* srcTexture = ToBackend(copy->source.texture.Get());
                     Texture* dstTexture = ToBackend(copy->destination.texture.Get());
 
diff --git a/src/dawn_native/opengl/CommandBufferGL.cpp b/src/dawn_native/opengl/CommandBufferGL.cpp
index 40eb26b..085a8f0 100644
--- a/src/dawn_native/opengl/CommandBufferGL.cpp
+++ b/src/dawn_native/opengl/CommandBufferGL.cpp
@@ -612,6 +612,10 @@
 
                 case Command::CopyBufferToBuffer: {
                     CopyBufferToBufferCmd* copy = mCommands.NextCommand<CopyBufferToBufferCmd>();
+                    if (copy->size == 0) {
+                        // Skip no-op copies.
+                        break;
+                    }
 
                     ToBackend(copy->source)->EnsureDataInitialized();
                     ToBackend(copy->destination)
@@ -630,6 +634,11 @@
 
                 case Command::CopyBufferToTexture: {
                     CopyBufferToTextureCmd* copy = mCommands.NextCommand<CopyBufferToTextureCmd>();
+                    if (copy->copySize.width == 0 || copy->copySize.height == 0 ||
+                        copy->copySize.depthOrArrayLayers == 0) {
+                        // Skip no-op copies.
+                        continue;
+                    }
                     auto& src = copy->source;
                     auto& dst = copy->destination;
                     Buffer* buffer = ToBackend(src.buffer.Get());
@@ -664,6 +673,11 @@
 
                 case Command::CopyTextureToBuffer: {
                     CopyTextureToBufferCmd* copy = mCommands.NextCommand<CopyTextureToBufferCmd>();
+                    if (copy->copySize.width == 0 || copy->copySize.height == 0 ||
+                        copy->copySize.depthOrArrayLayers == 0) {
+                        // Skip no-op copies.
+                        continue;
+                    }
                     auto& src = copy->source;
                     auto& dst = copy->destination;
                     auto& copySize = copy->copySize;
@@ -771,6 +785,11 @@
                 case Command::CopyTextureToTexture: {
                     CopyTextureToTextureCmd* copy =
                         mCommands.NextCommand<CopyTextureToTextureCmd>();
+                    if (copy->copySize.width == 0 || copy->copySize.height == 0 ||
+                        copy->copySize.depthOrArrayLayers == 0) {
+                        // Skip no-op copies.
+                        continue;
+                    }
                     auto& src = copy->source;
                     auto& dst = copy->destination;
 
diff --git a/src/dawn_native/vulkan/CommandBufferVk.cpp b/src/dawn_native/vulkan/CommandBufferVk.cpp
index 2383e90..6409079 100644
--- a/src/dawn_native/vulkan/CommandBufferVk.cpp
+++ b/src/dawn_native/vulkan/CommandBufferVk.cpp
@@ -516,6 +516,10 @@
             switch (type) {
                 case Command::CopyBufferToBuffer: {
                     CopyBufferToBufferCmd* copy = mCommands.NextCommand<CopyBufferToBufferCmd>();
+                    if (copy->size == 0) {
+                        // Skip no-op copies.
+                        break;
+                    }
 
                     Buffer* srcBuffer = ToBackend(copy->source.Get());
                     Buffer* dstBuffer = ToBackend(copy->destination.Get());
@@ -540,6 +544,11 @@
 
                 case Command::CopyBufferToTexture: {
                     CopyBufferToTextureCmd* copy = mCommands.NextCommand<CopyBufferToTextureCmd>();
+                    if (copy->copySize.width == 0 || copy->copySize.height == 0 ||
+                        copy->copySize.depthOrArrayLayers == 0) {
+                        // Skip no-op copies.
+                        continue;
+                    }
                     auto& src = copy->source;
                     auto& dst = copy->destination;
 
@@ -578,6 +587,11 @@
 
                 case Command::CopyTextureToBuffer: {
                     CopyTextureToBufferCmd* copy = mCommands.NextCommand<CopyTextureToBufferCmd>();
+                    if (copy->copySize.width == 0 || copy->copySize.height == 0 ||
+                        copy->copySize.depthOrArrayLayers == 0) {
+                        // Skip no-op copies.
+                        continue;
+                    }
                     auto& src = copy->source;
                     auto& dst = copy->destination;
 
@@ -610,6 +624,11 @@
                 case Command::CopyTextureToTexture: {
                     CopyTextureToTextureCmd* copy =
                         mCommands.NextCommand<CopyTextureToTextureCmd>();
+                    if (copy->copySize.width == 0 || copy->copySize.height == 0 ||
+                        copy->copySize.depthOrArrayLayers == 0) {
+                        // Skip no-op copies.
+                        continue;
+                    }
                     TextureCopy& src = copy->source;
                     TextureCopy& dst = copy->destination;
                     SubresourceRange srcRange = GetSubresourcesAffectedByCopy(src, copy->copySize);
diff --git a/src/tests/unittests/validation/CopyCommandsValidationTests.cpp b/src/tests/unittests/validation/CopyCommandsValidationTests.cpp
index dbbc3327..c75c50d 100644
--- a/src/tests/unittests/validation/CopyCommandsValidationTests.cpp
+++ b/src/tests/unittests/validation/CopyCommandsValidationTests.cpp
@@ -226,6 +226,22 @@
     }
 }
 
+// Test a successful B2B copy where the last external reference is dropped.
+// This is a regression test for crbug.com/1217741 where submitting a command
+// buffer with dropped resources when the copy size is 0 was a use-after-free.
+TEST_F(CopyCommandTest_B2B, DroppedBuffer) {
+    wgpu::Buffer source = CreateBuffer(16, wgpu::BufferUsage::CopySrc);
+    wgpu::Buffer destination = CreateBuffer(16, wgpu::BufferUsage::CopyDst);
+
+    wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+    encoder.CopyBufferToBuffer(source, 0, destination, 0, 0);
+    wgpu::CommandBuffer commandBuffer = encoder.Finish();
+
+    source = nullptr;
+    destination = nullptr;
+    device.GetQueue().Submit(1, &commandBuffer);
+}
+
 // Test B2B copies with OOB
 TEST_F(CopyCommandTest_B2B, OutOfBounds) {
     wgpu::Buffer source = CreateBuffer(16, wgpu::BufferUsage::CopySrc);