Implement depth-only/stencil-only copies on Vulkan and Metal

Bug: dawn:439
Change-Id: I07ab014f4f13b73c09b2eecc48cd38b06d88166a
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/24684
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Jiawei Shao <jiawei.shao@intel.com>
Commit-Queue: Austin Eng <enga@chromium.org>
diff --git a/src/dawn_native/vulkan/CommandBufferVk.cpp b/src/dawn_native/vulkan/CommandBufferVk.cpp
index 8315b1b..3b7d016 100644
--- a/src/dawn_native/vulkan/CommandBufferVk.cpp
+++ b/src/dawn_native/vulkan/CommandBufferVk.cpp
@@ -68,7 +68,7 @@
             // TODO(jiawei.shao@intel.com): support 1D and 3D textures
             ASSERT(srcTexture->GetDimension() == wgpu::TextureDimension::e2D &&
                    dstTexture->GetDimension() == wgpu::TextureDimension::e2D);
-            region.srcSubresource.aspectMask = srcTexture->GetVkAspectMask();
+            region.srcSubresource.aspectMask = srcTexture->GetVkAspectMask(srcCopy.aspect);
             region.srcSubresource.mipLevel = srcCopy.mipLevel;
             region.srcSubresource.baseArrayLayer = srcCopy.origin.z;
             region.srcSubresource.layerCount = copySize.depth;
@@ -77,7 +77,7 @@
             region.srcOffset.y = srcCopy.origin.y;
             region.srcOffset.z = 0;
 
-            region.dstSubresource.aspectMask = dstTexture->GetVkAspectMask();
+            region.dstSubresource.aspectMask = dstTexture->GetVkAspectMask(dstCopy.aspect);
             region.dstSubresource.mipLevel = dstCopy.mipLevel;
             region.dstSubresource.baseArrayLayer = dstCopy.origin.z;
             region.dstSubresource.layerCount = copySize.depth;
diff --git a/src/dawn_native/vulkan/QueueVk.cpp b/src/dawn_native/vulkan/QueueVk.cpp
index b19b236..c7c4ad7 100644
--- a/src/dawn_native/vulkan/QueueVk.cpp
+++ b/src/dawn_native/vulkan/QueueVk.cpp
@@ -36,10 +36,10 @@
             uint32_t optimallyAlignedBytesPerRow,
             uint32_t alignedRowsPerImage,
             const TextureDataLayout* dataLayout,
-            const Format& textureFormat,
+            const TexelBlockInfo& blockInfo,
             const Extent3D* writeSize) {
             uint32_t newDataSize = ComputeRequiredBytesInCopy(
-                textureFormat, *writeSize, optimallyAlignedBytesPerRow, alignedRowsPerImage);
+                blockInfo, *writeSize, optimallyAlignedBytesPerRow, alignedRowsPerImage);
 
             uint64_t optimalOffsetAlignment =
                 ToBackend(device)
@@ -56,10 +56,10 @@
             const uint8_t* srcPointer = static_cast<const uint8_t*>(data);
             srcPointer += dataLayout->offset;
 
-            uint32_t alignedRowsPerImageInBlock = alignedRowsPerImage / textureFormat.blockHeight;
-            uint32_t dataRowsPerImageInBlock = dataLayout->rowsPerImage / textureFormat.blockHeight;
+            uint32_t alignedRowsPerImageInBlock = alignedRowsPerImage / blockInfo.blockHeight;
+            uint32_t dataRowsPerImageInBlock = dataLayout->rowsPerImage / blockInfo.blockHeight;
             if (dataRowsPerImageInBlock == 0) {
-                dataRowsPerImageInBlock = writeSize->height / textureFormat.blockHeight;
+                dataRowsPerImageInBlock = writeSize->height / blockInfo.blockHeight;
             }
 
             uint64_t additionalOffset =
@@ -110,12 +110,14 @@
                                        size_t dataSize,
                                        const TextureDataLayout* dataLayout,
                                        const Extent3D* writeSize) {
-        uint32_t blockSize = destination->texture->GetFormat().blockByteSize;
-        uint32_t blockWidth = destination->texture->GetFormat().blockWidth;
+        const TexelBlockInfo& blockInfo =
+            destination->texture->GetFormat().GetTexelBlockInfo(destination->aspect);
+
         // We are only copying the part of the data that will appear in the texture.
         // Note that validating texture copy range ensures that writeSize->width and
         // writeSize->height are multiples of blockWidth and blockHeight respectively.
-        uint32_t alignedBytesPerRow = (writeSize->width) / blockWidth * blockSize;
+        uint32_t alignedBytesPerRow =
+            (writeSize->width) / blockInfo.blockWidth * blockInfo.blockByteSize;
         uint32_t alignedRowsPerImage = writeSize->height;
 
         uint32_t optimalBytesPerRowAlignment =
@@ -126,11 +128,10 @@
             Align(alignedBytesPerRow, optimalBytesPerRowAlignment);
 
         UploadHandle uploadHandle;
-        DAWN_TRY_ASSIGN(
-            uploadHandle,
-            UploadTextureDataAligningBytesPerRow(
-                GetDevice(), data, dataSize, alignedBytesPerRow, optimallyAlignedBytesPerRow,
-                alignedRowsPerImage, dataLayout, destination->texture->GetFormat(), writeSize));
+        DAWN_TRY_ASSIGN(uploadHandle, UploadTextureDataAligningBytesPerRow(
+                                          GetDevice(), data, dataSize, alignedBytesPerRow,
+                                          optimallyAlignedBytesPerRow, alignedRowsPerImage,
+                                          dataLayout, blockInfo, writeSize));
 
         TextureDataLayout passDataLayout = *dataLayout;
         passDataLayout.offset = uploadHandle.startOffset;
@@ -141,9 +142,10 @@
         textureCopy.texture = destination->texture;
         textureCopy.mipLevel = destination->mipLevel;
         textureCopy.origin = destination->origin;
+        textureCopy.aspect = destination->aspect;
 
         return ToBackend(GetDevice())
             ->CopyFromStagingToTexture(uploadHandle.stagingBuffer, passDataLayout, &textureCopy,
                                        *writeSize);
     }
