Implement 3D texture copies on Vulkan

Bug: dawn:781
Change-Id: I5a1b3cc906b2bcb89c0ba3b202bacd16e012efe8
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/50241
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Yunchao He <yunchao.he@intel.com>
diff --git a/src/dawn_native/vulkan/CommandBufferVk.cpp b/src/dawn_native/vulkan/CommandBufferVk.cpp
index 48427ba..bbdcf6c 100644
--- a/src/dawn_native/vulkan/CommandBufferVk.cpp
+++ b/src/dawn_native/vulkan/CommandBufferVk.cpp
@@ -69,33 +69,56 @@
             const Texture* dstTexture = ToBackend(dstCopy.texture.Get());
 
             VkImageCopy region;
-
-            // TODO(jiawei.shao@intel.com): support 1D and 3D textures
-            ASSERT(srcTexture->GetDimension() == wgpu::TextureDimension::e2D &&
-                   dstTexture->GetDimension() == wgpu::TextureDimension::e2D);
             region.srcSubresource.aspectMask = VulkanAspectMask(aspect);
             region.srcSubresource.mipLevel = srcCopy.mipLevel;
-            region.srcSubresource.baseArrayLayer = srcCopy.origin.z;
-            region.srcSubresource.layerCount = copySize.depthOrArrayLayers;
+            region.dstSubresource.aspectMask = VulkanAspectMask(aspect);
+            region.dstSubresource.mipLevel = dstCopy.mipLevel;
+
+            bool has3DTextureInCopy = false;
 
             region.srcOffset.x = srcCopy.origin.x;
             region.srcOffset.y = srcCopy.origin.y;
-            region.srcOffset.z = 0;
-
-            region.dstSubresource.aspectMask = VulkanAspectMask(aspect);
-            region.dstSubresource.mipLevel = dstCopy.mipLevel;
-            region.dstSubresource.baseArrayLayer = dstCopy.origin.z;
-            region.dstSubresource.layerCount = copySize.depthOrArrayLayers;
+            switch (srcTexture->GetDimension()) {
+                case wgpu::TextureDimension::e2D:
+                    region.srcSubresource.baseArrayLayer = srcCopy.origin.z;
+                    region.srcSubresource.layerCount = copySize.depthOrArrayLayers;
+                    region.srcOffset.z = 0;
+                    break;
+                case wgpu::TextureDimension::e3D:
+                    has3DTextureInCopy = true;
+                    region.srcSubresource.baseArrayLayer = 0;
+                    region.srcSubresource.layerCount = 1;
+                    region.srcOffset.z = srcCopy.origin.z;
+                    break;
+                case wgpu::TextureDimension::e1D:
+                    // TODO(jiawei.shao@intel.com): support 1D textures
+                    UNREACHABLE();
+            }
 
             region.dstOffset.x = dstCopy.origin.x;
             region.dstOffset.y = dstCopy.origin.y;
-            region.dstOffset.z = 0;
+            switch (dstTexture->GetDimension()) {
+                case wgpu::TextureDimension::e2D:
+                    region.dstSubresource.baseArrayLayer = dstCopy.origin.z;
+                    region.dstSubresource.layerCount = copySize.depthOrArrayLayers;
+                    region.dstOffset.z = 0;
+                    break;
+                case wgpu::TextureDimension::e3D:
+                    has3DTextureInCopy = true;
+                    region.dstSubresource.baseArrayLayer = 0;
+                    region.dstSubresource.layerCount = 1;
+                    region.dstOffset.z = dstCopy.origin.z;
+                    break;
+                case wgpu::TextureDimension::e1D:
+                    // TODO(jiawei.shao@intel.com): support 1D textures
+                    UNREACHABLE();
+            }
 
             ASSERT(HasSameTextureCopyExtent(srcCopy, dstCopy, copySize));
             Extent3D imageExtent = ComputeTextureCopyExtent(dstCopy, copySize);
             region.extent.width = imageExtent.width;
             region.extent.height = imageExtent.height;
-            region.extent.depth = 1;
+            region.extent.depth = has3DTextureInCopy ? copySize.depthOrArrayLayers : 1;
 
             return region;
         }
