Fix glCopyTexSubImage3D.

Two bugs were present in our implementation. We were using the y offset
for z in ensureSubImageInitialized. And for our D3D back-end we were
potentially reading from the wrong image index.

Bug: chromium:947342
Change-Id: If39671a911e08fcc641b9ba6f5910e3a2c16eb5d
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1558671
Commit-Queue: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Jonah Ryan-Davis <jonahr@google.com>
diff --git a/src/libANGLE/Context.cpp b/src/libANGLE/Context.cpp
index 7954dee..d86b8bc 100644
--- a/src/libANGLE/Context.cpp
+++ b/src/libANGLE/Context.cpp
@@ -3726,10 +3726,11 @@
     Offset destOffset(xoffset, yoffset, 0);
     Rectangle sourceArea(x, y, width, height);
 
+    ImageIndex index = ImageIndex::MakeFromTarget(target, level);
+
     Framebuffer *framebuffer = mState.getReadFramebuffer();
     Texture *texture         = getTargetTexture(TextureTargetToType(target));
-    ANGLE_CONTEXT_TRY(
-        texture->copySubImage(this, target, level, destOffset, sourceArea, framebuffer));
+    ANGLE_CONTEXT_TRY(texture->copySubImage(this, index, destOffset, sourceArea, framebuffer));
 }
 
 void Context::copyTexSubImage3D(TextureType target,
@@ -3753,10 +3754,11 @@
     Offset destOffset(xoffset, yoffset, zoffset);
     Rectangle sourceArea(x, y, width, height);
 
+    ImageIndex index = ImageIndex::MakeFromType(target, level, zoffset);
+
     Framebuffer *framebuffer = mState.getReadFramebuffer();
     Texture *texture         = getTargetTexture(target);
-    ANGLE_CONTEXT_TRY(texture->copySubImage(this, NonCubeTextureTypeToTarget(target), level,
-                                            destOffset, sourceArea, framebuffer));
+    ANGLE_CONTEXT_TRY(texture->copySubImage(this, index, destOffset, sourceArea, framebuffer));
 }
 
 void Context::framebufferTexture2D(GLenum target,
diff --git a/src/libANGLE/Texture.cpp b/src/libANGLE/Texture.cpp
index 9122a61..4854c6b 100644
--- a/src/libANGLE/Texture.cpp
+++ b/src/libANGLE/Texture.cpp
@@ -1147,24 +1147,22 @@
 }
 
 angle::Result Texture::copySubImage(Context *context,
-                                    TextureTarget target,
-                                    GLint level,
+                                    const ImageIndex &index,
                                     const Offset &destOffset,
                                     const Rectangle &sourceArea,
                                     Framebuffer *source)
 {
-    ASSERT(TextureTargetToType(target) == mState.mType);
+    ASSERT(TextureTargetToType(index.getTarget()) == mState.mType);
 
     // Ensure source FBO is initialized.
     ANGLE_TRY(source->ensureReadAttachmentInitialized(context, GL_COLOR_BUFFER_BIT));
 
-    Box destBox(destOffset.x, destOffset.y, destOffset.y, sourceArea.width, sourceArea.height, 1);
-    ANGLE_TRY(ensureSubImageInitialized(context, target, level, destBox));
-
-    ImageIndex index = ImageIndex::MakeFromTarget(target, level);
+    Box destBox(destOffset.x, destOffset.y, destOffset.z, sourceArea.width, sourceArea.height, 1);
+    ANGLE_TRY(
+        ensureSubImageInitialized(context, index.getTarget(), index.getLevelIndex(), destBox));
 
     ANGLE_TRY(mTexture->copySubImage(context, index, destOffset, sourceArea, source));
-    ANGLE_TRY(handleMipmapGenerationHint(context, level));
+    ANGLE_TRY(handleMipmapGenerationHint(context, index.getLevelIndex()));
 
     return angle::Result::Continue;
 }
diff --git a/src/libANGLE/Texture.h b/src/libANGLE/Texture.h
index ec5f625..e381140 100644
--- a/src/libANGLE/Texture.h
+++ b/src/libANGLE/Texture.h
@@ -349,8 +349,7 @@
                             GLenum internalFormat,
                             Framebuffer *source);
     angle::Result copySubImage(Context *context,
-                               TextureTarget target,
-                               GLint level,
+                               const ImageIndex &index,
                                const Offset &destOffset,
                                const Rectangle &sourceArea,
                                Framebuffer *source);
diff --git a/src/tests/gl_tests/CopyTexImageTest.cpp b/src/tests/gl_tests/CopyTexImageTest.cpp
index 3c8c33c..45b230e 100644
--- a/src/tests/gl_tests/CopyTexImageTest.cpp
+++ b/src/tests/gl_tests/CopyTexImageTest.cpp
@@ -512,6 +512,40 @@
     EXPECT_GL_ERROR(GL_INVALID_OPERATION);
 }
 
+// Test CopyTexImage3D with some simple parameters with a 2D array texture.
+TEST_P(CopyTexImageTestES3, 2DArraySubImage)
+{
+    // Seems to fail on AMD OpenGL Windows.
+    ANGLE_SKIP_TEST_IF(IsAMD() && IsOpenGL() & IsWindows());
+
+    GLTexture tex;
+    glBindTexture(GL_TEXTURE_2D_ARRAY, tex);
+
+    constexpr GLsizei kTexSize     = 4;
+    constexpr GLsizei kLayerOffset = 1;
+    constexpr GLsizei kLayers      = 2;
+
+    // Clear screen to green.
+    glClearColor(0, 1, 0, 1);
+    glClear(GL_COLOR_BUFFER_BIT);
+
+    // Initialize a two-layer 2D array texture with red.
+    std::vector<GLColor> red(kTexSize * kTexSize * kLayers, GLColor::red);
+    glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, kTexSize, kTexSize, kLayers, 0, GL_RGBA,
+                 GL_UNSIGNED_BYTE, red.data());
+    glCopyTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, kLayerOffset, 0, 0, kTexSize, kTexSize);
+    ASSERT_GL_NO_ERROR();
+
+    // Check level 0 (red from image data) and 1 (green from backbuffer clear).
+    GLFramebuffer fbo;
+    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+    glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex, 0, 0);
+    EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
+    glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex, 0, 1);
+    EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
+    ASSERT_GL_NO_ERROR();
+}
+
 // Use this to select which configurations (e.g. which renderer, which GLES major version) these
 // tests should be run against.
 ANGLE_INSTANTIATE_TEST(CopyTexImageTest,