Change Copy Operation Interfaces to Match WebGPU IDL

Cosmetic changes to copyBufferToTexture and copyTextureToBuffer to
match WebGPU IDL. Introduces BufferCopyView, TextureCopyView,
TextureAspect, and Origin3D types.

Bug: dawn:17
Change-Id: Ic0e7f472a9dc1353d3fc3839ff02f348bb6067e8
Reviewed-on: https://dawn-review.googlesource.com/c/2520
Commit-Queue: Brandon1 Jones <brandon1.jones@intel.com>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
diff --git a/dawn.json b/dawn.json
index 5837279..0437b81 100644
--- a/dawn.json
+++ b/dawn.json
@@ -240,6 +240,16 @@
             }
         ]
     },
+    "buffer copy view": {
+        "category": "structure",
+        "extensible": true,
+        "members": [
+            {"name": "buffer", "type": "buffer"},
+            {"name": "offset", "type": "uint32_t"},
+            {"name": "row pitch", "type": "uint32_t"},
+            {"name": "image height", "type": "uint32_t"}
+        ]
+    },
     "buffer descriptor": {
         "category": "structure",
         "extensible": true,
@@ -350,47 +360,17 @@
             {
                 "name": "copy buffer to texture",
                 "args": [
-                    {"name": "buffer", "type": "buffer"},
-                    {"name": "buffer offset", "type": "uint32_t"},
-                    {"name": "row pitch", "type": "uint32_t"},
-                    {"name": "texture", "type": "texture"},
-                    {"name": "x", "type": "uint32_t"},
-                    {"name": "y", "type": "uint32_t"},
-                    {"name": "z", "type": "uint32_t"},
-                    {"name": "width", "type": "uint32_t"},
-                    {"name": "height", "type": "uint32_t"},
-                    {"name": "depth", "type": "uint32_t"},
-                    {"name": "level", "type": "uint32_t"},
-                    {"name": "slice", "type": "uint32_t"}
-                ],
-                "TODO": [
-                    "Make pretty with Offset and Extents structures",
-                    "Allow choosing the aspect (depth vs. stencil)?",
-                    "Add these arguments too",
-                    {"name": "image height", "type": "uint32_t"}
+                    {"name": "source", "type": "buffer copy view", "annotation": "const*"},
+                    {"name": "destination", "type": "texture copy view", "annotation": "const*"},
+                    {"name": "copy size", "type": "extent 3D", "annotation": "const*"}
                 ]
             },
             {
                 "name": "copy texture to buffer",
                 "args": [
-                    {"name": "texture", "type": "texture"},
-                    {"name": "x", "type": "uint32_t"},
-                    {"name": "y", "type": "uint32_t"},
-                    {"name": "z", "type": "uint32_t"},
-                    {"name": "width", "type": "uint32_t"},
-                    {"name": "height", "type": "uint32_t"},
-                    {"name": "depth", "type": "uint32_t"},
-                    {"name": "level", "type": "uint32_t"},
-                    {"name": "slice", "type": "uint32_t"},
-                    {"name": "buffer", "type": "buffer"},
-                    {"name": "buffer offset", "type": "uint32_t"},
-                    {"name": "row pitch", "type": "uint32_t"}
-                ],
-                "TODO": [
-                    "Make pretty with Offset and Extents structures",
-                    "Allow choosing the aspect (depth vs. stencil)?",
-                    "Add these arguments too",
-                    {"name": "image height", "type": "uint32_t"}
+                    {"name": "source", "type": "texture copy view", "annotation": "const*"},
+                    {"name": "destination", "type": "buffer copy view", "annotation": "const*"},
+                    {"name": "copy size", "type": "extent 3D", "annotation": "const*"}
                 ]
             }
         ]
@@ -689,6 +669,14 @@
             {"value": 1, "name": "load"}
         ]
     },
+    "origin 3D": {
+        "category": "structure",
+        "members": [
+            {"name": "x", "type": "uint32_t"},
+            {"name": "y", "type": "uint32_t"},
+            {"name": "z", "type": "uint32_t"}
+        ]
+    },
     "pipeline layout": {
         "category": "object"
     },
@@ -1046,6 +1034,25 @@
             }
         ]
     },
+    "texture aspect": {
+        "category": "bitmask",
+        "values": [
+            {"value": 1, "name": "color"},
+            {"value": 2, "name": "depth"},
+            {"value": 4, "name": "stencil"}
+        ]
+    },
+    "texture copy view": {
+        "category": "structure",
+        "extensible": true,
+        "members": [
+            {"name": "texture", "type": "texture"},
+            {"name": "level", "type": "uint32_t"},
+            {"name": "slice", "type": "uint32_t"},
+            {"name": "origin", "type": "origin 3D"},
+            {"name": "aspect", "type": "texture aspect"}
+        ]
+    },
     "texture descriptor": {
         "category": "structure",
         "extensible": true,
diff --git a/examples/CppHelloTriangle.cpp b/examples/CppHelloTriangle.cpp
index c1a8f50..de60b2e 100644
--- a/examples/CppHelloTriangle.cpp
+++ b/examples/CppHelloTriangle.cpp
@@ -68,11 +68,15 @@
         data[i] = static_cast<uint8_t>(i % 253);
     }
 
-
     dawn::Buffer stagingBuffer = utils::CreateBufferFromData(device, data.data(), static_cast<uint32_t>(data.size()), dawn::BufferUsageBit::TransferSrc);
-    dawn::CommandBuffer copy = device.CreateCommandBufferBuilder()
-        .CopyBufferToTexture(stagingBuffer, 0, 0, texture, 0, 0, 0, 1024, 1024, 1, 0, 0)
-        .GetResult();
+    dawn::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(stagingBuffer, 0, 0, 0);
+    dawn::TextureCopyView textureCopyView =
+        utils::CreateTextureCopyView(texture, 0, 0, {0, 0, 0}, dawn::TextureAspect::Color);
+    dawn::Extent3D copySize = {1024, 1024, 1};
+    dawn::CommandBuffer copy =
+        device.CreateCommandBufferBuilder()
+            .CopyBufferToTexture(&bufferCopyView, &textureCopyView, &copySize)
+            .GetResult();
 
     queue.Submit(1, &copy);
 }
diff --git a/examples/glTFViewer/glTFViewer.cpp b/examples/glTFViewer/glTFViewer.cpp
index 5bbf838..873a246 100644
--- a/examples/glTFViewer/glTFViewer.cpp
+++ b/examples/glTFViewer/glTFViewer.cpp
@@ -433,9 +433,14 @@
             }
 
             dawn::Buffer staging = utils::CreateBufferFromData(device, data, rowPitch * iImage.height, dawn::BufferUsageBit::TransferSrc);
+            dawn::BufferCopyView bufferCopyView =
+                utils::CreateBufferCopyView(staging, 0, rowPitch, 0);
+            dawn::TextureCopyView textureCopyView =
+                utils::CreateTextureCopyView(oTexture, 0, 0, {0, 0, 0}, dawn::TextureAspect::Color);
+            dawn::Extent3D copySize = {iImage.width, iImage.height, 1};
             auto cmdbuf = device.CreateCommandBufferBuilder()
