[Compat] Fix cube texture for compressed texture

Bug: dawn:2131
Change-Id: Iaa0275ca00b29b2af31ad768fce1cc25f25fad7b
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/186320
Reviewed-by: Austin Eng <enga@chromium.org>
Reviewed-by: Stephen White <senorblanco@chromium.org>
Commit-Queue: Shrek Shao <shrekshao@google.com>
diff --git a/src/dawn/native/opengl/CommandBufferGL.cpp b/src/dawn/native/opengl/CommandBufferGL.cpp
index a004327..63b5fb8 100644
--- a/src/dawn/native/opengl/CommandBufferGL.cpp
+++ b/src/dawn/native/opengl/CommandBufferGL.cpp
@@ -1440,6 +1440,16 @@
             if (target == GL_TEXTURE_2D) {
                 gl.CompressedTexSubImage2D(target, destination.mipLevel, x, y, width, height,
                                            format.internalFormat, imageSize, data);
+            } else if (target == GL_TEXTURE_CUBE_MAP) {
+                DAWN_ASSERT(texture->GetArrayLayers() == 6);
+                const uint8_t* pointer = static_cast<const uint8_t*>(data);
+                uint32_t baseLayer = destination.origin.z;
+                for (uint32_t l = 0; l < copySize.depthOrArrayLayers; ++l) {
+                    GLenum cubeMapTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + baseLayer + l;
+                    gl.CompressedTexSubImage2D(cubeMapTarget, destination.mipLevel, x, y, width,
+                                               height, format.internalFormat, imageSize, pointer);
+                    pointer += dataLayout.rowsPerImage * dataLayout.bytesPerRow;
+                }
             } else {
                 gl.PixelStorei(GL_UNPACK_IMAGE_HEIGHT, dataLayout.rowsPerImage * blockInfo.height);
                 gl.CompressedTexSubImage3D(target, destination.mipLevel, x, y, z, width, height,
@@ -1463,7 +1473,25 @@
                                                format.internalFormat, rowSize, d);
                     d += dataLayout.bytesPerRow;
                 }
+            } else if (target == GL_TEXTURE_CUBE_MAP) {
+                DAWN_ASSERT(texture->GetArrayLayers() == 6);
+                const uint8_t* pointer = static_cast<const uint8_t*>(data);
+                uint32_t baseLayer = destination.origin.z;
+                for (uint32_t l = 0; l < copySize.depthOrArrayLayers; ++l) {
+                    const uint8_t* d =
+                        pointer + l * dataLayout.rowsPerImage * dataLayout.bytesPerRow;
+                    GLenum cubeMapTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + baseLayer + l;
+                    for (y = destination.origin.y; y < destination.origin.y + copySize.height;
+                         y += blockInfo.height) {
+                        uint32_t height = std::min(blockInfo.height, virtSize.height - y);
+                        gl.CompressedTexSubImage2D(cubeMapTarget, destination.mipLevel, x, y, width,
+                                                   height, format.internalFormat, rowSize, d);
+                        d += dataLayout.bytesPerRow;
+                    }
+                }
             } else {
+                DAWN_ASSERT(target == GL_TEXTURE_3D || target == GL_TEXTURE_2D_ARRAY ||
+                            target == GL_TEXTURE_CUBE_MAP_ARRAY);
                 const uint8_t* slice = static_cast<const uint8_t*>(data);
 
                 for (; z < destination.origin.z + copySize.depthOrArrayLayers; ++z) {
diff --git a/src/dawn/tests/end2end/CompressedTextureFormatTests.cpp b/src/dawn/tests/end2end/CompressedTextureFormatTests.cpp
index f286b69..699aac7 100644
--- a/src/dawn/tests/end2end/CompressedTextureFormatTests.cpp
+++ b/src/dawn/tests/end2end/CompressedTextureFormatTests.cpp
@@ -171,8 +171,8 @@
         return utils::MakeBindGroup(device, bindGroupLayout, {{0, sampler}, {1, textureView}});
     }
 
-    // Create a render pipeline for sampling from a texture and rendering into the render target.
-    wgpu::RenderPipeline CreateRenderPipelineForTest() {
+    // Create a render pipeline for sampling from a texture 2d and rendering into the render target.
+    wgpu::RenderPipeline CreateRenderPipelineForTestTex2D() {
         DAWN_ASSERT(IsFormatSupported());
 
         utils::ComboRenderPipelineDescriptor renderPipelineDescriptor;
@@ -209,6 +209,57 @@
         return device.CreateRenderPipeline(&renderPipelineDescriptor);
     }
 
+    // Create a render pipeline for sampling from a texture cube and rendering into the render
+    // target. Used for compatibility mode as cube texture cannot be bound as texture_2d_array.
+    wgpu::RenderPipeline CreateRenderPipelineForTestCube() {
+        utils::ComboRenderPipelineDescriptor renderPipelineDescriptor;
+        wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
+            struct VertexOut {
+                @location(0) texCoord : vec2 <f32>,
+                @builtin(position) position : vec4f,
+            }
+
+            @vertex
+            fn main(@builtin(vertex_index) VertexIndex : u32) -> VertexOut {
+                var pos = array(
+                    vec2f(-3.0,  1.0),
+                    vec2f( 3.0,  1.0),
+                    vec2f( 0.0, -2.0)
+                );
+                var output : VertexOut;
+                output.position = vec4f(pos[VertexIndex], 0.0, 1.0);
+                output.texCoord = vec2f(output.position.x / 2.0, -output.position.y / 2.0) + vec2f(0.5, 0.5);
+                return output;
+            })");
+        wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
+            @group(0) @binding(0) var sampler0 : sampler;
+            @group(0) @binding(1) var texture0 : texture_cube<f32>;
+            @group(0) @binding(2) var<uniform> layer : u32;
+
+            @fragment
+            fn main(@location(0) texCoord : vec2f) -> @location(0) vec4f {
+                var st: vec2f = texCoord;
+                st.y = 1. - st.y;
+                st = st * 2. - 1.;
+                var coords: vec3f;
+                switch(layer) {
+                    case 0u: { coords = vec3f(1., st.y, -st.x); } // Positive X
+                    case 1u: { coords = vec3f(-1., st.y, st.x); } // Negative X
+                    case 2u: { coords = vec3f(st.x, 1., -st.y); } // Positive Y
+                    case 3u: { coords = vec3f(st.x, -1., st.y); } // Negative Y
+                    case 4u: { coords = vec3f(st.x, st.y, 1.); }  // Positive Z
+                    case 5u: { coords = vec3f(-st.x, st.y, -1.);} // Negative Z
+                    default: { return vec4f(0.); } // Unreachable
+                }
+                return textureSample(texture0, sampler0, coords);
+            })");
+        renderPipelineDescriptor.vertex.module = vsModule;
+        renderPipelineDescriptor.cFragment.module = fsModule;
+        renderPipelineDescriptor.cTargets[0].format = utils::BasicRenderPass::kDefaultColorFormat;
+
+        return device.CreateRenderPipeline(&renderPipelineDescriptor);
+    }
+
     // Run the given render pipeline and bind group and verify the pixels in the render target.
     void VerifyCompressedTexturePixelValues(wgpu::RenderPipeline renderPipeline,
                                             wgpu::BindGroup bindGroup,
@@ -253,7 +304,7 @@
     }
 
     void VerifyTexture(const CopyConfig& config, wgpu::Texture texture) {
-        wgpu::RenderPipeline renderPipeline = CreateRenderPipelineForTest();
+        wgpu::RenderPipeline renderPipeline = CreateRenderPipelineForTestTex2D();
 
         wgpu::Extent3D virtualSizeAtLevel = GetVirtualSizeAtLevel(config);
 
@@ -695,6 +746,81 @@
     TestCopyRegionIntoFormatTextures(config);
 }
 
