Support depth-only/stencil-only copies on D3D12

Bug: dawn:439
Change-Id: I919a5e7bcb46f1817f9b15aaf49a1a72680aa47a
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/24960
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
diff --git a/src/dawn_native/d3d12/CommandBufferD3D12.cpp b/src/dawn_native/d3d12/CommandBufferD3D12.cpp
index 8fd4a5a..684a5e9 100644
--- a/src/dawn_native/d3d12/CommandBufferD3D12.cpp
+++ b/src/dawn_native/d3d12/CommandBufferD3D12.cpp
@@ -118,7 +118,7 @@
                 const D3D12_TEXTURE_COPY_LOCATION bufferLocation =
                     ComputeBufferLocationForCopyTextureRegion(texture, buffer->GetD3D12Resource(),
                                                               info.bufferSize, offset,
-                                                              bufferBytesPerRow);
+                                                              bufferBytesPerRow, aspect);
                 const D3D12_BOX sourceRegion =
                     ComputeD3D12BoxFromOffsetAndSize(info.textureOffset, info.copySize);
 
@@ -706,15 +706,17 @@
                                                         subresources);
                     buffer->TrackUsageAndTransitionNow(commandContext, wgpu::BufferUsage::CopyDst);
 
+                    const TexelBlockInfo& blockInfo =
+                        texture->GetFormat().GetTexelBlockInfo(copy->source.aspect);
+
                     // See comments around ComputeTextureCopySplits() for more details.
                     const TextureCopySplits copySplits = ComputeTextureCopySplits(
-                        copy->source.origin, copy->copySize, texture->GetFormat(),
-                        copy->destination.offset, copy->destination.bytesPerRow,
-                        copy->destination.rowsPerImage);
+                        copy->source.origin, copy->copySize, blockInfo, copy->destination.offset,
+                        copy->destination.bytesPerRow, copy->destination.rowsPerImage);
 
                     const uint64_t bytesPerSlice =
                         copy->destination.bytesPerRow *
-                        (copy->destination.rowsPerImage / texture->GetFormat().blockHeight);
+                        (copy->destination.rowsPerImage / blockInfo.blockHeight);
 
                     // copySplits.copies2D[1] is always calculated for the second copy slice with
                     // extra "bytesPerSlice" copy offset compared with the first copy slice. So
diff --git a/src/dawn_native/d3d12/DeviceD3D12.cpp b/src/dawn_native/d3d12/DeviceD3D12.cpp
index 2e2e60e..b040b1d 100644
--- a/src/dawn_native/d3d12/DeviceD3D12.cpp
+++ b/src/dawn_native/d3d12/DeviceD3D12.cpp
@@ -17,6 +17,7 @@
 #include "common/Assert.h"
 #include "dawn_native/BackendConnection.h"
 #include "dawn_native/ErrorData.h"
+#include "dawn_native/Format.h"
 #include "dawn_native/Instance.h"
 #include "dawn_native/d3d12/AdapterD3D12.h"
 #include "dawn_native/d3d12/BackendD3D12.h"