-}}  // namespace dawn_native::vulkan
\ No newline at end of file
+}}  // namespace dawn_native::vulkan
diff --git a/src/dawn_native/vulkan/TextureVk.cpp b/src/dawn_native/vulkan/TextureVk.cpp
index 3c7ffe8..6d1291bf 100644
--- a/src/dawn_native/vulkan/TextureVk.cpp
+++ b/src/dawn_native/vulkan/TextureVk.cpp
@@ -669,8 +669,21 @@
         return mHandle;
     }
 
-    VkImageAspectFlags Texture::GetVkAspectMask() const {
-        return VulkanAspectMask(GetFormat().aspects);
+    VkImageAspectFlags Texture::GetVkAspectMask(wgpu::TextureAspect aspect) const {
+        // TODO(enga): These masks could be precomputed.
+        switch (aspect) {
+            case wgpu::TextureAspect::All:
+                return VulkanAspectMask(GetFormat().aspects);
+            case wgpu::TextureAspect::DepthOnly:
+                ASSERT(GetFormat().aspects & Aspect::Depth);
+                return VulkanAspectMask(Aspect::Depth);
+            case wgpu::TextureAspect::StencilOnly:
+                ASSERT(GetFormat().aspects & Aspect::Stencil);
+                return VulkanAspectMask(Aspect::Stencil);
+            default:
+                UNREACHABLE();
+                return 0;
+        }
     }
 
     void Texture::TweakTransitionForExternalUsage(CommandRecordingContext* recordingContext,
@@ -872,7 +885,7 @@
         TransitionUsageNow(recordingContext, wgpu::TextureUsage::CopyDst, range);
         if (GetFormat().isRenderable) {
             VkImageSubresourceRange imageRange = {};
-            imageRange.aspectMask = GetVkAspectMask();
+            imageRange.aspectMask = GetVkAspectMask(wgpu::TextureAspect::All);
             imageRange.levelCount = 1;
             imageRange.layerCount = 1;
 
@@ -943,10 +956,12 @@
                         continue;
                     }
 
+                    ASSERT(GetFormat().aspects == Aspect::Color);
                     dawn_native::TextureCopy textureCopy;
                     textureCopy.texture = this;
                     textureCopy.origin = {0, 0, layer};
                     textureCopy.mipLevel = level;
+                    textureCopy.aspect = wgpu::TextureAspect::All;
 
                     VkBufferImageCopy region =
                         ComputeBufferImageCopyRegion(bufferCopy, textureCopy, copySize);
diff --git a/src/dawn_native/vulkan/TextureVk.h b/src/dawn_native/vulkan/TextureVk.h
index 8a1564a..6748ebd 100644
--- a/src/dawn_native/vulkan/TextureVk.h
+++ b/src/dawn_native/vulkan/TextureVk.h
@@ -59,7 +59,7 @@
                                                VkImage nativeImage);
 
         VkImage GetHandle() const;
-        VkImageAspectFlags GetVkAspectMask() const;
+        VkImageAspectFlags GetVkAspectMask(wgpu::TextureAspect aspect) const;
 
         // Transitions the texture to be used as `usage`, recording any necessary barrier in
         // `commands`.
diff --git a/src/dawn_native/vulkan/UtilsVulkan.cpp b/src/dawn_native/vulkan/UtilsVulkan.cpp
index 15011ce..b7116da 100644
--- a/src/dawn_native/vulkan/UtilsVulkan.cpp
+++ b/src/dawn_native/vulkan/UtilsVulkan.cpp
@@ -84,12 +84,14 @@
 
         region.bufferOffset = dataLayout.offset;
         // In Vulkan the row length is in texels while it is in bytes for Dawn
-        const Format& format = texture->GetFormat();
-        ASSERT(dataLayout.bytesPerRow % format.blockByteSize == 0);
-        region.bufferRowLength = dataLayout.bytesPerRow / format.blockByteSize * format.blockWidth;
+        const TexelBlockInfo& blockInfo =
+            texture->GetFormat().GetTexelBlockInfo(textureCopy.aspect);
+        ASSERT(dataLayout.bytesPerRow % blockInfo.blockByteSize == 0);
+        region.bufferRowLength =
+            dataLayout.bytesPerRow / blockInfo.blockByteSize * blockInfo.blockWidth;
         region.bufferImageHeight = dataLayout.rowsPerImage;
 
-        region.imageSubresource.aspectMask = texture->GetVkAspectMask();
+        region.imageSubresource.aspectMask = texture->GetVkAspectMask(textureCopy.aspect);
         region.imageSubresource.mipLevel = textureCopy.mipLevel;
 
         switch (textureCopy.texture->GetDimension()) {
@@ -115,4 +117,4 @@
 
         return region;
     }
-}}  // namespace dawn_native::vulkan
\ No newline at end of file
+}}  // namespace dawn_native::vulkan