@@ -526,7 +549,7 @@
                         ComputeBufferImageCopyRegion(src, dst, copy->copySize);
                     VkImageSubresourceLayers subresource = region.imageSubresource;
 
-                    ASSERT(dst.texture->GetDimension() == wgpu::TextureDimension::e2D);
+                    ASSERT(dst.texture->GetDimension() != wgpu::TextureDimension::e1D);
                     SubresourceRange range =
                         GetSubresourcesAffectedByCopy(copy->destination, copy->copySize);
 
@@ -564,7 +587,7 @@
                     VkBufferImageCopy region =
                         ComputeBufferImageCopyRegion(dst, src, copy->copySize);
 
-                    ASSERT(src.texture->GetDimension() == wgpu::TextureDimension::e2D);
+                    ASSERT(src.texture->GetDimension() != wgpu::TextureDimension::e1D);
                     SubresourceRange range =
                         GetSubresourcesAffectedByCopy(copy->source, copy->copySize);
 
diff --git a/src/dawn_native/vulkan/DeviceVk.cpp b/src/dawn_native/vulkan/DeviceVk.cpp
index 530b5ca..df3324f 100644
--- a/src/dawn_native/vulkan/DeviceVk.cpp
+++ b/src/dawn_native/vulkan/DeviceVk.cpp
@@ -650,7 +650,7 @@
         VkBufferImageCopy region = ComputeBufferImageCopyRegion(src, *dst, copySizePixels);
         VkImageSubresourceLayers subresource = region.imageSubresource;
 