-                .CopyBufferToTexture(staging, 0, rowPitch, oTexture, 0, 0, 0, iImage.width, iImage.height, 1, 0, 0)
-                .GetResult();
+                              .CopyBufferToTexture(&bufferCopyView, &textureCopyView, &copySize)
+                              .GetResult();
             queue.Submit(1, &cmdbuf);
 
             textures[iTextureID] = oTexture.CreateDefaultTextureView();
diff --git a/src/dawn_native/CommandBuffer.cpp b/src/dawn_native/CommandBuffer.cpp
index b5ff98a..8ddcc15 100644
--- a/src/dawn_native/CommandBuffer.cpp
+++ b/src/dawn_native/CommandBuffer.cpp
@@ -645,76 +645,58 @@
         copy->size = size;
     }
 
-    void CommandBufferBuilder::CopyBufferToTexture(BufferBase* buffer,
-                                                   uint32_t bufferOffset,
-                                                   uint32_t rowPitch,
-                                                   TextureBase* texture,
-                                                   uint32_t x,
-                                                   uint32_t y,
-                                                   uint32_t z,
-                                                   uint32_t width,
-                                                   uint32_t height,
-                                                   uint32_t depth,
-                                                   uint32_t level,
-                                                   uint32_t slice) {
+    void CommandBufferBuilder::CopyBufferToTexture(const BufferCopyView* source,
+                                                   const TextureCopyView* destination,
+                                                   const Extent3D* copySize) {
         if (ConsumedError(ValidateCanRecordTopLevelCommands())) {
             return;
         }
-
-        if (rowPitch == 0) {
-            rowPitch = ComputeDefaultRowPitch(texture, width);
-        }
         CopyBufferToTextureCmd* copy =
             mAllocator.Allocate<CopyBufferToTextureCmd>(Command::CopyBufferToTexture);
         new (copy) CopyBufferToTextureCmd;
-        copy->source.buffer = buffer;
-        copy->source.offset = bufferOffset;
-        copy->destination.texture = texture;
-        copy->destination.x = x;
-        copy->destination.y = y;
-        copy->destination.z = z;
-        copy->destination.width = width;
-        copy->destination.height = height;
-        copy->destination.depth = depth;
-        copy->destination.level = level;
-        copy->destination.slice = slice;
-        copy->rowPitch = rowPitch;
+        copy->source.buffer = source->buffer;
+        copy->source.offset = source->offset;
+        copy->destination.texture = destination->texture;
+        copy->destination.x = destination->origin.x;
+        copy->destination.y = destination->origin.y;
+        copy->destination.z = destination->origin.z;
+        copy->destination.width = copySize->width;
+        copy->destination.height = copySize->height;
+        copy->destination.depth = copySize->depth;
+        copy->destination.level = destination->level;
+        copy->destination.slice = destination->slice;
+        if (source->rowPitch == 0) {
+            copy->rowPitch = ComputeDefaultRowPitch(destination->texture, copySize->width);
+        } else {
+            copy->rowPitch = source->rowPitch;
+        }
     }
 
-    void CommandBufferBuilder::CopyTextureToBuffer(TextureBase* texture,
-                                                   uint32_t x,
-                                                   uint32_t y,
-                                                   uint32_t z,
-                                                   uint32_t width,
-                                                   uint32_t height,
-                                                   uint32_t depth,
-                                                   uint32_t level,
-                                                   uint32_t slice,
-                                                   BufferBase* buffer,
-                                                   uint32_t bufferOffset,
-                                                   uint32_t rowPitch) {
+    void CommandBufferBuilder::CopyTextureToBuffer(const TextureCopyView* source,
+                                                   const BufferCopyView* destination,
+                                                   const Extent3D* copySize) {
         if (ConsumedError(ValidateCanRecordTopLevelCommands())) {
             return;
         }
-
-        if (rowPitch == 0) {
-            rowPitch = ComputeDefaultRowPitch(texture, width);
-        }
         CopyTextureToBufferCmd* copy =
             mAllocator.Allocate<CopyTextureToBufferCmd>(Command::CopyTextureToBuffer);
         new (copy) CopyTextureToBufferCmd;
-        copy->source.texture = texture;
-        copy->source.x = x;
-        copy->source.y = y;
-        copy->source.z = z;
-        copy->source.width = width;
-        copy->source.height = height;
-        copy->source.depth = depth;
-        copy->source.level = level;
-        copy->source.slice = slice;
-        copy->destination.buffer = buffer;
-        copy->destination.offset = bufferOffset;
-        copy->rowPitch = rowPitch;
+        copy->source.texture = source->texture;
+        copy->source.x = source->origin.x;
+        copy->source.y = source->origin.y;
+        copy->source.z = source->origin.z;
+        copy->source.width = copySize->width;
+        copy->source.height = copySize->height;
+        copy->source.depth = copySize->depth;
+        copy->source.level = source->level;
+        copy->source.slice = source->slice;
+        copy->destination.buffer = destination->buffer;
+        copy->destination.offset = destination->offset;
+        if (destination->rowPitch == 0) {
+            copy->rowPitch = ComputeDefaultRowPitch(source->texture, copySize->width);
+        } else {
+            copy->rowPitch = destination->rowPitch;
+        }
     }
 
 }  // namespace dawn_native
diff --git a/src/dawn_native/CommandBuffer.h b/src/dawn_native/CommandBuffer.h
index 75fcef8..27f126a 100644
--- a/src/dawn_native/CommandBuffer.h
+++ b/src/dawn_native/CommandBuffer.h
@@ -67,30 +67,12 @@
                                 BufferBase* destination,
                                 uint32_t destinationOffset,
                                 uint32_t size);