diff --git a/src/dawn_native/d3d12/TextureCopySplitter.cpp b/src/dawn_native/d3d12/TextureCopySplitter.cpp
index 07da049..e1d380c 100644
--- a/src/dawn_native/d3d12/TextureCopySplitter.cpp
+++ b/src/dawn_native/d3d12/TextureCopySplitter.cpp
@@ -21,7 +21,7 @@
 namespace dawn_native { namespace d3d12 {
 
     namespace {
-        Origin3D ComputeTexelOffsets(const Format& format,
+        Origin3D ComputeTexelOffsets(const TexelBlockInfo& blockInfo,
                                      uint32_t offset,
                                      uint32_t bytesPerRow,
                                      uint32_t slicePitch) {
@@ -32,20 +32,20 @@
             uint32_t byteOffsetY = offset % slicePitch;
             uint32_t byteOffsetZ = offset - byteOffsetY;
 
-            return {byteOffsetX / format.blockByteSize * format.blockWidth,
-                    byteOffsetY / bytesPerRow * format.blockHeight, byteOffsetZ / slicePitch};
+            return {byteOffsetX / blockInfo.blockByteSize * blockInfo.blockWidth,
+                    byteOffsetY / bytesPerRow * blockInfo.blockHeight, byteOffsetZ / slicePitch};
         }
     }  // namespace
 
     Texture2DCopySplit ComputeTextureCopySplit(Origin3D origin,
                                                Extent3D copySize,
-                                               const Format& format,
+                                               const TexelBlockInfo& blockInfo,
                                                uint64_t offset,
                                                uint32_t bytesPerRow,
                                                uint32_t rowsPerImage) {
         Texture2DCopySplit copy;
 
-        ASSERT(bytesPerRow % format.blockByteSize == 0);
+        ASSERT(bytesPerRow % blockInfo.blockByteSize == 0);
 
         uint64_t alignedOffset =
             offset & ~static_cast<uint64_t>(D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT - 1);
@@ -71,12 +71,14 @@
         ASSERT(alignedOffset < offset);
         ASSERT(offset - alignedOffset < D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT);
 
-        uint32_t slicePitch = bytesPerRow * (rowsPerImage / format.blockHeight);
+        uint32_t slicePitch = bytesPerRow * (rowsPerImage / blockInfo.blockHeight);
         Origin3D texelOffset = ComputeTexelOffsets(
-            format, static_cast<uint32_t>(offset - alignedOffset), bytesPerRow, slicePitch);
+            blockInfo, static_cast<uint32_t>(offset - alignedOffset), bytesPerRow, slicePitch);
 
-        uint32_t copyBytesPerRowPitch = copySize.width / format.blockWidth * format.blockByteSize;
-        uint32_t byteOffsetInRowPitch = texelOffset.x / format.blockWidth * format.blockByteSize;
+        uint32_t copyBytesPerRowPitch =
+            copySize.width / blockInfo.blockWidth * blockInfo.blockByteSize;
+        uint32_t byteOffsetInRowPitch =
+            texelOffset.x / blockInfo.blockWidth * blockInfo.blockByteSize;
         if (copyBytesPerRowPitch + byteOffsetInRowPitch <= bytesPerRow) {
             // 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,7 +156,7 @@
         copy.copies[0].textureOffset = origin;
 
         ASSERT(bytesPerRow > byteOffsetInRowPitch);
-        uint32_t texelsPerRow = bytesPerRow / format.blockByteSize * format.blockWidth;
+        uint32_t texelsPerRow = bytesPerRow / blockInfo.blockByteSize * blockInfo.blockWidth;
         copy.copies[0].copySize.width = texelsPerRow - texelOffset.x;
         copy.copies[0].copySize.height = copySize.height;
         copy.copies[0].copySize.depth = copySize.depth;
@@ -174,10 +176,10 @@
         copy.copies[1].copySize.depth = copySize.depth;
 
         copy.copies[1].bufferOffset.x = 0;
-        copy.copies[1].bufferOffset.y = texelOffset.y + format.blockHeight;
+        copy.copies[1].bufferOffset.y = texelOffset.y + blockInfo.blockHeight;
         copy.copies[1].bufferOffset.z = texelOffset.z;
         copy.copies[1].bufferSize.width = copy.copies[1].copySize.width;
-        copy.copies[1].bufferSize.height = rowsPerImage + texelOffset.y + format.blockHeight;
+        copy.copies[1].bufferSize.height = rowsPerImage + texelOffset.y + blockInfo.blockHeight;
         copy.copies[1].bufferSize.depth = copySize.depth + texelOffset.z;
 
         return copy;
@@ -185,13 +187,13 @@
 
     TextureCopySplits ComputeTextureCopySplits(Origin3D origin,
                                                Extent3D copySize,
-                                               const Format& format,
+                                               const TexelBlockInfo& blockInfo,
                                                uint64_t offset,
                                                uint32_t bytesPerRow,
                                                uint32_t rowsPerImage) {
         TextureCopySplits copies;
 
-        const uint64_t bytesPerSlice = bytesPerRow * (rowsPerImage / format.blockHeight);
+        const uint64_t bytesPerSlice = bytesPerRow * (rowsPerImage / blockInfo.blockHeight);
 
         // The function ComputeTextureCopySplit() decides how to split the copy based on:
         // - the alignment of the buffer offset with D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT (512)
@@ -207,8 +209,8 @@
         const dawn_native::Extent3D copyOneLayerSize = {copySize.width, copySize.height, 1};
         const dawn_native::Origin3D copyFirstLayerOrigin = {origin.x, origin.y, 0};
 
-        copies.copies2D[0] = ComputeTextureCopySplit(copyFirstLayerOrigin, copyOneLayerSize, format,
-                                                     offset, bytesPerRow, rowsPerImage);
+        copies.copies2D[0] = ComputeTextureCopySplit(copyFirstLayerOrigin, copyOneLayerSize,
+                                                     blockInfo, offset, bytesPerRow, rowsPerImage);
 
         // When the copy only refers one texture 2D array layer copies.copies2D[1] will never be
         // used so we can safely early return here.
@@ -222,7 +224,7 @@
         } else {
             const uint64_t bufferOffsetNextLayer = offset + bytesPerSlice;
             copies.copies2D[1] =
-                ComputeTextureCopySplit(copyFirstLayerOrigin, copyOneLayerSize, format,
+                ComputeTextureCopySplit(copyFirstLayerOrigin, copyOneLayerSize, blockInfo,
                                         bufferOffsetNextLayer, bytesPerRow, rowsPerImage);
         }
 
diff --git a/src/dawn_native/d3d12/TextureCopySplitter.h b/src/dawn_native/d3d12/TextureCopySplitter.h
index f9f4b36..962c332 100644
--- a/src/dawn_native/d3d12/TextureCopySplitter.h
+++ b/src/dawn_native/d3d12/TextureCopySplitter.h
@@ -21,7 +21,7 @@
 
 namespace dawn_native {
 
-    struct Format;
+    struct TexelBlockInfo;
 
 }  // namespace dawn_native
 
@@ -51,14 +51,14 @@
 
     Texture2DCopySplit ComputeTextureCopySplit(Origin3D origin,
                                                Extent3D copySize,
-                                               const Format& format,
+                                               const TexelBlockInfo& blockInfo,
                                                uint64_t offset,
                                                uint32_t bytesPerRow,
                                                uint32_t rowsPerImage);
 
     TextureCopySplits ComputeTextureCopySplits(Origin3D origin,
                                                Extent3D copySize,
-                                               const Format& format,
+                                               const TexelBlockInfo& blockInfo,
                                                uint64_t offset,
                                                uint32_t bytesPerRow,
                                                uint32_t rowsPerImage);
diff --git a/src/dawn_native/d3d12/TextureD3D12.cpp b/src/dawn_native/d3d12/TextureD3D12.cpp
index 7767aa8..048822c 100644
--- a/src/dawn_native/d3d12/TextureD3D12.cpp
+++ b/src/dawn_native/d3d12/TextureD3D12.cpp
@@ -548,6 +548,26 @@
         return mResourceAllocation.GetD3D12Resource();
     }
 
+    DXGI_FORMAT Texture::GetD3D12CopyableSubresourceFormat(Aspect aspect) const {
+        ASSERT(GetFormat().aspects & aspect);
+
+        switch (GetFormat().format) {
+            case wgpu::TextureFormat::Depth24PlusStencil8:
+                switch (aspect) {
+                    case Aspect::Depth:
+                        return DXGI_FORMAT_R32_FLOAT;
+                    case Aspect::Stencil:
+                        return DXGI_FORMAT_R8_UINT;
+                    default:
+                        UNREACHABLE();
+                        return GetD3D12Format();
+                }
+            default:
+                ASSERT(HasOneBit(GetFormat().aspects));
+                return GetD3D12Format();
+        }
+    }
+
     void Texture::TrackUsageAndTransitionNow(CommandRecordingContext* commandContext,
                                              wgpu::TextureUsage usage,
                                              const SubresourceRange& range) {
@@ -967,7 +987,7 @@
                         D3D12_TEXTURE_COPY_LOCATION bufferLocation =
                             ComputeBufferLocationForCopyTextureRegion(
                                 this, ToBackend(uploadHandle.stagingBuffer)->GetResource(),
-                                info.bufferSize, copySplit.offset, bytesPerRow);
+                                info.bufferSize, copySplit.offset, bytesPerRow, Aspect::Color);
                         D3D12_BOX sourceRegion =
                             ComputeD3D12BoxFromOffsetAndSize(info.bufferOffset, info.copySize);
 
diff --git a/src/dawn_native/d3d12/TextureD3D12.h b/src/dawn_native/d3d12/TextureD3D12.h
index ef17bf4..d2df6be 100644
--- a/src/dawn_native/d3d12/TextureD3D12.h
+++ b/src/dawn_native/d3d12/TextureD3D12.h
@@ -48,6 +48,7 @@
 
         DXGI_FORMAT GetD3D12Format() const;
         ID3D12Resource* GetD3D12Resource() const;
+        DXGI_FORMAT GetD3D12CopyableSubresourceFormat(Aspect aspect) const;
 
         D3D12_RENDER_TARGET_VIEW_DESC GetRTVDescriptor(uint32_t mipLevel,
                                                        uint32_t baseArrayLayer,
diff --git a/src/dawn_native/d3d12/UtilsD3D12.cpp b/src/dawn_native/d3d12/UtilsD3D12.cpp
index 6a3b4bc..2837274 100644
--- a/src/dawn_native/d3d12/UtilsD3D12.cpp
+++ b/src/dawn_native/d3d12/UtilsD3D12.cpp
@@ -68,7 +68,7 @@
     D3D12_TEXTURE_COPY_LOCATION ComputeTextureCopyLocationForTexture(const Texture* texture,
                                                                      uint32_t level,
                                                                      uint32_t slice,
-                                                                     const Aspect& aspect) {
+                                                                     Aspect aspect) {
         D3D12_TEXTURE_COPY_LOCATION copyLocation;
         copyLocation.pResource = texture->GetD3D12Resource();
         copyLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
@@ -82,12 +82,14 @@
         ID3D12Resource* bufferResource,
         const Extent3D& bufferSize,
         const uint64_t offset,
-        const uint32_t rowPitch) {
+        const uint32_t rowPitch,
+        Aspect aspect) {
         D3D12_TEXTURE_COPY_LOCATION bufferLocation;
         bufferLocation.pResource = bufferResource;
         bufferLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
         bufferLocation.PlacedFootprint.Offset = offset;
-        bufferLocation.PlacedFootprint.Footprint.Format = texture->GetD3D12Format();
+        bufferLocation.PlacedFootprint.Footprint.Format =
+            texture->GetD3D12CopyableSubresourceFormat(aspect);
         bufferLocation.PlacedFootprint.Footprint.Width = bufferSize.width;
         bufferLocation.PlacedFootprint.Footprint.Height = bufferSize.height;
         bufferLocation.PlacedFootprint.Footprint.Depth = bufferSize.depth;
@@ -147,7 +149,8 @@
                                                        Texture* texture,
                                                        uint32_t textureMiplevel,
                                                        uint32_t textureSlice,
-                                                       const Aspect& aspect) {
+                                                       Aspect aspect) {
+        ASSERT(HasOneBit(aspect));
         const D3D12_TEXTURE_COPY_LOCATION textureLocation =
             ComputeTextureCopyLocationForTexture(texture, textureMiplevel, textureSlice, aspect);
 
@@ -160,7 +163,7 @@
             // members in Texture2DCopySplit::CopyInfo.
             const D3D12_TEXTURE_COPY_LOCATION bufferLocation =
                 ComputeBufferLocationForCopyTextureRegion(texture, bufferResource, info.bufferSize,
-                                                          offsetBytes, bufferBytesPerRow);
+                                                          offsetBytes, bufferBytesPerRow, aspect);
             const D3D12_BOX sourceRegion =
                 ComputeD3D12BoxFromOffsetAndSize(info.bufferOffset, info.copySize);
 
@@ -178,14 +181,14 @@
                                           const uint64_t offsetBytes,
                                           const uint32_t bytesPerRow,
                                           const uint32_t rowsPerImage,
-                                          const Aspect& aspects) {
+                                          Aspect aspect) {
+        ASSERT(HasOneBit(aspect));
         // See comments in ComputeTextureCopySplits() for more details.
-        const TextureCopySplits copySplits =
-            ComputeTextureCopySplits(textureCopy.origin, copySize, texture->GetFormat(),
-                                     offsetBytes, bytesPerRow, rowsPerImage);
+        const TexelBlockInfo& blockInfo = texture->GetFormat().GetTexelBlockInfo(aspect);
+        const TextureCopySplits copySplits = ComputeTextureCopySplits(
+            textureCopy.origin, copySize, blockInfo, offsetBytes, bytesPerRow, rowsPerImage);
 
-        const uint64_t bytesPerSlice =
-            bytesPerRow * (rowsPerImage / texture->GetFormat().blockHeight);
+        const uint64_t bytesPerSlice = bytesPerRow * (rowsPerImage / blockInfo.blockHeight);
 
         // copySplits.copies2D[1] is always calculated for the second copy slice with
         // extra "bytesPerSlice" copy offset compared with the first copy slice. So
@@ -207,7 +210,7 @@
             RecordCopyBufferToTextureFromTextureCopySplit(
                 commandContext->GetCommandList(), copySplitPerLayerBase, bufferResource,
                 bufferOffsetForNextSlice, bytesPerRow, texture, textureCopy.mipLevel,
-                copyTextureLayer, aspects);
+                copyTextureLayer, aspect);
 
             bufferOffsetsForNextSlice[splitIndex] += bytesPerSlice * copySplits.copies2D.size();
         }
diff --git a/src/dawn_native/d3d12/UtilsD3D12.h b/src/dawn_native/d3d12/UtilsD3D12.h
index ad47570..6109c0f 100644
--- a/src/dawn_native/d3d12/UtilsD3D12.h
+++ b/src/dawn_native/d3d12/UtilsD3D12.h
@@ -31,14 +31,15 @@
     D3D12_TEXTURE_COPY_LOCATION ComputeTextureCopyLocationForTexture(const Texture* texture,
                                                                      uint32_t level,
                                                                      uint32_t slice,
-                                                                     const Aspect& aspect);
+                                                                     Aspect aspect);
 
     D3D12_TEXTURE_COPY_LOCATION ComputeBufferLocationForCopyTextureRegion(
         const Texture* texture,
         ID3D12Resource* bufferResource,
         const Extent3D& bufferSize,
         const uint64_t offset,
-        const uint32_t rowPitch);
+        const uint32_t rowPitch,
+        Aspect aspect);
     D3D12_BOX ComputeD3D12BoxFromOffsetAndSize(const Origin3D& offset, const Extent3D& copySize);
 
     bool IsTypeless(DXGI_FORMAT format);
@@ -51,7 +52,7 @@
                                                        Texture* texture,
                                                        uint32_t textureMiplevel,
                                                        uint32_t textureSlice,
-                                                       const Aspect& aspect);
+                                                       Aspect aspect);
 
     void CopyBufferToTextureWithCopySplit(CommandRecordingContext* commandContext,
                                           const TextureCopy& textureCopy,
@@ -61,7 +62,7 @@
                                           const uint64_t offset,
                                           const uint32_t bytesPerRow,
                                           const uint32_t rowsPerImage,
-                                          const Aspect& aspect);
+                                          Aspect aspect);
 
 }}  // namespace dawn_native::d3d12
 
