d3d12: Rework TextureCopySplitter to compute in blocks

- Now that TextureCopySplitter has been updated to use strong types,
  rework it to compute in blocks.
- Change TextureCopyResource::CopyInfo to store values in blocks, rather
  than texels, and update RecordBufferTextureCopyFromSplits to convert
  these to texels at the last moment before adding to the D3D12 command
  list.
- Fix CopySplitTest.TextureX and TextureY to skip texel values that are
  not multiples of the block size, as required by the spec.

Bug: 424536624
Change-Id: I33ac1dda9b132ddb940d9d955ca7dd8d223352e2
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/247935
Commit-Queue: Antonio Maiorano <amaiorano@google.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/dawn/native/BlockInfo.h b/src/dawn/native/BlockInfo.h
index 33c17c8..d199269 100644
--- a/src/dawn/native/BlockInfo.h
+++ b/src/dawn/native/BlockInfo.h
@@ -33,24 +33,31 @@
 
 namespace dawn::native {
 
-// Strong types for texel and block counts
+// Strong types for texel and block counts.
+// These are used to avoid computation errors that occur when using regular integers
+// for texel and block values, and mixing them up in computations, particularly for block
+// sizes greater than 1x1.
+// By using strong types, we cannot mix these two types in computations, and must use
+// conversion functions provided in TypedTexelBlockInfo.
+
 // TexelCount and BlockCount are uint64_t, not uint32_t, because as typed
 // integers, they do not participate in type promotion to uint64_t, which
 // is being relied on for computing buffer offsets and such.
 using TexelCount = dawn::TypedInteger<struct TexelCountTag, uint64_t>;
 using BlockCount = dawn::TypedInteger<struct BlockCountTag, uint64_t>;
 
-// Strong type version of Origin3D
+// Strong type version of Origin3D, which is always in texel space
 struct TexelOrigin3D {
     TexelCount x{0};
     TexelCount y{0};
     TexelCount z{0};
 
-    // Default constructor
-    TexelOrigin3D() = default;
-
     // Construct from input values
-    TexelOrigin3D(TexelCount x, TexelCount y, TexelCount z) : x(x), y(y), z(z) {}
+    // NOLINTNEXTLINE: allow implicit constructor
+    TexelOrigin3D(TexelCount x = TexelCount{0},
+                  TexelCount y = TexelCount{0},
+                  TexelCount z = TexelCount{0})
+        : x(x), y(y), z(z) {}
 
     // Implicitly convert from Origin3D as Origin3D is always in texel space
     // NOLINTNEXTLINE: allow implicit constructor
@@ -62,7 +69,21 @@
     }
 };
 
-// Strong type version of Extent3D
+// Stores an origin in block space
+struct BlockOrigin3D {
+    BlockCount x{0};
+    BlockCount y{0};
+    BlockCount z{0};
+
+    // Construct from input values
+    // NOLINTNEXTLINE: allow implicit constructor
+    BlockOrigin3D(BlockCount x = BlockCount{0},
+                  BlockCount y = BlockCount{0},
+                  BlockCount z = BlockCount{0})
+        : x(x), y(y), z(z) {}
+};
+
+// Strong type version of Extent3D.
 struct TexelExtent3D {
     TexelCount width;
     TexelCount height{1};
@@ -72,7 +93,10 @@
     TexelExtent3D() = default;
 
     // Construct from input values
-    TexelExtent3D(TexelCount width, TexelCount height, TexelCount depthOrArrayLayers)
+    // NOLINTNEXTLINE: allow implicit constructor
+    TexelExtent3D(TexelCount width,
+                  TexelCount height = TexelCount{1},
+                  TexelCount depthOrArrayLayers = TexelCount{1})
         : width(width), height(height), depthOrArrayLayers(depthOrArrayLayers) {}
 
     // Implicitly convert from Extent3D as Extent3D is always in texel space
@@ -87,6 +111,23 @@
     }
 };
 
+// Stores an extent in block space
+struct BlockExtent3D {
+    BlockCount width;
+    BlockCount height{1};
+    BlockCount depthOrArrayLayers{1};
+
+    // Default constructor
+    BlockExtent3D() = default;
+
+    // Construct from input values
+    // NOLINTNEXTLINE: allow implicit constructor
+    BlockExtent3D(BlockCount width,
+                  BlockCount height = BlockCount{1},
+                  BlockCount depthOrArrayLayers = BlockCount{1})
+        : width(width), height(height), depthOrArrayLayers(depthOrArrayLayers) {}
+};
+
 // Strong type version of TexelBlockInfo that stores the dimensions of the block
 // as TexelCounts, and provides conversion functions between texels, blocks, and bytes.
 struct TypedTexelBlockInfo {
@@ -118,12 +159,14 @@
 
     // Convert texel height to block height
     BlockCount ToBlockHeight(TexelCount value) const {
-        return BlockCount{static_cast<uint64_t>((value + height - TexelCount{1}) / height)};
+        DAWN_ASSERT(value % height == TexelCount{0});
+        return BlockCount{static_cast<uint64_t>(value / height)};
     }
 
     // Convert from texel width to block width
     BlockCount ToBlockWidth(TexelCount value) const {
-        return BlockCount{static_cast<uint64_t>((value + width - TexelCount{1}) / width)};
+        DAWN_ASSERT(value % width == TexelCount{0});
+        return BlockCount{static_cast<uint64_t>(value / width)};
     }
 
     // Convert from texel depth to block depth
@@ -149,6 +192,28 @@
         // 'depth'
         return TexelCount{static_cast<uint64_t>(value)};
     }
+
+    // Convert from TexelOrigin3D to BlockOrigin3D
+    BlockOrigin3D ToBlock(const TexelOrigin3D& origin) {
+        return {ToBlockWidth(origin.x), ToBlockHeight(origin.y), ToBlockDepth(origin.z)};
+    }
+
+    // Convert from TexelExtent3D to BlockExtent3D
+    BlockExtent3D ToBlock(const TexelExtent3D& extent) {
+        return {ToBlockWidth(extent.width), ToBlockHeight(extent.height),
+                ToBlockDepth(extent.depthOrArrayLayers)};
+    }
+
+    // Convert from BlockOrigin3D to TexelOrigin3D
+    TexelOrigin3D ToTexel(const BlockOrigin3D& origin) {
+        return {ToTexelWidth(origin.x), ToTexelHeight(origin.y), ToTexelDepth(origin.z)};
+    }
+
+    // Convert from BlockExtent3D to TexelExtent3D
+    TexelExtent3D ToTexel(const BlockExtent3D& extent) {
+        return {ToTexelWidth(extent.width), ToTexelHeight(extent.height),
+                ToTexelDepth(extent.depthOrArrayLayers)};
+    }
 };
 
 }  // namespace dawn::native
