Fix bugs in the multi-layer copies with BC formats on D3D12 and OpenGL

This patch fixes two bugs in the copy commands with BC formats and
multiple array layers on D3D12 and OpenGL and adds two end2end tests as
the regression tests.

This patch also removes "viewArrayLayer" in the struct CopyConfig used
in CompressedTextureBCFormatTest and sets the base array layer into
CopyConfig.copyOrigin3D.z instead.

BUG=dawn:453
TEST=dawn_end2end_tests

Change-Id: I1c2e6b79fb7c44fc996655ab5a908e27ba8c4729
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/24183
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/dawn_native/d3d12/CommandBufferD3D12.cpp b/src/dawn_native/d3d12/CommandBufferD3D12.cpp
index 98fca16..a9c0249 100644
--- a/src/dawn_native/d3d12/CommandBufferD3D12.cpp
+++ b/src/dawn_native/d3d12/CommandBufferD3D12.cpp
@@ -592,7 +592,8 @@
                                                         subresources);
 
                     const uint64_t bytesPerSlice =
-                        copy->source.bytesPerRow * copy->source.rowsPerImage;
+                        copy->source.bytesPerRow *
+                        (copy->source.rowsPerImage / texture->GetFormat().blockHeight);
 
                     const dawn_native::Extent3D copyOneLayerSize = {copy->copySize.width,
                                                                     copy->copySize.height, 1};
@@ -648,7 +649,8 @@
                     buffer->TrackUsageAndTransitionNow(commandContext, wgpu::BufferUsage::CopyDst);
 
                     const uint64_t bytesPerSlice =
-                        copy->destination.bytesPerRow * copy->destination.rowsPerImage;
+                        copy->destination.bytesPerRow *
+                        (copy->destination.rowsPerImage / texture->GetFormat().blockHeight);
 
                     const dawn_native::Extent3D copyOneLayerSize = {copy->copySize.width,
                                                                     copy->copySize.height, 1};
diff --git a/src/dawn_native/opengl/CommandBufferGL.cpp b/src/dawn_native/opengl/CommandBufferGL.cpp
index bba5da0..4180eb5 100644
--- a/src/dawn_native/opengl/CommandBufferGL.cpp
+++ b/src/dawn_native/opengl/CommandBufferGL.cpp
@@ -542,7 +542,7 @@
                         ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D);
                         uint64_t copyDataSize = (copySize.width / formatInfo.blockWidth) *
                                                 (copySize.height / formatInfo.blockHeight) *
-                                                formatInfo.blockByteSize;
+                                                formatInfo.blockByteSize * copySize.depth;
                         Extent3D copyExtent = ComputeTextureCopyExtent(dst, copySize);
 
                         if (texture->GetArrayLayers() > 1) {
diff --git a/src/tests/end2end/CompressedTextureFormatTests.cpp b/src/tests/end2end/CompressedTextureFormatTests.cpp
index 80c2e2a..dd0302e 100644
--- a/src/tests/end2end/CompressedTextureFormatTests.cpp
+++ b/src/tests/end2end/CompressedTextureFormatTests.cpp
@@ -27,7 +27,6 @@
     wgpu::Extent3D copyExtent3D;
     wgpu::Origin3D copyOrigin3D = {0, 0, 0};
     uint32_t viewMipmapLevel = 0;
-    uint32_t viewArrayLayer = 0;
     uint32_t bufferOffset = 0;
     uint32_t bytesPerRowAlignment = kTextureBytesPerRowAlignment;
     uint32_t rowsPerImage = 0;
@@ -69,19 +68,24 @@
                 copyWidthInBlockAtLevel *
                 utils::GetTexelBlockSizeInBytes(copyConfig.textureDescriptor.format);
         }
+        uint32_t copyBytesPerImage = bufferRowPitchInBytes * copyHeightInBlockAtLevel;
         uint32_t uploadBufferSize =