+// Test copying into the whole cube texture with 2x2 blocks and sampling from it.
+// Made for compatibility mode.
+TEST_P(CompressedTextureFormatTest, Cube) {
+    DAWN_TEST_UNSUPPORTED_IF(!IsFormatSupported());
+    // TODO(crbug.com/dawn/2131): diagnose this failure on Win Angle D3D11
+    DAWN_SUPPRESS_TEST_IF(IsANGLED3D11());
+
+    const wgpu::TextureFormat format = GetParam().mTextureFormat;
+
+    constexpr uint32_t kLayers = 6;
+    CopyConfig config = GetDefaultSmallConfig(kLayers);
+    config.copyExtent3D = config.textureDescriptor.size;
+    config.bytesPerRowAlignment = Align(
+        config.copyExtent3D.width / BlockWidthInTexels() * utils::GetTexelBlockSizeInBytes(format),
+        kTextureBytesPerRowAlignment);
+    config.rowsPerImage = kLayers;
+    wgpu::TextureBindingViewDimensionDescriptor textureBindingViewDimensionDesc;
+    if (IsCompatibilityMode()) {
+        textureBindingViewDimensionDesc.textureBindingViewDimension =
+            wgpu::TextureViewDimension::Cube;
+        config.textureDescriptor.nextInChain = &textureBindingViewDimensionDesc;
+
+        wgpu::Texture texture = CreateTextureWithCompressedData(config);
+        wgpu::RenderPipeline renderPipeline = CreateRenderPipelineForTestCube();
+
+        wgpu::Extent3D virtualSizeAtLevel = GetVirtualSizeAtLevel(config);
+
+        // The copy region may exceed the subresource size because of the required paddings, so we
+        // should limit the size of the expectedData to make it match the real size of the render
+        // target.
+        wgpu::Extent3D noPaddingExtent3D = config.copyExtent3D;
+        if (config.copyOrigin3D.x + config.copyExtent3D.width > virtualSizeAtLevel.width) {
+            noPaddingExtent3D.width = virtualSizeAtLevel.width - config.copyOrigin3D.x;
+        }
+        if (config.copyOrigin3D.y + config.copyExtent3D.height > virtualSizeAtLevel.height) {
+            noPaddingExtent3D.height = virtualSizeAtLevel.height - config.copyOrigin3D.y;
+        }
+        noPaddingExtent3D.depthOrArrayLayers = 1u;
+
+        std::vector<utils::RGBA8> expectedData = GetExpectedData(noPaddingExtent3D);
+
+        wgpu::Origin3D firstLayerCopyOrigin = {config.copyOrigin3D.x, config.copyOrigin3D.y, 0};
+
+        wgpu::SamplerDescriptor samplerDesc = {};
+        wgpu::Sampler sampler = device.CreateSampler(&samplerDesc);
+
+        wgpu::TextureViewDescriptor textureViewDescriptor = {};
+        textureViewDescriptor.format = GetParam().mTextureFormat;
+        textureViewDescriptor.dimension = wgpu::TextureViewDimension::Cube;
+        textureViewDescriptor.baseMipLevel = config.viewMipmapLevel;
+        textureViewDescriptor.mipLevelCount = 1;
+        wgpu::TextureView textureView = texture.CreateView(&textureViewDescriptor);
+
+        for (uint32_t layer = config.copyOrigin3D.z;
+             layer < config.copyOrigin3D.z + config.copyExtent3D.depthOrArrayLayers; ++layer) {
+            wgpu::Buffer uniformBuffer = utils::CreateBufferFromData(
+                device, &layer, sizeof(uint32_t), wgpu::BufferUsage::Uniform);
+
+            wgpu::BindGroup bindGroup =
+                utils::MakeBindGroup(device, renderPipeline.GetBindGroupLayout(0),
+                                     {
+                                         {0, sampler},
+                                         {1, textureView},
+                                         {2, uniformBuffer},
+                                     });
+
+            VerifyCompressedTexturePixelValues(renderPipeline, bindGroup, virtualSizeAtLevel,
+                                               firstLayerCopyOrigin, noPaddingExtent3D,
+                                               expectedData);
+        }
+    } else {
+        TestCopyRegionIntoFormatTextures(config);
+    }
+}
+
 // Test copying into a sub-region of a texture works correctly.
 TEST_P(CompressedTextureFormatTest, CopyIntoSubRegion) {
     // TODO(crbug.com/dawn/976): Failing on Linux Intel OpenGL drivers.
@@ -769,7 +895,7 @@
     wgpu::Texture textureDst = CreateTextureFromTexture(textureSrc, config, config);
 
     // Verify if we can use texture as sampled textures correctly.
-    wgpu::RenderPipeline renderPipeline = CreateRenderPipelineForTest();
+    wgpu::RenderPipeline renderPipeline = CreateRenderPipelineForTestTex2D();
     wgpu::BindGroup bindGroup =
         CreateBindGroupForTest(renderPipeline.GetBindGroupLayout(0), textureDst,
                                config.copyOrigin3D.z, config.viewMipmapLevel);
@@ -809,7 +935,7 @@
     wgpu::Texture textureDst = CreateTextureFromTexture(textureSrc, srcConfig, dstConfig);
 
     // Verify if we can use texture as sampled textures correctly.
-    wgpu::RenderPipeline renderPipeline = CreateRenderPipelineForTest();
+    wgpu::RenderPipeline renderPipeline = CreateRenderPipelineForTestTex2D();
     wgpu::BindGroup bindGroup =
         CreateBindGroupForTest(renderPipeline.GetBindGroupLayout(0), textureDst,
                                dstConfig.copyOrigin3D.z, dstConfig.viewMipmapLevel);
@@ -850,7 +976,7 @@
     wgpu::Texture textureDst = CreateTextureFromTexture(textureSrc, srcConfig, dstConfig);
 
     // Verify if we can use texture as sampled textures correctly.
-    wgpu::RenderPipeline renderPipeline = CreateRenderPipelineForTest();
+    wgpu::RenderPipeline renderPipeline = CreateRenderPipelineForTestTex2D();
     wgpu::BindGroup bindGroup =
         CreateBindGroupForTest(renderPipeline.GetBindGroupLayout(0), textureDst,
                                dstConfig.copyOrigin3D.z, dstConfig.viewMipmapLevel);
@@ -906,7 +1032,7 @@
     wgpu::CommandBuffer commandBuffer = encoder.Finish();
     queue.Submit(1, &commandBuffer);
 
-    wgpu::RenderPipeline renderPipeline = CreateRenderPipelineForTest();
+    wgpu::RenderPipeline renderPipeline = CreateRenderPipelineForTestTex2D();
 
     for (uint32_t i = 0; i < kTotalCopyCount; ++i) {
         // Verify if we can use dstTextures as sampled textures correctly.
@@ -964,7 +1090,7 @@
 
     // We use the render pipeline to test if each layer can be correctly sampled with the
     // expected data.
-    wgpu::RenderPipeline renderPipeline = CreateRenderPipelineForTest();
+    wgpu::RenderPipeline renderPipeline = CreateRenderPipelineForTestTex2D();
 
     const wgpu::Extent3D kExpectedDataRegionPerLayer = {kDstVirtualSize.width,
                                                         kDstVirtualSize.height, 1u};