diff --git a/src/dawn/native/d3d12/TextureCopySplitter.cpp b/src/dawn/native/d3d12/TextureCopySplitter.cpp
index 2ec4d5d..e6a9437 100644
--- a/src/dawn/native/d3d12/TextureCopySplitter.cpp
+++ b/src/dawn/native/d3d12/TextureCopySplitter.cpp
@@ -34,27 +34,23 @@
 namespace dawn::native::d3d12 {
 
 namespace {
-// TODO(424536624): Return a BlockOrigin3D
-TexelOrigin3D ComputeTexelOffsets(const TypedTexelBlockInfo& blockInfo,
+BlockOrigin3D ComputeBlockOffsets(const TypedTexelBlockInfo& blockInfo,
                                   uint32_t offset,
                                   BlockCount blocksPerRow) {
     DAWN_ASSERT(blocksPerRow != BlockCount{0});
     BlockCount offsetInBlocks = blockInfo.BytesToBlocks(offset);
     BlockCount blockOffsetX = offsetInBlocks % blocksPerRow;
     BlockCount blockOffsetY = offsetInBlocks / blocksPerRow;
-    return {blockInfo.ToTexelWidth(blockOffsetX), blockInfo.ToTexelHeight(blockOffsetY),
-            TexelCount{0}};
+    return {blockOffsetX, blockOffsetY, BlockCount{0}};
 }
 
 uint64_t OffsetToFirstCopiedTexel(const TypedTexelBlockInfo& blockInfo,
                                   BlockCount blocksPerRow,
                                   uint64_t alignedOffset,
-                                  TexelOrigin3D bufferOffset) {
-    DAWN_ASSERT(bufferOffset.z == TexelCount{0});
-
+                                  BlockOrigin3D bufferOffset) {
+    DAWN_ASSERT(bufferOffset.z == BlockCount{0});
     uint64_t offset =
-        alignedOffset + blockInfo.ToBytes(blockInfo.ToBlockWidth(bufferOffset.x) +
-                                          blocksPerRow * blockInfo.ToBlockHeight(bufferOffset.y));
+        alignedOffset + blockInfo.ToBytes(bufferOffset.x + blocksPerRow * bufferOffset.y);
     return offset;
 }
 
@@ -74,11 +70,10 @@
                                                        const TexelBlockInfo& blockInfo_in,
                                                        uint64_t offset,
                                                        uint32_t bytesPerRow_in) {
-    // TODO(424536624): Rework this function to compute in blocks, then convert to texels
-    auto origin = TexelOrigin3D(origin_in);
-    auto copySize = TexelExtent3D(copySize_in);
-    auto blockInfo = TypedTexelBlockInfo(blockInfo_in);
-    auto blocksPerRow = blockInfo.BytesToBlocks(bytesPerRow_in);
+    TypedTexelBlockInfo blockInfo{blockInfo_in};
+    BlockOrigin3D origin = blockInfo.ToBlock(origin_in);
+    BlockExtent3D copySize = blockInfo.ToBlock(copySize_in);
+    BlockCount blocksPerRow = blockInfo.BytesToBlocks(bytesPerRow_in);
 
     TextureCopySubresource copy;
 
@@ -90,7 +85,7 @@
     // without further translation.
     if (offset == alignedOffset) {
         TextureCopySubresource::CopyInfo* copyInfo = copy.AddCopy();
-        copyInfo->bufferOffset = TexelOrigin3D{{0, 0, 0}};
+        copyInfo->bufferOffset = {};  // 0,0,0
         copyInfo->textureOffset = origin;
         copyInfo->copySize = copySize;
         copyInfo->alignedOffset = alignedOffset;
@@ -115,20 +110,20 @@
     // |+++++~~~~~~~~~~~|
     // |<---row pitch-->|
     //
-    // The X and Y offsets calculated in ComputeTexelOffsets can be visualized like this:
+    // The X and Y offsets calculated in ComputeBlockOffsets can be visualized like this:
     // |YYYYYYYYYYYYYYYY|
     // |XXXXXX++++++++++|
     // |++++++++++++++++|
     // |++++++~~~~~~~~~~|
     // |<---row pitch-->|
-    TexelOrigin3D texelOffset =
-        ComputeTexelOffsets(blockInfo, static_cast<uint32_t>(offset - alignedOffset), blocksPerRow);
+    BlockOrigin3D blockOffset =
+        ComputeBlockOffsets(blockInfo, static_cast<uint32_t>(offset - alignedOffset), blocksPerRow);
 
-    DAWN_ASSERT(texelOffset.y <= blockInfo.height);
-    DAWN_ASSERT(texelOffset.z == TexelCount{0});
+    DAWN_ASSERT(blockOffset.y <= BlockCount{1});
+    DAWN_ASSERT(blockOffset.z == BlockCount{0});
 
-    BlockCount copyBlocksPerRowPitch = blockInfo.ToBlockWidth(copySize.width);
-    BlockCount blockOffsetInRowPitch = blockInfo.ToBlockWidth(texelOffset.x);
+    BlockCount copyBlocksPerRowPitch = copySize.width;
+    BlockCount blockOffsetInRowPitch = blockOffset.x;
     if (copyBlocksPerRowPitch + blockOffsetInRowPitch <= blocksPerRow) {
         // The region's rows fit inside the bytes per row. In this case, extend the width of the
         // PlacedFootprint and copy the buffer with an offset location
@@ -154,11 +149,11 @@
         //  |----------------------------------|
 
         TextureCopySubresource::CopyInfo* copyInfo = copy.AddCopy();
-        copyInfo->bufferOffset = texelOffset;
+        copyInfo->bufferOffset = blockOffset;
         copyInfo->textureOffset = origin;
         copyInfo->copySize = copySize;
         copyInfo->alignedOffset = alignedOffset;
-        copyInfo->bufferSize = {copySize.width + texelOffset.x, copySize.height + texelOffset.y,
+        copyInfo->bufferSize = {copySize.width + blockOffset.x, copySize.height + blockOffset.y,
                                 copySize.depthOrArrayLayers};
         return copy;
     }
@@ -199,36 +194,35 @@
 
     // Copy 0
     DAWN_ASSERT(blocksPerRow > blockOffsetInRowPitch);
-    const TexelCount texelsPerRow = blockInfo.ToTexelWidth(blocksPerRow);
-    const TexelExtent3D copySize0 = {texelsPerRow - texelOffset.x, copySize.height,
+    const BlockExtent3D copySize0 = {blocksPerRow - blockOffset.x, copySize.height,
                                      copySize.depthOrArrayLayers};
+
     TextureCopySubresource::CopyInfo* copyInfo0 = copy.AddCopy();
-    copyInfo0->bufferOffset = texelOffset;
+    copyInfo0->bufferOffset = blockOffset;
     copyInfo0->textureOffset = origin;
     copyInfo0->copySize = copySize0;
     copyInfo0->alignedOffset = alignedOffset;
-    copyInfo0->bufferSize = {texelsPerRow, copySize.height + texelOffset.y,
+    copyInfo0->bufferSize = {blocksPerRow, copySize.height + blockOffset.y,
                              copySize.depthOrArrayLayers};
 
     // Copy 1
-    const uint64_t offsetForCopy1 =
-        offset + blockInfo.ToBytes(blockInfo.ToBlockWidth(copySize0.width));
+    const uint64_t offsetForCopy1 = offset + blockInfo.ToBytes(copySize0.width);
     const uint64_t alignedOffsetForCopy1 = AlignDownForDataPlacement(offsetForCopy1);
-    const TexelOrigin3D texelOffsetForCopy1 = ComputeTexelOffsets(
+    const BlockOrigin3D blockOffsetForCopy1 = ComputeBlockOffsets(
         blockInfo, static_cast<uint32_t>(offsetForCopy1 - alignedOffsetForCopy1), blocksPerRow);
 
-    DAWN_ASSERT(texelOffsetForCopy1.y <= blockInfo.height);
-    DAWN_ASSERT(texelOffsetForCopy1.z == TexelCount{0});
+    DAWN_ASSERT(blockOffsetForCopy1.y <= BlockCount{1});
+    DAWN_ASSERT(blockOffsetForCopy1.z == BlockCount{0});
 
-    const TexelOrigin3D textureOffset1 = {origin.x + copySize0.width, origin.y, origin.z};
+    const BlockOrigin3D textureOffset1 = {origin.x + copySize0.width, origin.y, origin.z};
 
     DAWN_ASSERT(copySize.width > copySize0.width);
-    const TexelExtent3D copySize1 = {copySize.width - copySize0.width, copySize.height,
+    const BlockExtent3D copySize1 = {copySize.width - copySize0.width, copySize.height,
                                      copySize.depthOrArrayLayers};
 
-    const TexelOrigin3D bufferOffset1 = texelOffsetForCopy1;
-    const TexelExtent3D bufferSize1 = {copySize1.width + texelOffsetForCopy1.x,
-                                       copySize.height + texelOffsetForCopy1.y,
+    const BlockOrigin3D bufferOffset1 = blockOffsetForCopy1;
+    const BlockExtent3D bufferSize1 = {copySize1.width + blockOffsetForCopy1.x,
+                                       copySize.height + blockOffsetForCopy1.y,
                                        copySize.depthOrArrayLayers};
 
     TextureCopySubresource::CopyInfo* copyInfo1 = copy.AddCopy();
@@ -293,20 +287,13 @@
 }
 
 void Recompute3DTextureCopyRegionWithEmptyFirstRowAndEvenCopyHeight(
-    Origin3D origin_in,
-    Extent3D copySize_in,
-    const TexelBlockInfo& blockInfo_in,
-    uint32_t bytesPerRow_in,
-    uint32_t rowsPerImage_in,
+    BlockOrigin3D origin,
+    BlockExtent3D copySize,
+    const TypedTexelBlockInfo& blockInfo,
+    BlockCount blocksPerRow,
+    BlockCount rowsPerImage,
     TextureCopySubresource& copy,
     uint32_t i) {
-    // TODO(424536624): Rework this function to compute in blocks, then convert to texels
-    auto origin = TexelOrigin3D(origin_in);
-    auto copySize = TexelExtent3D(copySize_in);
-    auto blockInfo = TypedTexelBlockInfo(blockInfo_in);
-    auto rowsPerImage = BlockCount(rowsPerImage_in);
-    auto blocksPerRow = blockInfo.BytesToBlocks(bytesPerRow_in);
-
     // Let's assign data and show why copy region generated by ComputeTextureCopySubresource
     // is incorrect if there is an empty row at the beginning of the copy block.
     // Assuming that bytesPerRow is 256 and we are doing a B2T copy, and copy size is {width: 2,
@@ -399,20 +386,20 @@
 
     // Copy 0: copy copySize0.height - 1 rows
     TextureCopySubresource::CopyInfo& copy0 = copy.copies[i];
-    copy0.copySize.height = copySize.height - blockInfo.height;
-    copy0.bufferSize.height = blockInfo.ToTexelHeight(rowsPerImage);  // rowsPerImageInTexels
+    copy0.copySize.height = copySize.height - BlockCount{1};
+    copy0.bufferSize.height = rowsPerImage;
 
     // Copy 1: move down 2 rows and copy the last row on image 0, and expand to all depth slices
     // but the last one.
     TextureCopySubresource::CopyInfo* copy1 = copy.AddCopy();
     *copy1 = copy0;
     copy1->alignedOffset = copy1->alignedOffset + 2 * blockInfo.ToBytes(blocksPerRow);
-    copy1->textureOffset.y += copySize.height - blockInfo.height;
+    copy1->textureOffset.y += copySize.height - BlockCount{1};
     // Offset two rows from the copy height for bufferOffset1 (See the figure above):
     //   - one for the row we advanced in the buffer: row (N + 4).
     //   - one for the last row we want to copy: row (N + 3) itself.
-    copy1->bufferOffset.y = copySize.height - TexelCount{2} * blockInfo.height;
-    copy1->copySize.height = blockInfo.height;
+    copy1->bufferOffset.y = copySize.height - BlockCount{2};
+    copy1->copySize.height = BlockCount{1};
     copy1->copySize.depthOrArrayLayers--;
     copy1->bufferSize.depthOrArrayLayers--;
 
@@ -423,12 +410,11 @@
         offsetForCopy0 +
         blockInfo.ToBytes(
             blocksPerRow *
-            (blockInfo.ToBlockHeight(copy0.copySize.height) +
-             rowsPerImage * (blockInfo.ToBlockDepth(copySize.depthOrArrayLayers) - BlockCount{1})));
+            (copy0.copySize.height + rowsPerImage * (copySize.depthOrArrayLayers - BlockCount{1})));
 
     uint64_t alignedOffsetForLastRowOfLastImage =
         AlignDownForDataPlacement(offsetForLastRowOfLastImage);
-    TexelOrigin3D texelOffsetForLastRowOfLastImage = ComputeTexelOffsets(
+    BlockOrigin3D blockOffsetForLastRowOfLastImage = ComputeBlockOffsets(
         blockInfo,
         static_cast<uint32_t>(offsetForLastRowOfLastImage - alignedOffsetForLastRowOfLastImage),
         blocksPerRow);
@@ -436,27 +422,22 @@
     TextureCopySubresource::CopyInfo* copy2 = copy.AddCopy();
     copy2->alignedOffset = alignedOffsetForLastRowOfLastImage;
     copy2->textureOffset = copy1->textureOffset;
-    copy2->textureOffset.z = origin.z + copySize.depthOrArrayLayers - TexelCount{1};
+    copy2->textureOffset.z = origin.z + copySize.depthOrArrayLayers - BlockCount{1};
     copy2->copySize = copy1->copySize;
-    copy2->copySize.depthOrArrayLayers = TexelCount{1};
-    copy2->bufferOffset = texelOffsetForLastRowOfLastImage;
+    copy2->copySize.depthOrArrayLayers = BlockCount{1};
+    copy2->bufferOffset = blockOffsetForLastRowOfLastImage;
     copy2->bufferSize.width = copy1->bufferSize.width;
-    DAWN_ASSERT(copy2->copySize.height == blockInfo.height);
+    DAWN_ASSERT(copy2->copySize.height == BlockCount{1});
     copy2->bufferSize.height = copy2->bufferOffset.y + copy2->copySize.height;
-    copy2->bufferSize.depthOrArrayLayers = TexelCount{1};
+    copy2->bufferSize.depthOrArrayLayers = BlockCount{1};
 }
 
 void Recompute3DTextureCopyRegionWithEmptyFirstRowAndOddCopyHeight(
-    Extent3D copySize_in,
-    const TexelBlockInfo& blockInfo_in,
-    uint32_t bytesPerRow_in,
+    BlockExtent3D copySize,
+    const TypedTexelBlockInfo& blockInfo,
+    BlockCount blocksPerRow,
     TextureCopySubresource& copy,
     uint32_t i) {
-    // TODO(424536624): Rework this function to compute in blocks, then convert to texels
-    auto copySize = TexelExtent3D(copySize_in);
-    auto blockInfo = TypedTexelBlockInfo(blockInfo_in);
-    auto blocksPerRow = blockInfo.BytesToBlocks(bytesPerRow_in);
-
     // Read the comments of Recompute3DTextureCopyRegionWithEmptyFirstRowAndEvenCopyHeight() for
     // the reason why it is incorrect if we simply extend the copy region to all depth slices
     // when there is an empty first row at the copy region.
@@ -469,27 +450,26 @@
 
     // Copy 0: copy the first depth slice (image 0)
     TextureCopySubresource::CopyInfo& copy0 = copy.copies[i];
-    copy0.copySize.depthOrArrayLayers = TexelCount{1};
-    const TexelCount kBufferDepth0 = TexelCount{1};
+    copy0.copySize.depthOrArrayLayers = BlockCount{1};
+    const BlockCount kBufferDepth0 = BlockCount{1};
     copy0.bufferSize.depthOrArrayLayers = kBufferDepth0;
 
     // Copy 1: copy the rest depth slices in one shot
     TextureCopySubresource::CopyInfo* copy1 = copy.AddCopy();
     *copy1 = copy0;
-    DAWN_ASSERT(blockInfo.ToBlockHeight(copySize.height) % BlockCount{2} == BlockCount{1});
-    copy1->alignedOffset += blockInfo.ToBytes(
-        (blockInfo.ToBlockHeight(copySize.height) + BlockCount{1}) * blocksPerRow);
+    DAWN_ASSERT(copySize.height % BlockCount{2} == BlockCount{1});
+    copy1->alignedOffset += blockInfo.ToBytes((copySize.height + BlockCount{1}) * blocksPerRow);
 
     DAWN_ASSERT(copy1->alignedOffset % D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT == 0);
     // textureOffset1.z should add one because the first slice has already been copied in copy0.
     copy1->textureOffset.z++;
     // bufferOffset1.y should be 0 because we skipped the first depth slice and there is no empty
     // row in this copy region.
-    copy1->bufferOffset.y = TexelCount{0};
+    copy1->bufferOffset.y = BlockCount{0};
     copy1->copySize.height = copySize.height;
-    copy1->copySize.depthOrArrayLayers = copySize.depthOrArrayLayers - TexelCount{1};
+    copy1->copySize.depthOrArrayLayers = copySize.depthOrArrayLayers - BlockCount{1};
     copy1->bufferSize.height = copySize.height;
-    copy1->bufferSize.depthOrArrayLayers = copySize.depthOrArrayLayers - TexelCount{1};
+    copy1->bufferSize.depthOrArrayLayers = copySize.depthOrArrayLayers - BlockCount{1};
 }
 
 TextureCopySubresource Compute3DTextureCopySplits(Origin3D origin_in,
@@ -498,11 +478,11 @@
                                                   uint64_t offset,
                                                   uint32_t bytesPerRow,
                                                   uint32_t rowsPerImage_in) {
-    // TODO(424536624): Rework this function to compute in blocks, then convert to texels
-    auto origin = TexelOrigin3D(origin_in);
-    auto copySize = TexelExtent3D(copySize_in);
-    auto blockInfo = TypedTexelBlockInfo(blockInfo_in);
-    auto rowsPerImage = BlockCount(rowsPerImage_in);
+    TypedTexelBlockInfo blockInfo{blockInfo_in};
+    BlockOrigin3D origin = blockInfo.ToBlock(origin_in);
+    BlockExtent3D copySize = blockInfo.ToBlock(copySize_in);
+    BlockCount blocksPerRow = blockInfo.BytesToBlocks(bytesPerRow);
+    BlockCount rowsPerImage{rowsPerImage_in};
 
     // To compute the copy region(s) for 3D textures, we call Compute2DTextureCopySubresource
     // and get copy region(s) for the first slice of the copy, then extend to all depth slices
@@ -518,67 +498,64 @@
 
     // Call Compute2DTextureCopySubresource and get copy regions. This function has already
     // forwarded "copySize.depthOrArrayLayers" to all depth slices.
-    TextureCopySubresource copySubresource =
-        Compute2DTextureCopySubresource(origin.ToOrigin3D(), copySize.ToExtent3D(),
-                                        blockInfo.ToTexelBlockInfo(), offset, bytesPerRow);
+    TextureCopySubresource copySubresource = Compute2DTextureCopySubresource(
+        blockInfo.ToTexel(origin).ToOrigin3D(), blockInfo.ToTexel(copySize).ToExtent3D(),
+        blockInfo.ToTexelBlockInfo(), offset, bytesPerRow);
 
     DAWN_ASSERT(copySubresource.count <= 2);
     // If copySize.depthOrArrayLayers is 1, we can return copySubresource. Because we don't need to
     // extend the copy region(s) to other depth slice(s).
-    if (copySize.depthOrArrayLayers == TexelCount{1}) {
+    if (copySize.depthOrArrayLayers == BlockCount{1}) {
         return copySubresource;
     }
 
-    TexelCount rowsPerImageInTexels = blockInfo.ToTexelHeight(rowsPerImage);
     // The copy region(s) generated by Compute2DTextureCopySubresource might be incorrect.
     // However, we may append a couple more copy regions in the for loop below. We don't need
     // to revise these new added copy regions.
     uint32_t originalCopyCount = copySubresource.count;
     for (uint32_t i = 0; i < originalCopyCount; ++i) {
         // There can be one empty row at most in a copy region.
-        TexelCount bufferHeight = copySubresource.copies[i].bufferSize.height;
-        DAWN_ASSERT(bufferHeight <= rowsPerImageInTexels + blockInfo.height);
+        BlockCount bufferHeight = copySubresource.copies[i].bufferSize.height;
+        DAWN_ASSERT(bufferHeight <= rowsPerImage + BlockCount{1});
 
-        if (bufferHeight == rowsPerImageInTexels) {
-            // If the copy region's bufferHeight equals to rowsPerImageInTexels, we can use this
+        if (bufferHeight == rowsPerImage) {
+            // If the copy region's bufferHeight equals to rowsPerImage, we can use this
             // copy region without any modification.
             continue;
         }
 
-        if (bufferHeight < rowsPerImageInTexels) {
-            // If we are copying multiple depth slices, we should skip rowsPerImageInTexels rows for
+        if (bufferHeight < rowsPerImage) {
+            // If we are copying multiple depth slices, we should skip rowsPerImage rows for
             // each slice even though we only copy partial rows in each slice sometimes.
-            copySubresource.copies[i].bufferSize.height = rowsPerImageInTexels;
+            copySubresource.copies[i].bufferSize.height = rowsPerImage;
         } else {
-            // bufferHeight > rowsPerImageInTexels. There is an empty row in this copy region due to
+            // bufferHeight > rowsPerImage. There is an empty row in this copy region due to
             // alignment adjustment.
 
             // bytesPerRow is definitely 256, and it is definitely a full copy on height.
-            // Otherwise, bufferHeight won't be greater than rowsPerImageInTexels and there won't be
+            // Otherwise, bufferHeight won't be greater than rowsPerImage and there won't be
             // an empty row at the beginning of this copy region.
             DAWN_ASSERT(bytesPerRow == D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
-            DAWN_ASSERT(copySize.height == rowsPerImageInTexels);
+            DAWN_ASSERT(copySize.height == rowsPerImage);
 
-            const BlockCount copyHeightInBlocks = blockInfo.ToBlockHeight(copySize.height);
-            if (static_cast<uint32_t>(copyHeightInBlocks) % 2 == 0) {
-                // If copyHeightInBlocks is even and there is an empty row at the beginning of the
+            const BlockCount copyHeight = copySize.height;
+            if (static_cast<uint32_t>(copyHeight) % 2 == 0) {
+                // If copyHeight is even and there is an empty row at the beginning of the
                 // first slice of the copy region, the offset of all depth slices will never be
                 // aligned to D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT (512) and there is always
                 // an empty row at each depth slice. We need a totally different approach to
                 // split the copy region.
                 Recompute3DTextureCopyRegionWithEmptyFirstRowAndEvenCopyHeight(
-                    origin.ToOrigin3D(), copySize.ToExtent3D(), blockInfo.ToTexelBlockInfo(),
-                    bytesPerRow, static_cast<uint32_t>(rowsPerImage), copySubresource, i);
+                    origin, copySize, blockInfo, blocksPerRow, rowsPerImage, copySubresource, i);
             } else {
-                // If copyHeightInBlocks is odd and there is an empty row at the beginning of the
+                // If copyHeight is odd and there is an empty row at the beginning of the
                 // first slice of the copy region, we can split the copy region into two copies:
                 // copy0 to copy the first slice, copy1 to copy the rest slices because the
                 // offset of slice 1 is aligned to D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT (512)
                 // without an empty row. This is an easier case relative to cases with even copy
                 // height.
                 Recompute3DTextureCopyRegionWithEmptyFirstRowAndOddCopyHeight(
-                    copySize.ToExtent3D(), blockInfo.ToTexelBlockInfo(), bytesPerRow,
-                    copySubresource, i);
+                    copySize, blockInfo, blocksPerRow, copySubresource, i);
             }
         }
     }
@@ -592,10 +569,9 @@
     const TexelBlockInfo& blockInfo_in,
     uint64_t offset,
     uint32_t /*bytesPerRow*/) {
-    // TODO(424536624): Rework this function to compute in blocks, then convert to texels
-    auto origin = TexelOrigin3D(origin_in);
-    auto copySize = TexelExtent3D(copySize_in);
-
+    TypedTexelBlockInfo blockInfo{blockInfo_in};
+    BlockOrigin3D origin = blockInfo.ToBlock(origin_in);
+    BlockExtent3D copySize = blockInfo.ToBlock(copySize_in);
     TextureCopySubresource copy;
     auto* copyInfo = copy.AddCopy();
 
@@ -611,11 +587,11 @@
     //                        |++++++++++++++++++++++|         |    |     |
     //                        |<-----CopyWidth------>|         |----------|
     //
-    copyInfo->textureOffset = {origin.x, origin.y, TexelCount{0}};
-    copyInfo->bufferOffset = TexelOrigin3D{{0, 0, 0}};
-    copyInfo->copySize = {copySize.width, copySize.height, TexelCount{1}};
+    copyInfo->textureOffset = {origin.x, origin.y, BlockCount{0}};
+    copyInfo->bufferOffset = {};  // 0,0,0
+    copyInfo->copySize = {copySize.width, copySize.height, BlockCount{1}};
     copyInfo->alignedOffset = offset;
-    copyInfo->bufferSize = {copySize.width, copySize.height, TexelCount{1}};
+    copyInfo->bufferSize = {copySize.width, copySize.height, BlockCount{1}};
     return copy;
 }
 
@@ -626,21 +602,20 @@
     uint64_t offset,
     uint32_t bytesPerRow_in,
     uint32_t rowsPerImage_in) {
-    // TODO(424536624): Rework this function to compute in blocks, then convert to texels
-    auto origin = TexelOrigin3D(origin_in);
-    auto copySize = TexelExtent3D(copySize_in);
-    auto blockInfo = TypedTexelBlockInfo(blockInfo_in);
-    auto rowsPerImage = BlockCount(rowsPerImage_in);
-    auto blocksPerRow = blockInfo.BytesToBlocks(bytesPerRow_in);
+    TypedTexelBlockInfo blockInfo{blockInfo_in};
+    BlockOrigin3D origin = blockInfo.ToBlock(origin_in);
+    BlockExtent3D copySize = blockInfo.ToBlock(copySize_in);
+    BlockCount rowsPerImage{rowsPerImage_in};
+    BlockCount blocksPerRow = blockInfo.BytesToBlocks(bytesPerRow_in);
 
     TextureCopySubresource copy;
 
-    Origin3D bufferOffset = {0, 0, 0};
+    BlockOrigin3D bufferOffset{BlockCount{0}, BlockCount{0}, BlockCount{0}};
 
     // You can visualize the data in the buffer (bufferLocation) like the inline comments.
     // * copy data is visualized as '+'.
-    const TexelCount depthInCopy1 = copySize.depthOrArrayLayers - TexelCount{1};
-    if (depthInCopy1 > TexelCount{0}) {
+    const BlockCount depthInCopy1 = copySize.depthOrArrayLayers - BlockCount{1};
+    if (depthInCopy1 > BlockCount{0}) {
         // `bufferLocation` in the 1st copy (first `depthInCopy1` images, optional):
         //
         //                bufferOffset(0, 0, 0)
@@ -666,8 +641,7 @@
         copyInfo1->textureOffset = origin;
         copyInfo1->copySize = {copySize.width, copySize.height, depthInCopy1};
         copyInfo1->alignedOffset = offset;
-        copyInfo1->bufferSize = {copySize.width, blockInfo.ToTexelHeight(rowsPerImage),
-                                 depthInCopy1};
+        copyInfo1->bufferSize = {copySize.width, rowsPerImage, depthInCopy1};
     }
 
     {
@@ -689,16 +663,16 @@
         //                                               ^
         //                                     End of all buffer data
         //
-        DAWN_ASSERT(copySize.depthOrArrayLayers >= TexelCount{1});
-        constexpr TexelCount depthInCopy2{1};
-        const TexelCount rowsPerImageInTexels2 = copySize.height;
+        DAWN_ASSERT(copySize.depthOrArrayLayers >= BlockCount{1});
+        constexpr BlockCount depthInCopy2{1};
+        const BlockCount rowsPerImageInTexels2 = copySize.height;
 
         auto* copyInfo2 = copy.AddCopy();
         copyInfo2->bufferOffset = bufferOffset;
         copyInfo2->textureOffset = {origin.x, origin.y, origin.z + depthInCopy1};
         copyInfo2->copySize = {copySize.width, copySize.height, depthInCopy2};
-        copyInfo2->alignedOffset = offset + blockInfo.ToBytes(blocksPerRow * rowsPerImage *
-                                                              blockInfo.ToBlockDepth(depthInCopy1));
+        copyInfo2->alignedOffset =
+            offset + blockInfo.ToBytes(blocksPerRow * rowsPerImage * depthInCopy1);
         copyInfo2->bufferSize = {copySize.width, rowsPerImageInTexels2, depthInCopy2};
     }
 
diff --git a/src/dawn/native/d3d12/TextureCopySplitter.h b/src/dawn/native/d3d12/TextureCopySplitter.h
index 4703377..3f7a020 100644
--- a/src/dawn/native/d3d12/TextureCopySplitter.h
+++ b/src/dawn/native/d3d12/TextureCopySplitter.h
@@ -49,13 +49,13 @@
         // The 512-byte aligned offset into buffer
         uint64_t alignedOffset = 0;
         // Offset into texture
-        TexelOrigin3D textureOffset;
+        BlockOrigin3D textureOffset;
         // Offset into buffer
-        TexelOrigin3D bufferOffset;
+        BlockOrigin3D bufferOffset;
         // width,height,depth of the texture linearly laid out in the buffer
-        TexelExtent3D bufferSize;
+        BlockExtent3D bufferSize;
         // width,height,depth to copy
-        TexelExtent3D copySize;
+        BlockExtent3D copySize;
     };
 
     CopyInfo* AddCopy();
diff --git a/src/dawn/native/d3d12/UtilsD3D12.cpp b/src/dawn/native/d3d12/UtilsD3D12.cpp
index a294975..7d828ee 100644
--- a/src/dawn/native/d3d12/UtilsD3D12.cpp
+++ b/src/dawn/native/d3d12/UtilsD3D12.cpp
@@ -105,7 +105,7 @@
 D3D12_TEXTURE_COPY_LOCATION ComputeBufferLocationForCopyTextureRegion(
     const Texture* texture,
     ID3D12Resource* bufferResource,
-    const Extent3D& bufferSize,
+    const TexelExtent3D& bufferSize,
     const uint64_t offset,
     const uint32_t rowPitch,
     Aspect aspect) {
@@ -115,9 +115,10 @@
     bufferLocation.PlacedFootprint.Offset = offset;
     bufferLocation.PlacedFootprint.Footprint.Format =
         texture->GetD3D12CopyableSubresourceFormat(aspect);
-    bufferLocation.PlacedFootprint.Footprint.Width = bufferSize.width;
-    bufferLocation.PlacedFootprint.Footprint.Height = bufferSize.height;
-    bufferLocation.PlacedFootprint.Footprint.Depth = bufferSize.depthOrArrayLayers;
+    bufferLocation.PlacedFootprint.Footprint.Width = static_cast<uint32_t>(bufferSize.width);
+    bufferLocation.PlacedFootprint.Footprint.Height = static_cast<uint32_t>(bufferSize.height);
+    bufferLocation.PlacedFootprint.Footprint.Depth =
+        static_cast<uint32_t>(bufferSize.depthOrArrayLayers);
     bufferLocation.PlacedFootprint.Footprint.RowPitch = rowPitch;
     return bufferLocation;
 }
@@ -192,37 +193,42 @@
                                        ID3D12Resource* bufferResource,
                                        uint64_t baseOffset,
                                        uint64_t bufferBytesPerRow,
+                                       const TexelBlockInfo& blockInfo_in,
                                        TextureBase* textureBase,
                                        uint32_t textureMiplevel,
                                        uint32_t textureLayer,
                                        Aspect aspect) {
     Texture* texture = ToBackend(textureBase);
+    TypedTexelBlockInfo blockInfo{blockInfo_in};
     const D3D12_TEXTURE_COPY_LOCATION textureLocation =
         ComputeTextureCopyLocationForTexture(texture, textureMiplevel, textureLayer, aspect);
 
     for (uint32_t i = 0; i < baseCopySplit.count; ++i) {
         const TextureCopySubresource::CopyInfo& info = baseCopySplit.copies[i];
 
+        TexelOrigin3D textureOffset = blockInfo.ToTexel(info.textureOffset);
+        TexelOrigin3D bufferOffset = blockInfo.ToTexel(info.bufferOffset);
+        TexelExtent3D copySize = blockInfo.ToTexel(info.copySize);
+        TexelExtent3D bufferSize = blockInfo.ToTexel(info.bufferSize);
+
         const uint64_t offsetBytes = info.alignedOffset + baseOffset;
         const D3D12_TEXTURE_COPY_LOCATION bufferLocation =
-            ComputeBufferLocationForCopyTextureRegion(texture, bufferResource,
-                                                      info.bufferSize.ToExtent3D(), offsetBytes,
-                                                      bufferBytesPerRow, aspect);
+            ComputeBufferLocationForCopyTextureRegion(texture, bufferResource, bufferSize,
+                                                      offsetBytes, bufferBytesPerRow, aspect);
 
         if (direction == BufferTextureCopyDirection::B2T) {
-            const D3D12_BOX sourceRegion = ComputeD3D12BoxFromOffsetAndSize(
-                info.bufferOffset.ToOrigin3D(), info.copySize.ToExtent3D());
-            const Origin3D textureOffset = info.textureOffset.ToOrigin3D();
-            commandList->CopyTextureRegion(&textureLocation, textureOffset.x, textureOffset.y,
-                                           textureOffset.z, &bufferLocation, &sourceRegion);
+            const D3D12_BOX sourceRegion =
+                ComputeD3D12BoxFromOffsetAndSize(bufferOffset.ToOrigin3D(), copySize.ToExtent3D());
+            const Origin3D to = textureOffset.ToOrigin3D();
+            commandList->CopyTextureRegion(&textureLocation, to.x, to.y, to.z, &bufferLocation,
+                                           &sourceRegion);
         } else {
             DAWN_ASSERT(direction == BufferTextureCopyDirection::T2B);
-            const D3D12_BOX sourceRegion = ComputeD3D12BoxFromOffsetAndSize(
-                info.textureOffset.ToOrigin3D(), info.copySize.ToExtent3D());
-            const Origin3D bufferOffset = info.bufferOffset.ToOrigin3D();
-
-            commandList->CopyTextureRegion(&bufferLocation, bufferOffset.x, bufferOffset.y,
-                                           bufferOffset.z, &textureLocation, &sourceRegion);
+            const D3D12_BOX sourceRegion =
+                ComputeD3D12BoxFromOffsetAndSize(textureOffset.ToOrigin3D(), copySize.ToExtent3D());
+            const Origin3D bo = bufferOffset.ToOrigin3D();
+            commandList->CopyTextureRegion(&bufferLocation, bo.x, bo.y, bo.z, &textureLocation,
+                                           &sourceRegion);
         }
     }
 }
@@ -260,10 +266,10 @@
         const uint64_t bufferOffsetForNextLayer = bufferOffsetsForNextLayer[splitIndex];
         const uint32_t copyTextureLayer = copyLayer + textureCopy.origin.z;
 
-        RecordBufferTextureCopyFromSplits(direction, commandList, copySplitPerLayerBase,
-                                          bufferResource, bufferOffsetForNextLayer, bytesPerRow,
-                                          textureCopy.texture.Get(), textureCopy.mipLevel,
-                                          copyTextureLayer, textureCopy.aspect);
+        RecordBufferTextureCopyFromSplits(
+            direction, commandList, copySplitPerLayerBase, bufferResource, bufferOffsetForNextLayer,
+            bytesPerRow, blockInfo, textureCopy.texture.Get(), textureCopy.mipLevel,
+            copyTextureLayer, textureCopy.aspect);
 
         bufferOffsetsForNextLayer[splitIndex] += bytesPerLayer * copySplits.copySubresources.size();
     }
@@ -287,7 +293,7 @@
     for (uint32_t copyLayer = 0; copyLayer < copySize.depthOrArrayLayers; ++copyLayer) {
         uint32_t copyTextureLayer = copyLayer + textureCopy.origin.z;
         RecordBufferTextureCopyFromSplits(direction, commandList, copySubresource, bufferResource,
-                                          bufferOffsetForNextLayer, bytesPerRow,
+                                          bufferOffsetForNextLayer, bytesPerRow, blockInfo,
                                           textureCopy.texture.Get(), textureCopy.mipLevel,
                                           copyTextureLayer, textureCopy.aspect);
         bufferOffsetForNextLayer += bytesPerLayer;
@@ -327,8 +333,8 @@
                                                               blockInfo, offset, bytesPerRow);
             }
             RecordBufferTextureCopyFromSplits(direction, commandList, copyRegions, bufferResource,
-                                              0, bytesPerRow, texture, textureCopy.mipLevel, 0,
-                                              textureCopy.aspect);
+                                              0, bytesPerRow, blockInfo, texture,
+                                              textureCopy.mipLevel, 0, textureCopy.aspect);
             break;
         }
 
@@ -357,8 +363,8 @@
                                                          offset, bytesPerRow, rowsPerImage);
             }
             RecordBufferTextureCopyFromSplits(direction, commandList, copyRegions, bufferResource,
-                                              0, bytesPerRow, texture, textureCopy.mipLevel, 0,
-                                              textureCopy.aspect);
+                                              0, bytesPerRow, blockInfo, texture,
+                                              textureCopy.mipLevel, 0, textureCopy.aspect);
             break;
         }
     }
diff --git a/src/dawn/tests/unittests/d3d12/CopySplitTests.cpp b/src/dawn/tests/unittests/d3d12/CopySplitTests.cpp
index 619158e..a972bb4 100644
--- a/src/dawn/tests/unittests/d3d12/CopySplitTests.cpp
+++ b/src/dawn/tests/unittests/d3d12/CopySplitTests.cpp
@@ -78,12 +78,12 @@
 }
 
 // TODO(425944899): Store TexelOrigin3D in TextureSpec
-TexelOrigin3D ToTOrigin3D(const TextureSpec& textureSpec) {
+TexelOrigin3D ToTexelOrigin3D(const TextureSpec& textureSpec) {
     return TexelOrigin3D{textureSpec.x, textureSpec.y, textureSpec.z};
 }
 
 // TODO(425944899): Store TexelExtent3D in TextureSpec
-TexelExtent3D ToTExtent3D(const TextureSpec& textureSpec) {
+TexelExtent3D ToTexelExtent3D(const TextureSpec& textureSpec) {
     return TexelExtent3D{textureSpec.width, textureSpec.height, textureSpec.depthOrArrayLayers};
 }
 
@@ -95,9 +95,10 @@
     TypedTexelBlockInfo blockInfo = ToTypedTexelBlockInfo(textureSpec);
     for (uint32_t i = 0; i < copySplit.count; ++i) {
         const auto& copy = copySplit.copies[i];
-        const TexelExtent3D& copySize = copy.copySize;
-        const TexelOrigin3D& bufferOffset = copy.bufferOffset;
-        const TexelExtent3D& bufferSize = copy.bufferSize;
+        // TODO(425944899): Rework this function to work in blocks, not texels
+        const TexelExtent3D& copySize = blockInfo.ToTexel(copy.copySize);
+        const TexelOrigin3D& bufferOffset = blockInfo.ToTexel(copy.bufferOffset);
+        const TexelExtent3D& bufferSize = blockInfo.ToTexel(copy.bufferSize);
         ASSERT_LE(bufferOffset.x + copySize.width, bufferSize.width);
         ASSERT_LE(bufferOffset.y + copySize.height, bufferSize.height);
         ASSERT_LE(bufferOffset.z + copySize.depthOrArrayLayers, bufferSize.depthOrArrayLayers);
@@ -170,19 +171,21 @@
 }
 
 // Check that no pair of copy regions intersect each other
-void ValidateDisjoint(const TextureCopySubresource& copySplit) {
+void ValidateDisjoint(const TextureSpec& textureSpec, const TextureCopySubresource& copySplit) {
+    TypedTexelBlockInfo blockInfo = ToTypedTexelBlockInfo(textureSpec);
     for (uint32_t i = 0; i < copySplit.count; ++i) {
         const auto& a = copySplit.copies[i];
-        const TexelExtent3D& copySizeA = a.copySize;
-        const TexelOrigin3D& textureOffsetA = a.textureOffset;
+        // TODO(425944899): Rework this function to work in blocks, not texels
+        const TexelExtent3D& copySizeA = blockInfo.ToTexel(a.copySize);
+        const TexelOrigin3D& textureOffsetA = blockInfo.ToTexel(a.textureOffset);
         for (uint32_t j = i + 1; j < copySplit.count; ++j) {
             const auto& b = copySplit.copies[j];
             // If textureOffset.x is 0, and copySize.width is 2, we are copying pixel 0 and
             // 1. We never touch pixel 2 on x-axis. So the copied range on x-axis should be
             // [textureOffset.x, textureOffset.x + copySize.width - 1] and both ends are
             // included.
-            const TexelExtent3D& copySizeB = b.copySize;
-            const TexelOrigin3D& textureOffsetB = b.textureOffset;
+            const TexelExtent3D& copySizeB = blockInfo.ToTexel(b.copySize);
+            const TexelOrigin3D& textureOffsetB = blockInfo.ToTexel(b.textureOffset);
             bool overlapX =
                 InclusiveRangesOverlap(textureOffsetA.x, textureOffsetA.x + copySizeA.width - 1_tc,
                                        textureOffsetB.x, textureOffsetB.x + copySizeB.width - 1_tc);
@@ -201,9 +204,11 @@
 void ValidateTextureBounds(const TextureSpec& textureSpec,
                            const TextureCopySubresource& copySplit) {
     ASSERT_GT(copySplit.count, 0u);
+    TypedTexelBlockInfo blockInfo = ToTypedTexelBlockInfo(textureSpec);
 
-    const TexelExtent3D& copySize0 = copySplit.copies[0].copySize;
-    const TexelOrigin3D& textureOffset0 = copySplit.copies[0].textureOffset;
+    // TODO(425944899): Rework this function to work in blocks, not texels
+    const TexelExtent3D& copySize0 = blockInfo.ToTexel(copySplit.copies[0].copySize);
+    const TexelOrigin3D& textureOffset0 = blockInfo.ToTexel(copySplit.copies[0].textureOffset);
     TexelCount minX = textureOffset0.x;
     TexelCount minY = textureOffset0.y;
     TexelCount minZ = textureOffset0.z;
@@ -213,11 +218,11 @@
 
     for (uint32_t i = 1; i < copySplit.count; ++i) {
         const auto& copy = copySplit.copies[i];
-        const TexelOrigin3D& textureOffset = copy.textureOffset;
+        const TexelOrigin3D& textureOffset = blockInfo.ToTexel(copy.textureOffset);
         minX = std::min(minX, textureOffset.x);
         minY = std::min(minY, textureOffset.y);
         minZ = std::min(minZ, textureOffset.z);
-        const TexelExtent3D& copySize = copy.copySize;
+        const TexelExtent3D& copySize = blockInfo.ToTexel(copy.copySize);
         maxX = std::max(maxX, textureOffset.x + copySize.width);
         maxY = std::max(maxY, textureOffset.y + copySize.height);
         maxZ = std::max(maxZ, textureOffset.z + copySize.depthOrArrayLayers);
@@ -234,15 +239,18 @@
 // Validate that the number of pixels copied is exactly equal to the number of pixels in the
 // texture region
 void ValidatePixelCount(const TextureSpec& textureSpec, const TextureCopySubresource& copySplit) {
-    TexelCount count{0};
+    TypedTexelBlockInfo blockInfo = ToTypedTexelBlockInfo(textureSpec);
+    TexelCount totalCopiedTexels{0};
     for (uint32_t i = 0; i < copySplit.count; ++i) {
         const auto& copy = copySplit.copies[i];
-        const TexelExtent3D& copySize = copy.copySize;
-        TexelCount copiedPixels = copySize.width * copySize.height * copySize.depthOrArrayLayers;
-        ASSERT_GT(copiedPixels, 0_tc);
-        count += copiedPixels;
+        // TODO(425944899): Rework this function to work in blocks, not texels
+        const TexelExtent3D& copySize = blockInfo.ToTexel(copy.copySize);
+        TexelCount copiedTexels = copySize.width * copySize.height * copySize.depthOrArrayLayers;
+        ASSERT_GT(copiedTexels, 0_tc);
+        totalCopiedTexels += copiedTexels;
     }
-    ASSERT_EQ(count, textureSpec.width * textureSpec.height * textureSpec.depthOrArrayLayers);
+    ASSERT_EQ(totalCopiedTexels,
+              textureSpec.width * textureSpec.height * textureSpec.depthOrArrayLayers);
 }
 
 // Check that every buffer offset is at the correct pixel location
@@ -256,23 +264,22 @@
 
     for (uint32_t i = 0; i < copySplit.count; ++i) {
         const auto& copy = copySplit.copies[i];
-        const TexelOrigin3D& bufferOffset = copy.bufferOffset;
-        const TexelOrigin3D& textureOffset = copy.textureOffset;
+        const BlockOrigin3D& bufferOffset = copy.bufferOffset;
+        const BlockOrigin3D& textureOffset = copy.textureOffset;
         // Note that for relaxed, the row pitch (bytesPerRow) is not required to be 256 bytes,
         // but Dawn currently doesn't do anything about this.
         BlockCount rowPitchInBlocks = blockInfo.BytesToBlocks(bufferSpec.bytesPerRow);
         BlockCount slicePitchInBlocks = rowPitchInBlocks * bufferSpec.rowsPerImage;
-        BlockCount absoluteOffsetInBlocks =
-            blockInfo.BytesToBlocks(copy.alignedOffset) + blockInfo.ToBlockWidth(bufferOffset.x) +
-            blockInfo.ToBlockHeight(bufferOffset.y) * rowPitchInBlocks;
+        BlockCount absoluteOffsetInBlocks = blockInfo.BytesToBlocks(copy.alignedOffset) +
+                                            bufferOffset.x + bufferOffset.y * rowPitchInBlocks;
 
         // There is one empty row at most in a 2D copy region. However, it is not true for
         // a 3D texture copy region when we are copying the last row of each slice. We may
         // need to offset a lot rows and copy.bufferOffset.y may be big.
         if (dimension == wgpu::TextureDimension::e2D) {
-            ASSERT_LE(bufferOffset.y, textureSpec.blockHeight);
+            ASSERT_LE(bufferOffset.y, BlockCount{1});
         }
-        ASSERT_EQ(bufferOffset.z, 0_tc);
+        ASSERT_EQ(bufferOffset.z, 0_bc);
 
         ASSERT_GE(absoluteOffsetInBlocks, blockInfo.BytesToBlocks(bufferSpec.offset));
 
@@ -283,10 +290,9 @@
         BlockCount yBlocks = (relativeOffsetInBlocks % slicePitchInBlocks) / rowPitchInBlocks;
         BlockCount xBlocks = relativeOffsetInBlocks % rowPitchInBlocks;
 
-        ASSERT_EQ(textureOffset.x - textureSpec.x, blockInfo.ToTexelWidth(xBlocks));
-        ASSERT_EQ(textureOffset.y - textureSpec.y, blockInfo.ToTexelHeight(yBlocks));
-        ASSERT_EQ(textureOffset.z - textureSpec.z, blockInfo.ToTexelDepth(z))
-            << "textureOffset: " << textureOffset.z;
+        ASSERT_EQ(textureOffset.x - blockInfo.ToBlockWidth(textureSpec.x), xBlocks);
+        ASSERT_EQ(textureOffset.y - blockInfo.ToBlockHeight(textureSpec.y), yBlocks);
+        ASSERT_EQ(textureOffset.z - blockInfo.ToBlockDepth(textureSpec.z), z);
     }
 }
 
@@ -297,7 +303,7 @@
                        bool relaxed) {
     ValidateFootprints(textureSpec, bufferSpec, copySplit, dimension);
     ValidateOffset(copySplit, relaxed);
-    ValidateDisjoint(copySplit);
+    ValidateDisjoint(textureSpec, copySplit);
     ValidateTextureBounds(textureSpec, copySplit);
     ValidatePixelCount(textureSpec, copySplit);
     ValidateBufferOffset(textureSpec, bufferSpec, copySplit, dimension, relaxed);
@@ -765,14 +771,14 @@
                 }
                 if (relaxed) {
                     copySplit = Compute2DTextureCopySubresourceWithRelaxedRowPitchAndOffset(
-                        ToTOrigin3D(textureSpec).ToOrigin3D(),
-                        ToTExtent3D(textureSpec).ToExtent3D(),
+                        ToTexelOrigin3D(textureSpec).ToOrigin3D(),
+                        ToTexelExtent3D(textureSpec).ToExtent3D(),
                         ToTypedTexelBlockInfo(textureSpec).ToTexelBlockInfo(), bufferSpec.offset,
                         bufferSpec.bytesPerRow);
                 } else {
                     copySplit = Compute2DTextureCopySubresource(
-                        ToTOrigin3D(textureSpec).ToOrigin3D(),
-                        ToTExtent3D(textureSpec).ToExtent3D(),
+                        ToTexelOrigin3D(textureSpec).ToOrigin3D(),
+                        ToTexelExtent3D(textureSpec).ToExtent3D(),
                         ToTypedTexelBlockInfo(textureSpec).ToTexelBlockInfo(), bufferSpec.offset,
                         bufferSpec.bytesPerRow);
                 }
@@ -781,14 +787,14 @@
             case wgpu::TextureDimension::e3D: {
                 if (relaxed) {
                     copySplit = Compute3DTextureCopySubresourceWithRelaxedRowPitchAndOffset(
-                        ToTOrigin3D(textureSpec).ToOrigin3D(),
-                        ToTExtent3D(textureSpec).ToExtent3D(),
+                        ToTexelOrigin3D(textureSpec).ToOrigin3D(),
+                        ToTexelExtent3D(textureSpec).ToExtent3D(),
                         ToTypedTexelBlockInfo(textureSpec).ToTexelBlockInfo(), bufferSpec.offset,
                         bufferSpec.bytesPerRow, static_cast<uint32_t>(bufferSpec.rowsPerImage));
                 } else {
                     copySplit = Compute3DTextureCopySplits(
-                        ToTOrigin3D(textureSpec).ToOrigin3D(),
-                        ToTExtent3D(textureSpec).ToExtent3D(),
+                        ToTexelOrigin3D(textureSpec).ToOrigin3D(),
+                        ToTexelExtent3D(textureSpec).ToExtent3D(),
                         ToTypedTexelBlockInfo(textureSpec).ToTexelBlockInfo(), bufferSpec.offset,
                         bufferSpec.bytesPerRow, static_cast<uint32_t>(bufferSpec.rowsPerImage));
                 }
@@ -856,6 +862,9 @@
 TEST_P(CopySplitTest, TextureX) {
     for (TextureSpec textureSpec : kBaseTextureSpecs) {
         for (uint32_t val : kCheckValues) {
+            if (TexelCount{val} % textureSpec.blockWidth != 0_tc) {
+                continue;
+            }
             textureSpec.x = TexelCount{val};
             for (const BufferSpec& bufferSpec : BaseBufferSpecs(textureSpec)) {
                 DoTest(textureSpec, bufferSpec);
@@ -867,6 +876,9 @@
 TEST_P(CopySplitTest, TextureY) {
     for (TextureSpec textureSpec : kBaseTextureSpecs) {
         for (uint32_t val : kCheckValues) {
+            if (TexelCount{val} % textureSpec.blockHeight != 0_tc) {
+                continue;
+            }
             textureSpec.y = TexelCount{val};
             for (const BufferSpec& bufferSpec : BaseBufferSpecs(textureSpec)) {
                 DoTest(textureSpec, bufferSpec);