-            copyConfig.bufferOffset + bufferRowPitchInBytes * copyHeightInBlockAtLevel;
+            copyConfig.bufferOffset + copyBytesPerImage * copyConfig.copyExtent3D.depth;
 
         // Fill uploadData with the pre-prepared one-block compressed texture data.
         std::vector<uint8_t> uploadData(uploadBufferSize, 0);
         std::vector<uint8_t> oneBlockCompressedTextureData =
             GetOneBlockBCFormatTextureData(copyConfig.textureDescriptor.format);
-        for (uint32_t h = 0; h < copyHeightInBlockAtLevel; ++h) {
-            for (uint32_t w = 0; w < copyWidthInBlockAtLevel; ++w) {
-                uint32_t uploadBufferOffset = copyConfig.bufferOffset + bufferRowPitchInBytes * h +
-                                              oneBlockCompressedTextureData.size() * w;
-                std::memcpy(&uploadData[uploadBufferOffset], oneBlockCompressedTextureData.data(),
-                            oneBlockCompressedTextureData.size() * sizeof(uint8_t));
+        for (uint32_t layer = 0; layer < copyConfig.copyExtent3D.depth; ++layer) {
+            for (uint32_t h = 0; h < copyHeightInBlockAtLevel; ++h) {
+                for (uint32_t w = 0; w < copyWidthInBlockAtLevel; ++w) {
+                    uint32_t uploadBufferOffset =
+                        copyConfig.bufferOffset + copyBytesPerImage * layer +
+                        bufferRowPitchInBytes * h + oneBlockCompressedTextureData.size() * w;
+                    std::memcpy(&uploadData[uploadBufferOffset],
+                                oneBlockCompressedTextureData.data(),
+                                oneBlockCompressedTextureData.size() * sizeof(uint8_t));
+                }
             }
         }
 
@@ -92,11 +96,8 @@
             utils::CreateBufferCopyView(stagingBuffer, copyConfig.bufferOffset,
                                         copyConfig.bytesPerRowAlignment, copyConfig.rowsPerImage);
 
-        ASSERT(copyConfig.copyOrigin3D.z == 0);
-        wgpu::Origin3D copyOrigin = copyConfig.copyOrigin3D;
-        copyOrigin.z = copyConfig.viewArrayLayer;
         wgpu::TextureCopyView textureCopyView = utils::CreateTextureCopyView(
-            bcCompressedTexture, copyConfig.viewMipmapLevel, copyOrigin);
+            bcCompressedTexture, copyConfig.viewMipmapLevel, copyConfig.copyOrigin3D);
 
         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
         encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, &copyConfig.copyExtent3D);
@@ -204,9 +205,6 @@
         wgpu::Texture bcTexture = CreateTextureWithCompressedData(config);
 
         wgpu::RenderPipeline renderPipeline = CreateRenderPipelineForTest();
-        wgpu::BindGroup bindGroup = CreateBindGroupForTest(
-            renderPipeline.GetBindGroupLayout(0), bcTexture, config.textureDescriptor.format,
-            config.viewArrayLayer, config.viewMipmapLevel);
 
         wgpu::Extent3D virtualSizeAtLevel = GetVirtualSizeAtLevel(config);
 
@@ -220,11 +218,21 @@
         if (config.copyOrigin3D.y + config.copyExtent3D.height > virtualSizeAtLevel.height) {
             noPaddingExtent3D.height = virtualSizeAtLevel.height - config.copyOrigin3D.y;
         }
+        noPaddingExtent3D.depth = 1u;
 
         std::vector<RGBA8> expectedData =
             GetExpectedData(config.textureDescriptor.format, virtualSizeAtLevel);
-        VerifyCompressedTexturePixelValues(renderPipeline, bindGroup, virtualSizeAtLevel,
-                                           config.copyOrigin3D, noPaddingExtent3D, expectedData);
+
+        wgpu::Origin3D firstLayerCopyOrigin = {config.copyOrigin3D.x, config.copyOrigin3D.y, 0};
+        for (uint32_t layer = config.copyOrigin3D.z;
+             layer < config.copyOrigin3D.z + config.copyExtent3D.depth; ++layer) {
+            wgpu::BindGroup bindGroup = CreateBindGroupForTest(
+                renderPipeline.GetBindGroupLayout(0), bcTexture, config.textureDescriptor.format,
+                layer, config.viewMipmapLevel);
+            VerifyCompressedTexturePixelValues(renderPipeline, bindGroup, virtualSizeAtLevel,
+                                               firstLayerCopyOrigin, noPaddingExtent3D,
+                                               expectedData);
+        }
     }
 
     // Create a texture and initialize it with the pre-prepared compressed texture data.
