D3D11: Fix basic ReadPixels from 3D attachments.

Use the TextureHelper class to abstractly handle 2D and 3d textures.

Also refactor the Image11 copy methods to be a bit cleaner and not
use the copy conversion path when unnecessary.

This patch does not yet fix layer attachments - the fix for that will
come up in a subsequent patch.

BUG=angleproject:1290

Change-Id: If8b7aa8848ca4260e0dde690e7a99e115a97fabb
Reviewed-on: https://chromium-review.googlesource.com/323442
Tryjob-Request: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Tested-by: Jamie Madill <jmadill@chromium.org>
Commit-Queue: Jamie Madill <jmadill@chromium.org>
diff --git a/src/tests/gl_tests/ReadPixelsTest.cpp b/src/tests/gl_tests/ReadPixelsTest.cpp
index 15ccd94..7809c21 100644
--- a/src/tests/gl_tests/ReadPixelsTest.cpp
+++ b/src/tests/gl_tests/ReadPixelsTest.cpp
@@ -409,6 +409,152 @@
     EXPECT_GL_ERROR(GL_INVALID_OPERATION);
 }
 
+class ReadPixelsTextureTest : public ANGLETest
+{
+  public:
+    ReadPixelsTextureTest() : mFBO(0), mTexture(0)
+    {
+        setWindowWidth(32);
+        setWindowHeight(32);
+        setConfigRedBits(8);
+        setConfigGreenBits(8);
+        setConfigBlueBits(8);
+        setConfigAlphaBits(8);
+    }
+
+    void SetUp() override
+    {
+        ANGLETest::SetUp();
+
+        glGenTextures(1, &mTexture);
+        glGenFramebuffers(1, &mFBO);
+        glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
+    }
+
+    void TearDown() override
+    {
+        glDeleteFramebuffers(1, &mFBO);
+        glDeleteTextures(1, &mTexture);
+
+        ANGLETest::TearDown();
+    }
+
+    void testRead(GLenum textureTarget, GLint levels, GLint attachmentLevel, GLint attachmentLayer)
+    {
+        glBindTexture(textureTarget, mTexture);
+        glTexStorage3D(textureTarget, levels, GL_RGBA8, 4, 4, 4);
+        glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTexture, attachmentLevel,
+                                  attachmentLayer);
+
+        initializeTextureData(textureTarget, levels);
+        verifyColor(attachmentLevel, attachmentLayer);
+    }
+
+    // Give each {level,layer} pair a (probably) unique color via random.
+    GLuint getColorValue(GLint level, GLint layer)
+    {
+        mRNG.reseed(level + layer * 32);
+        return mRNG.randomUInt();
+    }
+
+    void verifyColor(GLint level, GLint layer)
+    {
+        angle::GLColor colorValue(getColorValue(level, layer));
+        EXPECT_PIXEL_COLOR_EQ(0, 0, colorValue);
+    }
+
+    void initializeTextureData(GLenum textureTarget, GLint levels)
+    {
+        for (GLint level = 0; level < levels; ++level)
+        {
+            GLint mipSize = 4 >> level;
+            GLint layers  = (textureTarget == GL_TEXTURE_3D ? mipSize : 4);
+
+            size_t layerSize = mipSize * mipSize;
+            std::vector<GLuint> textureData(layers * layerSize);
+
+            for (GLint layer = 0; layer < layers; ++layer)
+            {
+                GLuint colorValue = getColorValue(level, layer);
+                size_t offset = (layer * layerSize);
+                std::fill(textureData.begin() + offset, textureData.begin() + offset + layerSize,
+                          colorValue);
+            }
+
+            glTexSubImage3D(textureTarget, level, 0, 0, 0, mipSize, mipSize, layers, GL_RGBA,
+                            GL_UNSIGNED_BYTE, textureData.data());
+        }
+    }
+
+    angle::RNG mRNG;
+    GLuint mFBO;
+    GLuint mTexture;
+};
+
+// Test 3D attachment readback.
+TEST_P(ReadPixelsTextureTest, BasicAttachment3D)
+{
+    testRead(GL_TEXTURE_3D, 1, 0, 0);
+}
+
+// Test 3D attachment readback, non-zero mip.
+TEST_P(ReadPixelsTextureTest, MipAttachment3D)
+{
+    testRead(GL_TEXTURE_3D, 2, 1, 0);
+}
+
+// Test 3D attachment readback, non-zero layer.
+TEST_P(ReadPixelsTextureTest, LayerAttachment3D)
+{
+    if (isD3D11())
+    {
+        // TODO(jmadill): Fix ReadPixels from non-zero layer attachments.
+        std::cout << "Test disabled on D3D11." << std::endl;
+        return;
+    }
+
+    testRead(GL_TEXTURE_3D, 1, 0, 1);
+}
+
+// Test 3D attachment readback, non-zero mip and layer.
+TEST_P(ReadPixelsTextureTest, MipLayerAttachment3D)
+{
+    if (isD3D11())
+    {
+        // TODO(jmadill): Fix ReadPixels from non-zero layer attachments.
+        std::cout << "Test disabled on D3D11." << std::endl;
+        return;
+    }
+
+    testRead(GL_TEXTURE_3D, 2, 1, 1);
+}
+
+// Test 2D array attachment readback.
+TEST_P(ReadPixelsTextureTest, BasicAttachment2DArray)
+{
+    testRead(GL_TEXTURE_2D_ARRAY, 1, 0, 0);
+}
+
+// Test 3D attachment readback, non-zero mip.
+TEST_P(ReadPixelsTextureTest, MipAttachment2DArray)
+{
+    testRead(GL_TEXTURE_2D_ARRAY, 2, 1, 0);
+}
+
+// Test 3D attachment readback, non-zero layer.
+TEST_P(ReadPixelsTextureTest, LayerAttachment2DArray)
+{
+    testRead(GL_TEXTURE_2D_ARRAY, 1, 0, 1);
+}
+
+// Test 3D attachment readback, non-zero mip and layer.
+TEST_P(ReadPixelsTextureTest, MipLayerAttachment2DArray)
+{
+    testRead(GL_TEXTURE_2D_ARRAY, 2, 1, 1);
+}
+
+// TODO(jmadill): Tests for PBOs with 3D and layer attachments.
+
 }  // anonymous namespace
 
 // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against.
@@ -416,3 +562,4 @@
 ANGLE_INSTANTIATE_TEST(ReadPixelsPBOTest, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
 ANGLE_INSTANTIATE_TEST(ReadPixelsPBODrawTest, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
 ANGLE_INSTANTIATE_TEST(ReadPixelsMultisampleTest, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
+ANGLE_INSTANTIATE_TEST(ReadPixelsTextureTest, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());