diff --git a/src/tests/end2end/DepthStencilCopyTests.cpp b/src/tests/end2end/DepthStencilCopyTests.cpp
index 72c9e66..22e3e63 100644
--- a/src/tests/end2end/DepthStencilCopyTests.cpp
+++ b/src/tests/end2end/DepthStencilCopyTests.cpp
@@ -320,4 +320,4 @@
     }
 }
 
-DAWN_INSTANTIATE_TEST(DepthStencilCopyTests, MetalBackend(), VulkanBackend());
+DAWN_INSTANTIATE_TEST(DepthStencilCopyTests, D3D12Backend(), MetalBackend(), VulkanBackend());
diff --git a/src/tests/end2end/TextureZeroInitTests.cpp b/src/tests/end2end/TextureZeroInitTests.cpp
index 7289757..bca410f 100644
--- a/src/tests/end2end/TextureZeroInitTests.cpp
+++ b/src/tests/end2end/TextureZeroInitTests.cpp
@@ -651,10 +651,12 @@
                             depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_StencilOnly));
 
         // TODO(crbug.com/dawn/439): Implement stencil copies on other platforms
-        if (IsMetal() || IsVulkan()) {
+        if (IsMetal() || IsVulkan() || IsD3D12()) {
             // Check by copy that the stencil data is 2.
-            EXPECT_LAZY_CLEAR(0u, EXPECT_TEXTURE_EQ(uint8_t(2), depthStencilTexture, 0, 0, 0, 0,
-                                                    wgpu::TextureAspect::StencilOnly));
+            std::vector<uint8_t> expected(kSize * kSize, 2);
+            EXPECT_LAZY_CLEAR(
+                0u, EXPECT_TEXTURE_EQ(expected.data(), depthStencilTexture, 0, 0, kSize, kSize, 0,
+                                      0, wgpu::TextureAspect::StencilOnly));
         }
     }
 