-        void CopyBufferToTexture(BufferBase* buffer,
-                                 uint32_t bufferOffset,
-                                 uint32_t rowPitch,
-                                 TextureBase* texture,
-                                 uint32_t x,
-                                 uint32_t y,
-                                 uint32_t z,
-                                 uint32_t width,
-                                 uint32_t height,
-                                 uint32_t depth,
-                                 uint32_t level,
-                                 uint32_t slice);
-        void CopyTextureToBuffer(TextureBase* texture,
-                                 uint32_t x,
-                                 uint32_t y,
-                                 uint32_t z,
-                                 uint32_t width,
-                                 uint32_t height,
-                                 uint32_t depth,
-                                 uint32_t level,
-                                 uint32_t slice,
-                                 BufferBase* buffer,
-                                 uint32_t bufferOffset,
-                                 uint32_t rowPitch);
+        void CopyBufferToTexture(const BufferCopyView* source,
+                                 const TextureCopyView* destination,
+                                 const Extent3D* copySize);
+        void CopyTextureToBuffer(const TextureCopyView* source,
+                                 const BufferCopyView* destination,
+                                 const Extent3D* copySize);
 
         // Functions to interact with the encoders
         bool ConsumedError(MaybeError maybeError) {
diff --git a/src/tests/DawnTest.cpp b/src/tests/DawnTest.cpp
index f58dd2b..8001941 100644
--- a/src/tests/DawnTest.cpp
+++ b/src/tests/DawnTest.cpp
@@ -301,10 +301,14 @@
 
     // We need to enqueue the copy immediately because by the time we resolve the expectation,
     // the texture might have been modified.
+    dawn::TextureCopyView textureCopyView =
+        utils::CreateTextureCopyView(texture, level, 0, {x, y, 0}, dawn::TextureAspect::Color);
+    dawn::BufferCopyView bufferCopyView =
+        utils::CreateBufferCopyView(readback.buffer, readback.offset, rowPitch, 0);
+    dawn::Extent3D copySize = {width, height, 1};
     dawn::CommandBuffer commands =
         device.CreateCommandBufferBuilder()
-            .CopyTextureToBuffer(texture, x, y, 0, width, height, 1, level, 0, readback.buffer,
-                                 readback.offset, rowPitch)
+            .CopyTextureToBuffer(&textureCopyView, &bufferCopyView, &copySize)
             .GetResult();
 
     queue.Submit(1, &commands);
diff --git a/src/tests/end2end/BindGroupTests.cpp b/src/tests/end2end/BindGroupTests.cpp
index 429abda..42dd704 100644
--- a/src/tests/end2end/BindGroupTests.cpp
+++ b/src/tests/end2end/BindGroupTests.cpp
@@ -258,7 +258,12 @@
         .GetResult();
 
     dawn::CommandBufferBuilder builder = device.CreateCommandBufferBuilder();
-    builder.CopyBufferToTexture(stagingBuffer, 0, widthInBytes, texture, 0, 0, 0, width, height, 1, 0, 0);
+    dawn::BufferCopyView bufferCopyView =
+        utils::CreateBufferCopyView(stagingBuffer, 0, widthInBytes, 0);
+    dawn::TextureCopyView textureCopyView =
+        utils::CreateTextureCopyView(texture, 0, 0, {0, 0, 0}, dawn::TextureAspect::Color);
+    dawn::Extent3D copySize = {width, height, 1};
+    builder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, &copySize);
     dawn::RenderPassEncoder pass = builder.BeginRenderPass(renderPass.renderPassInfo);
     pass.SetRenderPipeline(pipeline);
     pass.SetBindGroup(0, bindGroup);
diff --git a/src/tests/end2end/CopyTests.cpp b/src/tests/end2end/CopyTests.cpp
index 0c04a04..3732303 100644
--- a/src/tests/end2end/CopyTests.cpp
+++ b/src/tests/end2end/CopyTests.cpp
@@ -100,8 +100,12 @@
                 // Create an upload buffer and use it to populate the current slice of the texture in `level` mip level
                 dawn::Buffer uploadBuffer = utils::CreateBufferFromData(device, textureArrayData[slice].data(),
                     static_cast<uint32_t>(sizeof(RGBA8) * textureArrayData[slice].size()), dawn::BufferUsageBit::TransferSrc);
-
-                cmdBuilder.CopyBufferToTexture(uploadBuffer, 0, rowPitch, texture, 0, 0, 0, width, height, 1, textureSpec.level, slice);
+                dawn::BufferCopyView bufferCopyView =
+                    utils::CreateBufferCopyView(uploadBuffer, 0, rowPitch, 0);
+                dawn::TextureCopyView textureCopyView = utils::CreateTextureCopyView(
+                    texture, textureSpec.level, slice, {0, 0, 0}, dawn::TextureAspect::Color);
+                dawn::Extent3D copySize = {width, height, 1};
+                cmdBuilder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, &copySize);
             }
 
             // Create a buffer of size `size * textureSpec.arrayLayer` and populate it with empty data (0,0,0,0)
@@ -117,7 +121,13 @@
             uint32_t bufferOffset = bufferSpec.offset;
             for (uint32_t slice = 0; slice < textureSpec.arrayLayer; ++slice) {
                 // Copy the region [(`x`, `y`), (`x + copyWidth, `y + copyWidth`)] from the `level` mip into the buffer at `offset + bufferSpec.size * slice` and `rowPitch`
-                cmdBuilder.CopyTextureToBuffer(texture, textureSpec.x, textureSpec.y, 0, textureSpec.copyWidth, textureSpec.copyHeight, 1, textureSpec.level, slice, buffer, bufferOffset, bufferSpec.rowPitch);
+                dawn::TextureCopyView textureCopyView = utils::CreateTextureCopyView(
+                    texture, textureSpec.level, slice, {textureSpec.x, textureSpec.y, 0},
+                    dawn::TextureAspect::Color);
+                dawn::BufferCopyView bufferCopyView =
+                    utils::CreateBufferCopyView(buffer, bufferOffset, bufferSpec.rowPitch, 0);
+                dawn::Extent3D copySize = {textureSpec.copyWidth, textureSpec.copyHeight, 1};
+                cmdBuilder.CopyTextureToBuffer(&textureCopyView, &bufferCopyView, &copySize);
                 bufferOffset += bufferSpec.size;
             }
 
@@ -198,12 +208,24 @@
 
             std::vector<RGBA8> emptyData(texelCount);
             dawn::Buffer uploadBuffer = utils::CreateBufferFromData(device, emptyData.data(), static_cast<uint32_t>(sizeof(RGBA8) * emptyData.size()), dawn::BufferUsageBit::TransferSrc);
-
-            cmdBuilder.CopyBufferToTexture(uploadBuffer, 0, rowPitch, texture, 0, 0, 0, width, height, 1, textureSpec.level, 0);
+            dawn::BufferCopyView bufferCopyView =
+                utils::CreateBufferCopyView(uploadBuffer, 0, rowPitch, 0);
+            dawn::TextureCopyView textureCopyView = utils::CreateTextureCopyView(
+                texture, textureSpec.level, 0, {0, 0, 0}, dawn::TextureAspect::Color);
+            dawn::Extent3D copySize = {width, height, 1};
+            cmdBuilder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, &copySize);
         }
 
         // Copy to the region [(`x`, `y`), (`x + copyWidth, `y + copyWidth`)] at the `level` mip from the buffer at the specified `offset` and `rowPitch`
-        cmdBuilder.CopyBufferToTexture(buffer, bufferSpec.offset, bufferSpec.rowPitch, texture, textureSpec.x, textureSpec.y, 0, textureSpec.copyWidth, textureSpec.copyHeight, 1, textureSpec.level, 0);
+        {
+            dawn::BufferCopyView bufferCopyView =
+                utils::CreateBufferCopyView(buffer, bufferSpec.offset, bufferSpec.rowPitch, 0);
+            dawn::TextureCopyView textureCopyView = utils::CreateTextureCopyView(
+                texture, textureSpec.level, 0, {textureSpec.x, textureSpec.y, 0},
+                dawn::TextureAspect::Color);
+            dawn::Extent3D copySize = {textureSpec.copyWidth, textureSpec.copyHeight, 1};
+            cmdBuilder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, &copySize);
+        }
 
         dawn::CommandBuffer commands = cmdBuilder.GetResult();
         queue.Submit(1, &commands);
diff --git a/src/tests/end2end/SamplerTests.cpp b/src/tests/end2end/SamplerTests.cpp
index 36c24bd..9de5d63 100644
--- a/src/tests/end2end/SamplerTests.cpp
+++ b/src/tests/end2end/SamplerTests.cpp
@@ -100,9 +100,14 @@
         data[1] = data[rowPixels] = white;
 
         dawn::Buffer stagingBuffer = utils::CreateBufferFromData(device, data, sizeof(data), dawn::BufferUsageBit::TransferSrc);