@@ -240,17 +248,10 @@
                                     wgpu::Texture dstTexture,
                                     CopyConfig srcConfig,
                                     CopyConfig dstConfig) {
-        ASSERT(srcConfig.copyOrigin3D.z == 0);
-        wgpu::Origin3D srcCopyOrigin = srcConfig.copyOrigin3D;
-        srcCopyOrigin.z = srcConfig.viewArrayLayer;
-        wgpu::TextureCopyView textureCopyViewSrc =
-            utils::CreateTextureCopyView(srcTexture, srcConfig.viewMipmapLevel, srcCopyOrigin);
-
-        ASSERT(dstConfig.copyOrigin3D.z == 0);
-        wgpu::Origin3D dstCopyOrigin = dstConfig.copyOrigin3D;
-        dstCopyOrigin.z = dstConfig.viewArrayLayer;
-        wgpu::TextureCopyView textureCopyViewDst =
-            utils::CreateTextureCopyView(dstTexture, dstConfig.viewMipmapLevel, dstCopyOrigin);
+        wgpu::TextureCopyView textureCopyViewSrc = utils::CreateTextureCopyView(
+            srcTexture, srcConfig.viewMipmapLevel, srcConfig.copyOrigin3D);
+        wgpu::TextureCopyView textureCopyViewDst = utils::CreateTextureCopyView(
+            dstTexture, dstConfig.viewMipmapLevel, dstConfig.copyOrigin3D);
         encoder.CopyTextureToTexture(&textureCopyViewSrc, &textureCopyViewDst,
                                      &dstConfig.copyExtent3D);
     }
@@ -490,7 +491,7 @@
 
     constexpr uint32_t kArrayLayerCount = 3;
     config.textureDescriptor.size.depth = kArrayLayerCount;
-    config.viewArrayLayer = kArrayLayerCount - 1;
+    config.copyOrigin3D.z = kArrayLayerCount - 1;
 
     for (wgpu::TextureFormat format : kBCFormats) {
         config.textureDescriptor.format = format;
@@ -587,7 +588,7 @@
         wgpu::RenderPipeline renderPipeline = CreateRenderPipelineForTest();
         wgpu::BindGroup bindGroup =
             CreateBindGroupForTest(renderPipeline.GetBindGroupLayout(0), bcTextureDst, format,
-                                   config.viewArrayLayer, config.viewMipmapLevel);
+                                   config.copyOrigin3D.z, config.viewMipmapLevel);
 
         std::vector<RGBA8> expectedData = GetExpectedData(format, kVirtualSize);
         VerifyCompressedTexturePixelValues(renderPipeline, bindGroup, kVirtualSize,
@@ -651,7 +652,7 @@
         wgpu::RenderPipeline renderPipeline = CreateRenderPipelineForTest();
         wgpu::BindGroup bindGroup =
             CreateBindGroupForTest(renderPipeline.GetBindGroupLayout(0), bcTextureDst, format,
-                                   dstConfig.viewArrayLayer, dstConfig.viewMipmapLevel);
+                                   dstConfig.copyOrigin3D.z, dstConfig.viewMipmapLevel);
 
         std::vector<RGBA8> expectedData = GetExpectedData(format, kDstVirtualSize);
         VerifyCompressedTexturePixelValues(renderPipeline, bindGroup, kDstVirtualSize,
@@ -711,7 +712,7 @@
         wgpu::RenderPipeline renderPipeline = CreateRenderPipelineForTest();
         wgpu::BindGroup bindGroup =
             CreateBindGroupForTest(renderPipeline.GetBindGroupLayout(0), bcTextureDst, format,
-                                   dstConfig.viewArrayLayer, dstConfig.viewMipmapLevel);
+                                   dstConfig.copyOrigin3D.z, dstConfig.viewMipmapLevel);
 
         std::vector<RGBA8> expectedData = GetExpectedData(format, kDstVirtualSize);
         VerifyCompressedTexturePixelValues(renderPipeline, bindGroup, kDstVirtualSize,
@@ -792,7 +793,7 @@
             // Verify if we can use bcDstTextures as sampled textures correctly.
             wgpu::BindGroup bindGroup0 = CreateBindGroupForTest(
                 renderPipeline.GetBindGroupLayout(0), bcDstTextures[i], format,
-                dstConfigs[i].viewArrayLayer, dstConfigs[i].viewMipmapLevel);
+                dstConfigs[i].copyOrigin3D.z, dstConfigs[i].viewMipmapLevel);
 
             std::vector<RGBA8> expectedData = GetExpectedData(format, dstVirtualSizes[i]);
             VerifyCompressedTexturePixelValues(renderPipeline, bindGroup0, dstVirtualSizes[i],
@@ -1003,6 +1004,62 @@
     }
 }
 