@@ -722,10 +724,12 @@
                             depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_StencilOnly));
 
         // TODO(crbug.com/dawn/439): Implement stencil copies on other platforms
-        if (IsMetal() || IsVulkan()) {
+        if (IsMetal() || IsVulkan() || IsD3D12()) {
             // Check by copy that the stencil data is 0.
-            EXPECT_LAZY_CLEAR(0u, EXPECT_TEXTURE_EQ(uint8_t(0), depthStencilTexture, 0, 0, 0, 0,
-                                                    wgpu::TextureAspect::StencilOnly));
+            std::vector<uint8_t> expected(kSize * kSize, 0);
+            EXPECT_LAZY_CLEAR(
+                0u, EXPECT_TEXTURE_EQ(expected.data(), depthStencilTexture, 0, 0, kSize, kSize, 0,
+                                      0, wgpu::TextureAspect::StencilOnly));
         }
     }
 }
@@ -734,7 +738,7 @@
 // Lazy clear of the stencil aspect via copy should not touch depth.
 TEST_P(TextureZeroInitTest, IndependentDepthStencilCopyAfterDiscard) {
     // TODO(crbug.com/dawn/439): Implement stencil copies on other platforms
-    DAWN_SKIP_TEST_IF(!(IsMetal() || IsVulkan()));
+    DAWN_SKIP_TEST_IF(!(IsMetal() || IsVulkan() || IsD3D12()));
 
     // TODO(enga): Figure out why this fails on Metal Intel.
     DAWN_SKIP_TEST_IF(IsMetal() && IsIntel());
@@ -767,8 +771,9 @@
                          depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_StencilOnly));
 
     // Check by copy that the stencil data is lazily cleared to 0.
-    EXPECT_LAZY_CLEAR(1u, EXPECT_TEXTURE_EQ(uint8_t(0), depthStencilTexture, 0, 0, 0, 0,
-                                            wgpu::TextureAspect::StencilOnly));
+    std::vector<uint8_t> expected(kSize * kSize, 0);
+    EXPECT_LAZY_CLEAR(1u, EXPECT_TEXTURE_EQ(expected.data(), depthStencilTexture, 0, 0, kSize,
+                                            kSize, 0, 0, wgpu::TextureAspect::StencilOnly));
 
     // Everything is initialized now
     EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(depthStencilTexture.Get(), 0, 1, 0,