-        dawn::CommandBuffer copy = device.CreateCommandBufferBuilder()
-            .CopyBufferToTexture(stagingBuffer, 0, 256, texture, 0, 0, 0, 2, 2, 1, 0, 0)
-            .GetResult();
+        dawn::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(stagingBuffer, 0, 256, 0);
+        dawn::TextureCopyView textureCopyView =
+            utils::CreateTextureCopyView(texture, 0, 0, {0, 0, 0}, dawn::TextureAspect::Color);
+        dawn::Extent3D copySize = {2, 2, 1};
+        dawn::CommandBuffer copy =
+            device.CreateCommandBufferBuilder()
+                .CopyBufferToTexture(&bufferCopyView, &textureCopyView, &copySize)
+                .GetResult();
 
         queue.Submit(1, &copy);
         mTextureView = texture.CreateDefaultTextureView();
diff --git a/src/tests/end2end/TextureViewTests.cpp b/src/tests/end2end/TextureViewTests.cpp
index c545883..a0ee5ad 100644
--- a/src/tests/end2end/TextureViewTests.cpp
+++ b/src/tests/end2end/TextureViewTests.cpp
@@ -124,9 +124,12 @@
                 dawn::Buffer stagingBuffer = utils::CreateBufferFromData(
                     device, data.data(), data.size() * sizeof(RGBA8),
                     dawn::BufferUsageBit::TransferSrc);
-                builder.CopyBufferToTexture(
-                    stagingBuffer, 0, kTextureRowPitchAlignment, mTexture, 0, 0, 0, texWidth,
-                    texHeight, 1, level, layer);
+                dawn::BufferCopyView bufferCopyView =
+                    utils::CreateBufferCopyView(stagingBuffer, 0, kTextureRowPitchAlignment, 0);
+                dawn::TextureCopyView textureCopyView = utils::CreateTextureCopyView(
+                    mTexture, level, layer, {0, 0, 0}, dawn::TextureAspect::Color);
+                dawn::Extent3D copySize = {texWidth, texHeight, 1};
+                builder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, &copySize);
             }
         }
         dawn::CommandBuffer copy = builder.GetResult();
diff --git a/src/tests/unittests/validation/CopyCommandsValidationTests.cpp b/src/tests/unittests/validation/CopyCommandsValidationTests.cpp
index 4789b02..636df88 100644
--- a/src/tests/unittests/validation/CopyCommandsValidationTests.cpp
+++ b/src/tests/unittests/validation/CopyCommandsValidationTests.cpp
@@ -15,6 +15,7 @@
 #include "common/Constants.h"
 #include "common/Math.h"
 #include "tests/unittests/validation/ValidationTest.h"
+#include "utils/DawnHelpers.h"
 
 class CopyCommandTest : public ValidationTest {
     protected:
@@ -45,6 +46,64 @@
             uint32_t rowPitch = Align(width * 4, kTextureRowPitchAlignment);
             return (rowPitch * (height - 1) + width) * depth;
         }
+
+        void TestB2TCopy(utils::Expectation expectation,
+                         dawn::Buffer srcBuffer,
+                         uint32_t srcOffset,
+                         uint32_t srcRowPitch,
+                         uint32_t srcImageHeight,
+                         dawn::Texture destTexture,
+                         uint32_t destLevel,
+                         uint32_t destSlice,
+                         dawn::Origin3D destOrigin,
+                         dawn::TextureAspect destAspect,
+                         dawn::Extent3D extent3D) {
+            dawn::BufferCopyView bufferCopyView =
+                utils::CreateBufferCopyView(srcBuffer, srcOffset, srcRowPitch, srcImageHeight);
+            dawn::TextureCopyView textureCopyView = utils::CreateTextureCopyView(
+                destTexture, destLevel, destSlice, destOrigin, destAspect);
+
+            if (expectation == utils::Expectation::Success) {
+                dawn::CommandBuffer commands =
+                    AssertWillBeSuccess(device.CreateCommandBufferBuilder())
+                        .CopyBufferToTexture(&bufferCopyView, &textureCopyView, &extent3D)
+                        .GetResult();
+            } else {
+                dawn::CommandBuffer commands =
+                    AssertWillBeError(device.CreateCommandBufferBuilder())
+                        .CopyBufferToTexture(&bufferCopyView, &textureCopyView, &extent3D)
+                        .GetResult();
+            }
+        }
+
+        void TestT2BCopy(utils::Expectation expectation,
+                         dawn::Texture srcTexture,
+                         uint32_t srcLevel,
+                         uint32_t srcSlice,
+                         dawn::Origin3D srcOrigin,
+                         dawn::TextureAspect srcAspect,
+                         dawn::Buffer destBuffer,
+                         uint32_t destOffset,
+                         uint32_t destRowPitch,
+                         uint32_t destImageHeight,
+                         dawn::Extent3D extent3D) {
+            dawn::BufferCopyView bufferCopyView =
+                utils::CreateBufferCopyView(destBuffer, destOffset, destRowPitch, destImageHeight);
+            dawn::TextureCopyView textureCopyView = utils::CreateTextureCopyView(
+                srcTexture, srcLevel, srcSlice, srcOrigin, srcAspect);
+
+            if (expectation == utils::Expectation::Success) {
+                dawn::CommandBuffer commands =
+                    AssertWillBeSuccess(device.CreateCommandBufferBuilder())
+                        .CopyTextureToBuffer(&textureCopyView, &bufferCopyView, &extent3D)
+                        .GetResult();
+            } else {
+                dawn::CommandBuffer commands =
+                    AssertWillBeError(device.CreateCommandBufferBuilder())
+                        .CopyTextureToBuffer(&textureCopyView, &bufferCopyView, &extent3D)
+                        .GetResult();
+            }
+        }
 };
 
 class CopyCommandTest_B2B : public CopyCommandTest {
@@ -129,40 +188,44 @@
 
     // Different copies, including some that touch the OOB condition
     {
-        dawn::CommandBuffer commands = AssertWillBeSuccess(device.CreateCommandBufferBuilder())
-            // Copy 4x4 block in corner of first mip.
-            .CopyBufferToTexture(source, 0, 256, destination, 0, 0, 0, 4, 4, 1, 0, 0)
-            // Copy 4x4 block in opposite corner of first mip.
-            .CopyBufferToTexture(source, 0, 256, destination, 12, 12, 0, 4, 4, 1, 0, 0)
-            // Copy 4x4 block in the 4x4 mip.
-            .CopyBufferToTexture(source, 0, 256, destination, 0, 0, 0, 4, 4, 1, 2, 0)
-            // Copy with a buffer offset
-            .CopyBufferToTexture(source, bufferSize - 4, 256, destination, 0, 0, 0, 1, 1, 1, 0, 0)
-            .GetResult();
+        // Copy 4x4 block in corner of first mip.
+        TestB2TCopy(utils::Expectation::Success, source, 0, 256, 0, destination, 0, 0, {0, 0, 0},
+                    dawn::TextureAspect::Color, {4, 4, 1});
+        // Copy 4x4 block in opposite corner of first mip.
+        TestB2TCopy(utils::Expectation::Success, source, 0, 256, 0, destination, 0, 0, {12, 12, 0},
+                    dawn::TextureAspect::Color, {4, 4, 1});
+        // Copy 4x4 block in the 4x4 mip.
+        TestB2TCopy(utils::Expectation::Success, source, 0, 256, 0, destination, 2, 0, {0, 0, 0},
+                    dawn::TextureAspect::Color, {4, 4, 1});
+        // Copy with a buffer offset
+        TestB2TCopy(utils::Expectation::Success, source, bufferSize - 4, 256, 0, destination, 0, 0,
+                    {0, 0, 0}, dawn::TextureAspect::Color, {1, 1, 1});
     }
 
     // Copies with a 256-byte aligned row pitch but unaligned texture region
     {
-        dawn::CommandBuffer commands = AssertWillBeSuccess(device.CreateCommandBufferBuilder())
-            // Unaligned region
-            .CopyBufferToTexture(source, 0, 256, destination, 0, 0, 0, 3, 4, 1, 0, 0)
-            // Unaligned region with texture offset
-            .CopyBufferToTexture(source, 0, 256, destination, 5, 7, 0, 2, 3, 1, 0, 0)
-            // Unaligned region, with buffer offset
-            .CopyBufferToTexture(source, 31 * 4, 256, destination, 0, 0, 0, 3, 3, 1, 0, 0)
-            .GetResult();
+        // Unaligned region
+        TestB2TCopy(utils::Expectation::Success, source, 0, 256, 0, destination, 0, 0, {0, 0, 0},
+                    dawn::TextureAspect::Color, {3, 4, 1});
+        // Unaligned region with texture offset
+        TestB2TCopy(utils::Expectation::Success, source, 0, 256, 0, destination, 0, 0, {5, 7, 0},
+                    dawn::TextureAspect::Color, {2, 3, 1});
+        // Unaligned region, with buffer offset
+        TestB2TCopy(utils::Expectation::Success, source, 31 * 4, 256, 0, destination, 0, 0, {0, 0, 0},
+                    dawn::TextureAspect::Color, {3, 3, 1});
     }
 
     // Empty copies are valid
     {
-        dawn::CommandBuffer commands = AssertWillBeSuccess(device.CreateCommandBufferBuilder())
-            // An empty copy
-            .CopyBufferToTexture(source, 0, 0, destination, 0, 0, 0, 0, 0, 1, 0, 0)
-            // An empty copy touching the end of the buffer
-            .CopyBufferToTexture(source, bufferSize, 0, destination, 0, 0, 0, 0, 0, 1, 0, 0)
-            // An empty copy touching the side of the texture
-            .CopyBufferToTexture(source, 0, 0, destination, 16, 16, 0, 0, 0, 1, 0, 0)
-            .GetResult();
+        // An empty copy
+        TestB2TCopy(utils::Expectation::Success, source, 0, 0, 0, destination, 0, 0, {0, 0, 0},
+                    dawn::TextureAspect::Color, {0, 0, 1});
+        // An empty copy touching the end of the buffer
+        TestB2TCopy(utils::Expectation::Success, source, bufferSize, 0, 0, destination, 0, 0, {0, 0,
+                    0}, dawn::TextureAspect::Color, {0, 0, 1});
+        // An empty copy touching the side of the texture
+        TestB2TCopy(utils::Expectation::Success, source, 0, 0, 0, destination, 0, 0, {16, 16, 0},
+                    dawn::TextureAspect::Color, {0, 0, 1});
     }
 }
 