+// Test copying a whole 2D array texture with array layer count > 1 in one copy command works with
+// BC formats.
+TEST_P(CompressedTextureBCFormatTest, CopyWhole2DArrayTexture) {
+    // TODO(jiawei.shao@intel.com): find out why this test is flaky on Windows Intel Vulkan
+    // bots.
+    DAWN_SKIP_TEST_IF(IsIntel() && IsVulkan() && IsWindows());
+
+    // TODO(jiawei.shao@intel.com): find out why this test fails on Windows Intel OpenGL drivers.
+    DAWN_SKIP_TEST_IF(IsIntel() && IsOpenGL() && IsWindows());
+
+    DAWN_SKIP_TEST_IF(!IsBCFormatSupported());
+
+    constexpr uint32_t kArrayLayerCount = 3;
+
+    CopyConfig config;
+    config.textureDescriptor.usage = kDefaultBCFormatTextureUsage;
+    config.textureDescriptor.size = {8, 8, kArrayLayerCount};
+
+    config.copyExtent3D = config.textureDescriptor.size;
+    config.copyExtent3D.depth = kArrayLayerCount;
+
+    for (wgpu::TextureFormat format : kBCFormats) {
+        config.textureDescriptor.format = format;
+        TestCopyRegionIntoBCFormatTextures(config);
+    }
+}
+
+// Test copying a multiple 2D texture array layers in one copy command works with BC formats.
+TEST_P(CompressedTextureBCFormatTest, CopyMultiple2DArrayLayers) {
+    // TODO(jiawei.shao@intel.com): find out why this test is flaky on Windows Intel Vulkan
+    // bots.
+    DAWN_SKIP_TEST_IF(IsIntel() && IsVulkan() && IsWindows());
+
+    // TODO(jiawei.shao@intel.com): find out why this test fails on Windows Intel OpenGL drivers.
+    DAWN_SKIP_TEST_IF(IsIntel() && IsOpenGL() && IsWindows());
+
+    DAWN_SKIP_TEST_IF(!IsBCFormatSupported());
+
+    constexpr uint32_t kArrayLayerCount = 3;
+
+    CopyConfig config;
+    config.textureDescriptor.usage = kDefaultBCFormatTextureUsage;
+    config.textureDescriptor.size = {8, 8, kArrayLayerCount};
+
+    constexpr uint32_t kCopyBaseArrayLayer = 1;
+    constexpr uint32_t kCopyLayerCount = 2;
+    config.copyOrigin3D = {0, 0, kCopyBaseArrayLayer};
+    config.copyExtent3D = config.textureDescriptor.size;
+    config.copyExtent3D.depth = kCopyLayerCount;
+
+    for (wgpu::TextureFormat format : kBCFormats) {
+        config.textureDescriptor.format = format;
+        TestCopyRegionIntoBCFormatTextures(config);
+    }
+}
+
 // TODO(jiawei.shao@intel.com): support BC formats on OpenGL backend
 DAWN_INSTANTIATE_TEST(CompressedTextureBCFormatTest,
                       D3D12Backend(),