-        ASSERT(dst->texture->GetDimension() == wgpu::TextureDimension::e2D);
+        ASSERT(dst->texture->GetDimension() != wgpu::TextureDimension::e1D);
         SubresourceRange range = GetSubresourcesAffectedByCopy(*dst, copySizePixels);
 
         if (IsCompleteSubresourceCopiedTo(dst->texture.Get(), copySizePixels,
diff --git a/src/dawn_native/vulkan/TextureVk.cpp b/src/dawn_native/vulkan/TextureVk.cpp
index 90f85f3..c69fe27 100644
--- a/src/dawn_native/vulkan/TextureVk.cpp
+++ b/src/dawn_native/vulkan/TextureVk.cpp
@@ -986,9 +986,6 @@
         ASSERT(imageBarriers != nullptr);
         const Format& format = GetFormat();
 
-        // This transitions assume it is a 2D texture
-        ASSERT(GetDimension() == wgpu::TextureDimension::e2D);
-
         wgpu::TextureUsage allLastUsages = wgpu::TextureUsage::None;
         mSubresourceLastUsages.Update(
             range, [&](const SubresourceRange& range, wgpu::TextureUsage* lastUsage) {
diff --git a/src/dawn_native/vulkan/UtilsVulkan.cpp b/src/dawn_native/vulkan/UtilsVulkan.cpp
index 6167201..7375377 100644
--- a/src/dawn_native/vulkan/UtilsVulkan.cpp
+++ b/src/dawn_native/vulkan/UtilsVulkan.cpp
@@ -139,8 +139,22 @@
                 break;
             }
 
+            case wgpu::TextureDimension::e3D: {
+                region.imageOffset.x = textureCopy.origin.x;
+                region.imageOffset.y = textureCopy.origin.y;
+                region.imageOffset.z = textureCopy.origin.z;
+
+                region.imageSubresource.baseArrayLayer = 0;
+                region.imageSubresource.layerCount = 1;
+
+                Extent3D imageExtent = ComputeTextureCopyExtent(textureCopy, copySize);
+                region.imageExtent.width = imageExtent.width;
+                region.imageExtent.height = imageExtent.height;
+                region.imageExtent.depth = imageExtent.depthOrArrayLayers;
+                break;
+            }
+
             case wgpu::TextureDimension::e1D:
-            case wgpu::TextureDimension::e3D:
                 UNREACHABLE();
         }
 
diff --git a/src/tests/end2end/CopyTests.cpp b/src/tests/end2end/CopyTests.cpp
index 3426a40..951b923 100644
--- a/src/tests/end2end/CopyTests.cpp
+++ b/src/tests/end2end/CopyTests.cpp
@@ -968,9 +968,9 @@
 
 // Test that copying whole 3D texture in one texture-to-buffer-copy works.
 TEST_P(CopyTests_T2B, Texture3DFull) {
-    // TODO(yunchao.he@intel.com): implement 3D texture copy on Vulkan, Metal, OpenGL and OpenGLES
+    // TODO(yunchao.he@intel.com): implement 3D texture copy on Metal, OpenGL and OpenGLES
     // backend.
-    DAWN_SKIP_TEST_IF(IsVulkan() || IsMetal() || IsOpenGL() || IsOpenGLES());
+    DAWN_SKIP_TEST_IF(IsMetal() || IsOpenGL() || IsOpenGLES());
 
     constexpr uint32_t kWidth = 256;
     constexpr uint32_t kHeight = 128;
@@ -985,9 +985,9 @@
 
 // Test that copying a range of texture 3D depths in one texture-to-buffer-copy works.
 TEST_P(CopyTests_T2B, Texture3DSubRegion) {
-    // TODO(yunchao.he@intel.com): implement 3D texture copy on Vulkan, Metal, OpenGL and OpenGLES
+    // TODO(yunchao.he@intel.com): implement 3D texture copy on Metal, OpenGL and OpenGLES
     // backend.
-    DAWN_SKIP_TEST_IF(IsVulkan() || IsMetal() || IsOpenGL() || IsOpenGLES());
+    DAWN_SKIP_TEST_IF(IsMetal() || IsOpenGL() || IsOpenGLES());
 
     constexpr uint32_t kWidth = 256;
     constexpr uint32_t kHeight = 128;
@@ -1445,9 +1445,9 @@
 
 // Test that copying whole texture 3D in one buffer-to-texture-copy works.
 TEST_P(CopyTests_B2T, Texture3DFull) {
-    // TODO(yunchao.he@intel.com): implement 3D texture copy on Vulkan, Metal, OpenGL and OpenGLES
+    // TODO(yunchao.he@intel.com): implement 3D texture copy on Metal, OpenGL and OpenGLES
     // backend.
-    DAWN_SKIP_TEST_IF(IsVulkan() || IsMetal() || IsOpenGL() || IsOpenGLES());
+    DAWN_SKIP_TEST_IF(IsMetal() || IsOpenGL() || IsOpenGLES());
 
     constexpr uint32_t kWidth = 256;
     constexpr uint32_t kHeight = 128;
@@ -1462,9 +1462,9 @@
 
 // Test that copying a range of texture 3D Depths in one texture-to-buffer-copy works.
 TEST_P(CopyTests_B2T, Texture3DSubRegion) {
-    // TODO(yunchao.he@intel.com): implement 3D texture copy on Vulkan, Metal, OpenGL and OpenGLES
+    // TODO(yunchao.he@intel.com): implement 3D texture copy on Metal, OpenGL and OpenGLES
     // backend.
-    DAWN_SKIP_TEST_IF(IsVulkan() || IsMetal() || IsOpenGL() || IsOpenGLES());
+    DAWN_SKIP_TEST_IF(IsMetal() || IsOpenGL() || IsOpenGLES());
 
     constexpr uint32_t kWidth = 256;
     constexpr uint32_t kHeight = 128;
@@ -1799,9 +1799,9 @@
 
 // Test that copying whole 3D texture in one texture-to-texture-copy works.
 TEST_P(CopyTests_T2T, Texture3DFull) {
-    // TODO(yunchao.he@intel.com): implement 3D texture copy on Vulkan, Metal, OpenGL and OpenGLES
+    // TODO(yunchao.he@intel.com): implement 3D texture copy, Metal, OpenGL and OpenGLES
     // backend.
-    DAWN_SKIP_TEST_IF(IsVulkan() || IsMetal() || IsOpenGL() || IsOpenGLES());
+    DAWN_SKIP_TEST_IF(IsMetal() || IsOpenGL() || IsOpenGLES());
 
     constexpr uint32_t kWidth = 256;
     constexpr uint32_t kHeight = 128;