@@ -174,25 +237,16 @@
                                                      dawn::TextureUsageBit::TransferDst);
 
     // OOB on the buffer because we copy too many pixels
-    {
-        dawn::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
-            .CopyBufferToTexture(source, 0, 256, destination, 0, 0, 0, 4, 5, 1, 0, 0)
-            .GetResult();
-    }
+    TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 0, destination, 0, 0, {0, 0, 0},
+                dawn::TextureAspect::Color, {4, 5, 1});
 
     // OOB on the buffer because of the offset
-    {
-        dawn::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
-            .CopyBufferToTexture(source, 4, 256, destination, 0, 0, 0, 4, 4, 1, 0, 0)
-            .GetResult();
-    }
+    TestB2TCopy(utils::Expectation::Failure, source, 4, 256, 0, destination, 0, 0, {0, 0, 0},
+                dawn::TextureAspect::Color, {4, 4, 1});
 
     // OOB on the buffer because (row pitch * (height - 1) + width) * depth overflows
-    {
-        dawn::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
-            .CopyBufferToTexture(source, 0, 512, destination, 0, 0, 0, 4, 3, 1, 0, 0)
-            .GetResult();
-    }
+    TestB2TCopy(utils::Expectation::Failure, source, 0, 512, 0, destination, 0, 0, {0, 0, 0},
+                dawn::TextureAspect::Color, {4, 3, 1});
 
     // Not OOB on the buffer although row pitch * height overflows
     // but (row pitch * (height - 1) + width) * depth does not overlow
@@ -200,9 +254,9 @@
         uint32_t sourceBufferSize = BufferSizeForTextureCopy(7, 3, 1);
         ASSERT_TRUE(256 * 3 > sourceBufferSize) << "row pitch * height should overflow buffer";
         dawn::Buffer sourceBuffer = CreateBuffer(sourceBufferSize, dawn::BufferUsageBit::TransferSrc);
-        dawn::CommandBuffer commands = AssertWillBeSuccess(device.CreateCommandBufferBuilder())
-            .CopyBufferToTexture(sourceBuffer, 0, 256, destination, 0, 0, 0, 7, 3, 1, 0, 0)
-            .GetResult();
+
+        TestB2TCopy(utils::Expectation::Success, source, 0, 256, 0, destination, 0, 0, {0, 0, 0},
+                    dawn::TextureAspect::Color, {7, 3, 1});
     }
 }
 
@@ -214,39 +268,24 @@
                                                      dawn::TextureUsageBit::TransferDst);
 
     // OOB on the texture because x + width overflows
-    {
-        dawn::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
-            .CopyBufferToTexture(source, 0, 256, destination, 13, 12, 0, 4, 4, 1, 0, 0)
-            .GetResult();
-    }
+    TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 0, destination, 0, 0, {13, 12, 0},
+                dawn::TextureAspect::Color, {4, 4, 1});
 
     // OOB on the texture because y + width overflows
-    {
-        dawn::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
-            .CopyBufferToTexture(source, 0, 256, destination, 12, 13, 0, 4, 4, 1, 0, 0)
-            .GetResult();
-    }
+    TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 0, destination, 0, 0, {12, 13, 0},
+                dawn::TextureAspect::Color, {4, 4, 1});
 
     // OOB on the texture because we overflow a non-zero mip
-    {
-        dawn::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
-            .CopyBufferToTexture(source, 0, 256, destination, 1, 0, 0, 4, 4, 1, 2, 0)
-            .GetResult();
-    }
+    TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 0, destination, 2, 0, {1, 0, 0},
+                dawn::TextureAspect::Color, {4, 4, 1});
 
     // OOB on the texture even on an empty copy when we copy to a non-existent mip.
-    {
-        dawn::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
-            .CopyBufferToTexture(source, 0, 0, destination, 0, 0, 0, 0, 0, 1, 5, 0)
-            .GetResult();
-    }
+    TestB2TCopy(utils::Expectation::Failure, source, 0, 0, 0, destination, 5, 0, {0, 0, 0},
+                dawn::TextureAspect::Color, {0, 0, 1});
 
     // OOB on the texture because slice overflows
-    {
-        dawn::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
-            .CopyBufferToTexture(source, 0, 0, destination, 0, 0, 0, 0, 0, 1, 0, 2)
-            .GetResult();
-    }
+    TestB2TCopy(utils::Expectation::Failure, source, 0, 0, 0, destination, 0, 2, {0, 0, 0},
+                dawn::TextureAspect::Color, {0, 0, 1});
 }
 
 // Test that we force Z=0 and Depth=1 on copies to 2D textures
@@ -256,18 +295,12 @@
                                                      dawn::TextureUsageBit::TransferDst);
 
     // Z=1 on an empty copy still errors
-    {
-        dawn::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
-            .CopyBufferToTexture(source, 0, 0, destination, 0, 0, 1, 0, 0, 1, 0, 0)
-            .GetResult();
-    }
+    TestB2TCopy(utils::Expectation::Failure, source, 0, 0, 0, destination, 0, 0, {0, 0, 1},
+                dawn::TextureAspect::Color, {0, 0, 1});
 
     // Depth=0 on an empty copy still errors
-    {
-        dawn::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
-            .CopyBufferToTexture(source, 0, 0, destination, 0, 0, 0, 0, 0, 0, 0, 0)
-            .GetResult();
-    }
+    TestB2TCopy(utils::Expectation::Failure, source, 0, 0, 0, destination, 0, 0, {0, 0, 0},
+                dawn::TextureAspect::Color, {0, 0, 0});
 }
 
 // Test B2T copies with incorrect buffer usage
@@ -280,18 +313,12 @@
                                                  dawn::TextureUsageBit::Sampled);
 
     // Incorrect source usage
-    {
-        dawn::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
-            .CopyBufferToTexture(vertex, 0, 256, destination, 0, 0, 0, 4, 4, 1, 0, 0)
-            .GetResult();
-    }
+    TestB2TCopy(utils::Expectation::Failure, vertex, 0, 256, 0, destination, 0, 0, {0, 0, 0},
+                dawn::TextureAspect::Color, {4, 4, 1});
 
     // Incorrect destination usage
-    {
-        dawn::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
-            .CopyBufferToTexture(source, 0, 256, sampled, 0, 0, 0, 4, 4, 1, 0, 0)
-            .GetResult();
-    }
+    TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 0, sampled, 0, 0, {0, 0, 0},
+                dawn::TextureAspect::Color, {4, 4, 1});
 }
 
 TEST_F(CopyCommandTest_B2T, IncorrectRowPitch) {
@@ -301,25 +328,16 @@
         dawn::TextureUsageBit::TransferDst);
 
     // Default row pitch is not 256-byte aligned
-    {
-        dawn::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
-            .CopyBufferToTexture(source, 0, 0, destination, 0, 0, 0, 3, 4, 1, 0, 0)
-            .GetResult();
-    }
+    TestB2TCopy(utils::Expectation::Failure, source, 0, 0, 0, destination, 0, 0, {0, 0, 0},
+                dawn::TextureAspect::Color, {3, 4, 1});
 
     // Row pitch is not 256-byte aligned
-    {
-        dawn::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
-            .CopyBufferToTexture(source, 0, 128, destination, 0, 0, 0, 4, 4, 1, 0, 0)
-            .GetResult();
-    }
+    TestB2TCopy(utils::Expectation::Failure, source, 0, 128, 0, destination, 0, 0, {0, 0, 0},
+                dawn::TextureAspect::Color, {4, 4, 1});
 
     // Row pitch is less than width * bytesPerPixel
-    {
-        dawn::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
-            .CopyBufferToTexture(source, 0, 256, destination, 0, 0, 0, 65, 1, 1, 0, 0)
-            .GetResult();
-    }
+    TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 0, destination, 0, 0, {0, 0, 0},
+                dawn::TextureAspect::Color, {65, 1, 1});
 }
 
 // Test B2T copies with incorrect buffer offset usage
@@ -330,26 +348,17 @@
                                                      dawn::TextureUsageBit::TransferDst);
 
     // Correct usage
-    {
-        dawn::CommandBuffer commands = AssertWillBeSuccess(device.CreateCommandBufferBuilder())
-            .CopyBufferToTexture(source, bufferSize - 4, 256, destination, 0, 0, 0, 1, 1, 1, 0, 0)
-            .GetResult();
-    }
+    TestB2TCopy(utils::Expectation::Success, source, bufferSize - 4, 256, 0, destination, 0, 0, {0,
+                0, 0}, dawn::TextureAspect::Color, {1, 1, 1});
+
     // Incorrect usages
     {
-        dawn::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
-            .CopyBufferToTexture(source, bufferSize - 5, 256, destination, 0, 0, 0, 1, 1, 1, 0, 0)
-            .GetResult();
-    }
-    {
-        dawn::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
-            .CopyBufferToTexture(source, bufferSize - 6, 256, destination, 0, 0, 0, 1, 1, 1, 0, 0)
-            .GetResult();
-    }
-    {
-        dawn::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
-            .CopyBufferToTexture(source, bufferSize - 7, 256, destination, 0, 0, 0, 1, 1, 1, 0, 0)
-            .GetResult();
+        TestB2TCopy(utils::Expectation::Failure, source, bufferSize - 5, 256, 0, destination, 0, 0,
+                    {0, 0, 0}, dawn::TextureAspect::Color, {1, 1, 1});
+        TestB2TCopy(utils::Expectation::Failure, source, bufferSize - 6, 256, 0, destination, 0, 0,
+                    {0, 0, 0}, dawn::TextureAspect::Color, {1, 1, 1});
+        TestB2TCopy(utils::Expectation::Failure, source, bufferSize - 7, 256, 0, destination, 0, 0,
+                    {0, 0, 0}, dawn::TextureAspect::Color, {1, 1, 1});
     }
 }
 
@@ -365,40 +374,44 @@
 
     // Different copies, including some that touch the OOB condition
     {
-        dawn::CommandBuffer commands = AssertWillBeSuccess(device.CreateCommandBufferBuilder())
-            // Copy from 4x4 block in corner of first mip.
-            .CopyTextureToBuffer(source, 0, 0, 0, 4, 4, 1, 0, 0, destination, 0, 256)
-            // Copy from 4x4 block in opposite corner of first mip.
-            .CopyTextureToBuffer(source, 12, 12, 0, 4, 4, 1, 0, 0, destination, 0, 256)
-            // Copy from 4x4 block in the 4x4 mip.
-            .CopyTextureToBuffer(source, 0, 0, 0, 4, 4, 1, 2, 0, destination, 0, 256)
-            // Copy with a buffer offset
-            .CopyTextureToBuffer(source, 0, 0, 0, 1, 1, 1, 0, 0, destination, bufferSize - 4, 256)
-            .GetResult();
+        // Copy from 4x4 block in corner of first mip.
+        TestT2BCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, dawn::TextureAspect::Color,
+                    destination, 0, 256, 0, {4, 4, 1});
+        // Copy from 4x4 block in opposite corner of first mip.
+        TestT2BCopy(utils::Expectation::Success, source, 0, 0, {12, 12, 0},
+                    dawn::TextureAspect::Color, destination, 0, 256, 0, {4, 4, 1});
+        // Copy from 4x4 block in the 4x4 mip.
+        TestT2BCopy(utils::Expectation::Success, source, 2, 0, {0, 0, 0}, dawn::TextureAspect::Color,
+                    destination, 0, 256, 0, {4, 4, 1});
+        // Copy with a buffer offset
+        TestT2BCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, dawn::TextureAspect::Color,
+                    destination, bufferSize - 4, 256, 0, {1, 1, 1});
     }
 
     // Copies with a 256-byte aligned row pitch but unaligned texture region
     {
-        dawn::CommandBuffer commands = AssertWillBeSuccess(device.CreateCommandBufferBuilder())
-            // Unaligned region
-            .CopyTextureToBuffer(source, 0, 0, 0, 3, 4, 1, 0, 0, destination, 0, 256)
-            // Unaligned region with texture offset
-            .CopyTextureToBuffer(source, 5, 7, 0, 2, 3, 1, 0, 0, destination, 0, 256)
-            // Unaligned region, with buffer offset
-            .CopyTextureToBuffer(source, 0, 0, 0, 3, 3, 1, 2, 0, destination, 31 * 4, 256)
-            .GetResult();
+        // Unaligned region
+        TestT2BCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, dawn::TextureAspect::Color,
+                    destination, 0, 256, 0, {3, 4, 1});
+        // Unaligned region with texture offset
+        TestT2BCopy(utils::Expectation::Success, source, 0, 0, {5, 7, 0}, dawn::TextureAspect::Color,
+                    destination, 0, 256, 0, {2, 3, 1});
+        // Unaligned region, with buffer offset
+        TestT2BCopy(utils::Expectation::Success, source, 2, 0, {0, 0, 0}, dawn::TextureAspect::Color,
+                    destination, 31 * 4, 256, 0, {3, 3, 1});
     }
 
     // Empty copies are valid
     {
-        dawn::CommandBuffer commands = AssertWillBeSuccess(device.CreateCommandBufferBuilder())
-            // An empty copy
-            .CopyTextureToBuffer(source, 0, 0, 0, 0, 0, 1, 0, 0, destination, 0, 0)
-            // An empty copy touching the end of the buffer
-            .CopyTextureToBuffer(source, 0, 0, 0, 0, 0, 1, 0, 0, destination, bufferSize, 0)
-            // An empty copy touching the side of the texture
-            .CopyTextureToBuffer(source, 16, 16, 0, 0, 0, 1, 0, 0, destination, 0, 0)
-            .GetResult();
+        // An empty copy
+        TestT2BCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, dawn::TextureAspect::Color,
+                    destination, 0, 0, 0, {0, 0, 1});
+        // An empty copy touching the end of the buffer
+        TestT2BCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, dawn::TextureAspect::Color,
+                    destination, bufferSize, 0, 0, {0, 0, 1});
+        // An empty copy touching the side of the texture
+        TestT2BCopy(utils::Expectation::Success, source, 0, 0, {16, 16, 0},
+                    dawn::TextureAspect::Color, destination, 0, 0, 0, {0, 0, 1});
     }
 }
 
@@ -410,32 +423,20 @@
     dawn::Buffer destination = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferDst);
 
     // OOB on the texture because x + width overflows
-    {
-        dawn::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
-            .CopyTextureToBuffer(source, 13, 12, 0, 4, 4, 1, 0, 0, destination, 0, 256)
-            .GetResult();
-    }
+    TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {13, 12, 0}, dawn::TextureAspect::Color,
+                destination, 0, 256, 0, {4, 4, 1});
 
     // OOB on the texture because y + width overflows
-    {
-        dawn::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
-            .CopyTextureToBuffer(source, 12, 13, 0, 4, 4, 1, 0, 0, destination, 0, 256)
-            .GetResult();
-    }
+    TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {12, 13, 0}, dawn::TextureAspect::Color,
+                destination, 0, 256, 0, {4, 4, 1});
 
     // OOB on the texture because we overflow a non-zero mip
-    {
-        dawn::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
-            .CopyTextureToBuffer(source, 1, 0, 0, 4, 4, 1, 2, 0, destination, 0, 256)
-            .GetResult();
-    }
+    TestT2BCopy(utils::Expectation::Failure, source, 2, 0, {1, 0, 0}, dawn::TextureAspect::Color,
+                destination, 0, 256, 0, {4, 4, 1});
 
     // OOB on the texture even on an empty copy when we copy from a non-existent mip.
-    {
-        dawn::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
-            .CopyTextureToBuffer(source, 0, 0, 0, 0, 0, 1, 5, 0, destination, 0, 0)
-            .GetResult();
-    }
+    TestT2BCopy(utils::Expectation::Failure, source, 5, 0, {0, 0, 0}, dawn::TextureAspect::Color,
+                destination, 0, 0, 0, {0, 0, 1});
 }
 
 // Test OOB conditions on the buffer
@@ -446,25 +447,16 @@
     dawn::Buffer destination = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferDst);
 
     // OOB on the buffer because we copy too many pixels
-    {
-        dawn::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
-            .CopyTextureToBuffer(source, 0, 0, 0, 4, 5, 1, 0, 0, destination, 0, 256)
-            .GetResult();
-    }
+    TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, dawn::TextureAspect::Color,
+                destination, 0, 256, 0, {4, 5, 1});
 
     // OOB on the buffer because of the offset
-    {
-        dawn::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
-            .CopyTextureToBuffer(source, 0, 0, 0, 4, 4, 1, 0, 0, destination, 4, 256)
-            .GetResult();
-    }
+    TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, dawn::TextureAspect::Color,
+                destination, 4, 256, 0, {4, 4, 1});
 
     // OOB on the buffer because (row pitch * (height - 1) + width) * depth overflows
-    {
-        dawn::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
-            .CopyTextureToBuffer(source, 0, 0, 0, 4, 3, 1, 0, 0, destination, 0, 512)
-            .GetResult();
-    }
+    TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, dawn::TextureAspect::Color,
+                destination, 0, 512, 0, {4, 3, 1});
 
     // Not OOB on the buffer although row pitch * height overflows
     // but (row pitch * (height - 1) + width) * depth does not overlow
@@ -472,9 +464,8 @@
         uint32_t destinationBufferSize = BufferSizeForTextureCopy(7, 3, 1);
         ASSERT_TRUE(256 * 3 > destinationBufferSize) << "row pitch * height should overflow buffer";
         dawn::Buffer destinationBuffer = CreateBuffer(destinationBufferSize, dawn::BufferUsageBit::TransferDst);
-        dawn::CommandBuffer commands = AssertWillBeSuccess(device.CreateCommandBufferBuilder())
-            .CopyTextureToBuffer(source, 0, 0, 0, 7, 3, 1, 0, 0, destinationBuffer, 0, 256)
-            .GetResult();
+        TestT2BCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, dawn::TextureAspect::Color,
+                    destinationBuffer, 0, 256, 0, {7, 3, 1});
     }
 }
 
@@ -486,18 +477,12 @@
     dawn::Buffer destination = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferDst);
 
     // Z=1 on an empty copy still errors
-    {
-        dawn::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
-            .CopyTextureToBuffer(source, 0, 0, 1, 0, 0, 1, 0, 0, destination, 0, 0)
-            .GetResult();
-    }
+    TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 1}, dawn::TextureAspect::Color,
+                destination, 0, 0, 0, {0, 0, 1});
 
     // Depth=0 on an empty copy still errors
-    {
-        dawn::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
-            .CopyTextureToBuffer(source, 0, 0, 0, 0, 0, 0, 0, 0, destination, 0, 0)
-            .GetResult();
-    }
+    TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, dawn::TextureAspect::Color,
+                destination, 0, 0, 0, {0, 0, 0});
 }
 
 // Test T2B copies with incorrect buffer usage
@@ -511,18 +496,12 @@
     dawn::Buffer vertex = CreateBuffer(bufferSize, dawn::BufferUsageBit::Vertex);
 
     // Incorrect source usage
-    {
-        dawn::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
-            .CopyTextureToBuffer(sampled, 0, 0, 0, 4, 4, 1, 0, 0, destination, 0, 256)
-            .GetResult();
-    }
+    TestT2BCopy(utils::Expectation::Failure, sampled, 0, 0, {0, 0, 0}, dawn::TextureAspect::Color,
+                destination, 0, 256, 0, {4, 4, 1});
 
     // Incorrect destination usage
-    {
-        dawn::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
-            .CopyTextureToBuffer(source, 0, 0, 0, 4, 4, 1, 0, 0, vertex, 0, 256)
-            .GetResult();
-    }
+    TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, dawn::TextureAspect::Color,
+                vertex, 0, 256, 0, {4, 4, 1});
 }
 
 TEST_F(CopyCommandTest_T2B, IncorrectRowPitch) {
@@ -532,25 +511,16 @@
     dawn::Buffer destination = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferSrc);
 
     // Default row pitch is not 256-byte aligned
-    {
-        dawn::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
-            .CopyTextureToBuffer(source, 0, 0, 0, 3, 4, 1, 0, 0, destination, 0, 256)
-            .GetResult();
-    }
+    TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, dawn::TextureAspect::Color,
+                destination, 0, 256, 0, {3, 4, 1});
 
     // Row pitch is not 256-byte aligned
-    {
-        dawn::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
-            .CopyTextureToBuffer(source, 0, 0, 0, 4, 4, 1, 0, 0, destination, 0, 257)
-            .GetResult();
-    }
+    TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, dawn::TextureAspect::Color,
+                destination, 0, 257, 0, {4, 4, 1});
 
     // Row pitch is less than width * bytesPerPixel
-    {
-        dawn::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
-            .CopyTextureToBuffer(source, 0, 0, 0, 65, 1, 1, 0, 0, destination, 0, 256)
-            .GetResult();
-    }
+    TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, dawn::TextureAspect::Color,
+                destination, 0, 256, 0, {65, 1, 1});
 }
 
 // Test T2B copies with incorrect buffer offset usage
@@ -561,26 +531,15 @@
     dawn::Buffer destination = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferDst);
 
     // Correct usage
-    {
-        dawn::CommandBuffer commands = AssertWillBeSuccess(device.CreateCommandBufferBuilder())
-            .CopyTextureToBuffer(source, 0, 0, 0, 1, 1, 1, 0, 0, destination, bufferSize - 4, 256)
-            .GetResult();
-    }
+    TestT2BCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, dawn::TextureAspect::Color,
+                destination, bufferSize - 4, 256, 0, {1, 1, 1});
+
     // Incorrect usages
-    {
-        dawn::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
-            .CopyTextureToBuffer(source, 0, 0, 0, 1, 1, 1, 0, 0, destination, bufferSize - 5, 256)
-            .GetResult();
-    }
-    {
-        dawn::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
-            .CopyTextureToBuffer(source, 0, 0, 0, 1, 1, 1, 0, 0, destination, bufferSize - 6, 256)
-            .GetResult();
-    }
-    {
-        dawn::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
-            .CopyTextureToBuffer(source, 0, 0, 0, 1, 1, 1, 0, 0, destination, bufferSize - 7, 256)
-            .GetResult();
-    }
+    TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, dawn::TextureAspect::Color,
+                destination, bufferSize - 5, 256, 0, {1, 1, 1});
+    TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, dawn::TextureAspect::Color,
+                destination, bufferSize - 6, 256, 0, {1, 1, 1});
+    TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, dawn::TextureAspect::Color,
+                destination, bufferSize - 7, 256, 0, {1, 1, 1});
 }
 
diff --git a/src/utils/DawnHelpers.cpp b/src/utils/DawnHelpers.cpp
index 97e1c80..e88a92d 100644
--- a/src/utils/DawnHelpers.cpp
+++ b/src/utils/DawnHelpers.cpp
@@ -153,6 +153,34 @@
         return result;
     }
 
+    dawn::BufferCopyView CreateBufferCopyView(dawn::Buffer buffer,
+                                              uint32_t offset,
+                                              uint32_t rowPitch,
+                                              uint32_t imageHeight) {
+        dawn::BufferCopyView bufferCopyView;
+        bufferCopyView.buffer = buffer;
+        bufferCopyView.offset = offset;
+        bufferCopyView.rowPitch = rowPitch;
+        bufferCopyView.imageHeight = imageHeight;
+
+        return bufferCopyView;
+    }
+
+    dawn::TextureCopyView CreateTextureCopyView(dawn::Texture texture,
+                                                uint32_t level,
+                                                uint32_t slice,
+                                                dawn::Origin3D origin,
+                                                dawn::TextureAspect aspect) {
+        dawn::TextureCopyView textureCopyView;
+        textureCopyView.texture = texture;
+        textureCopyView.level = level;
+        textureCopyView.slice = slice;
+        textureCopyView.origin = origin;
+        textureCopyView.aspect = aspect;
+
+        return textureCopyView;
+    }
+
     dawn::SamplerDescriptor GetDefaultSamplerDescriptor() {
         dawn::SamplerDescriptor desc;
 
diff --git a/src/utils/DawnHelpers.h b/src/utils/DawnHelpers.h
index 03a3e30..e1e0ed0 100644
--- a/src/utils/DawnHelpers.h
+++ b/src/utils/DawnHelpers.h
@@ -18,6 +18,8 @@
 
 namespace utils {
 
+    enum Expectation { Success, Failure };
+
     dawn::ShaderModule CreateShaderModule(const dawn::Device& device,
                                           dawn::ShaderStage stage,
                                           const char* source);
@@ -35,6 +37,16 @@
         return CreateBufferFromData(device, data.begin(), uint32_t(sizeof(T) * data.size()), usage);
     }
 
+    dawn::BufferCopyView CreateBufferCopyView(dawn::Buffer buffer,
+                                              uint32_t offset,
+                                              uint32_t rowPitch,
+                                              uint32_t imageHeight);
+    dawn::TextureCopyView CreateTextureCopyView(dawn::Texture texture,
+                                                uint32_t level,
+                                                uint32_t slice,
+                                                dawn::Origin3D origin,
+                                                dawn::TextureAspect aspect);
+
     struct BasicRenderPass {
         uint32_t width;
         uint32_t height;