Implement CHROMIUM_copy_compressed_texture for D3D11.

BUG=angleproject:1356

Change-Id: Id563997d2921cf558c52a781ae66d8bde58d1f2f
Reviewed-on: https://chromium-review.googlesource.com/339847
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Commit-Queue: Geoff Lang <geofflang@chromium.org>
diff --git a/src/tests/gl_tests/CopyCompressedTextureTest.cpp b/src/tests/gl_tests/CopyCompressedTextureTest.cpp
new file mode 100644
index 0000000..4d41519
--- /dev/null
+++ b/src/tests/gl_tests/CopyCompressedTextureTest.cpp
@@ -0,0 +1,369 @@
+//
+// Copyright 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// CopyCompressedTextureTest.cpp: Tests of the GL_CHROMIUM_copy_compressed_texture extension
+
+#include "test_utils/ANGLETest.h"
+
+namespace angle
+{
+
+class CopyCompressedTextureTest : public ANGLETest
+{
+  protected:
+    CopyCompressedTextureTest()
+    {
+        setWindowWidth(256);
+        setWindowHeight(256);
+        setConfigRedBits(8);
+        setConfigGreenBits(8);
+        setConfigBlueBits(8);
+        setConfigAlphaBits(8);
+    }
+
+    void SetUp() override
+    {
+        ANGLETest::SetUp();
+
+        glGenTextures(2, mTextures);
+
+        mProgram = CompileProgram(
+            "attribute vec2 a_position;\n"
+            "varying vec2 v_texcoord;\n"
+            "void main()\n"
+            "{\n"
+            "   gl_Position = vec4(a_position, 0.0, 1.0);\n"
+            "   v_texcoord = (a_position + 1.0) * 0.5;\n"
+            "}\n",
+            "precision mediump float;\n"
+            "uniform sampler2D u_texture;\n"
+            "varying vec2 v_texcoord;\n"
+            "void main()\n"
+            "{\n"
+            "    gl_FragColor = texture2D(u_texture, v_texcoord);\n"
+            "}\n");
+        ASSERT_NE(0u, mProgram);
+
+        if (extensionEnabled("GL_CHROMIUM_copy_compressed_texture"))
+        {
+            glCompressedCopyTextureCHROMIUM =
+                reinterpret_cast<PFNGLCOMPRESSEDCOPYTEXTURECHROMIUMPROC>(
+                    eglGetProcAddress("glCompressedCopyTextureCHROMIUM"));
+        }
+    }
+
+    void TearDown() override
+    {
+        glDeleteTextures(2, mTextures);
+        glDeleteProgram(mProgram);
+
+        ANGLETest::TearDown();
+    }
+
+    bool checkExtensions() const
+    {
+        if (!extensionEnabled("GL_CHROMIUM_copy_compressed_texture"))
+        {
+            std::cout
+                << "Test skipped because GL_CHROMIUM_copy_compressed_texture is not available."
+                << std::endl;
+            return false;
+        }
+
+        EXPECT_NE(nullptr, glCompressedCopyTextureCHROMIUM);
+        if (glCompressedCopyTextureCHROMIUM == nullptr)
+        {
+            return false;
+        }
+
+        return true;
+    }
+
+    GLuint mProgram     = 0;
+    GLuint mTextures[2] = {0, 0};
+
+    PFNGLCOMPRESSEDCOPYTEXTURECHROMIUMPROC glCompressedCopyTextureCHROMIUM = nullptr;
+};
+
+namespace
+{
+
+const GLColor &CompressedImageColor = GLColor::red;
+
+// Single compressed ATC block of source pixels all set to:
+// CompressedImageColor.
+const uint8_t CompressedImageATC[8] = {0x0, 0x7c, 0x0, 0xf8, 0x55, 0x55, 0x55, 0x55};
+
+// Single compressed ATCIA block of source pixels all set to:
+// CompressedImageColor.
+const uint8_t CompressedImageATCIA[16] = {0xff, 0xff, 0x0, 0x0,  0x0,  0x0,  0x0,  0x0,
+                                          0x0,  0x7c, 0x0, 0xf8, 0x55, 0x55, 0x55, 0x55};
+
+// Single compressed DXT1 block of source pixels all set to:
+// CompressedImageColor.
+const uint8_t CompressedImageDXT1[8] = {0x00, 0xf8, 0x00, 0xf8, 0xaa, 0xaa, 0xaa, 0xaa};
+
+// Single compressed DXT5 block of source pixels all set to:
+// CompressedImageColor.
+const uint8_t CompressedImageDXT5[16] = {0xff, 0xff, 0x0, 0x0,  0x0,  0x0,  0x0,  0x0,
+                                         0x0,  0xf8, 0x0, 0xf8, 0xaa, 0xaa, 0xaa, 0xaa};
+
+// Single compressed DXT1 block of source pixels all set to:
+// CompressedImageColor.
+const uint8_t CompressedImageETC1[8] = {0x0, 0x0, 0xf8, 0x2, 0xff, 0xff, 0x0, 0x0};
+
+}  // anonymous namespace
+
+// Test to ensure that the basic functionality of the extension works.
+TEST_P(CopyCompressedTextureTest, Basic)
+{
+    if (!checkExtensions())
+    {
+        return;
+    }
+
+    if (!extensionEnabled("GL_EXT_texture_compression_dxt1"))
+    {
+        std::cout << "Test skipped because GL_EXT_texture_compression_dxt1 is not available."
+                  << std::endl;
+        return;
+    }
+
+    glBindTexture(GL_TEXTURE_2D, mTextures[0]);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, 4, 4, 0,
+                           sizeof(CompressedImageDXT1), CompressedImageDXT1);
+    ASSERT_GL_NO_ERROR();
+
+    glBindTexture(GL_TEXTURE_2D, mTextures[1]);
+    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glCompressedCopyTextureCHROMIUM(mTextures[0], mTextures[1]);
+    ASSERT_GL_NO_ERROR();
+
+    // Load texture.
+    glActiveTexture(GL_TEXTURE0);
+    glBindTexture(GL_TEXTURE_2D, mTextures[1]);
+    GLint textureLoc = glGetUniformLocation(mProgram, "u_texture");
+    glUseProgram(mProgram);
+    glUniform1i(textureLoc, 0);
+
+    // Draw.
+    drawQuad(mProgram, "a_position", 0.5f);
+
+    EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, CompressedImageColor);
+    ASSERT_GL_NO_ERROR();
+}
+
+// Test validation of compressed formats
+TEST_P(CopyCompressedTextureTest, InternalFormat)
+{
+    if (!checkExtensions())
+    {
+        return;
+    }
+
+    struct Data
+    {
+        GLint format;
+        const uint8_t *data;
+        GLsizei dataSize;
+
+        Data() : Data(GL_NONE, nullptr, 0) {}
+        Data(GLint format, const uint8_t *data, GLsizei dataSize)
+            : format(format), data(data), dataSize(dataSize)
+        {
+        }
+    };
+    std::vector<Data> supportedFormats;
+
+    if (extensionEnabled("GL_AMD_compressed_ATC_texture"))
+    {
+        supportedFormats.push_back(
+            Data(GL_ATC_RGB_AMD, CompressedImageATC, sizeof(CompressedImageATC)));
+        supportedFormats.push_back(Data(GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD, CompressedImageATCIA,
+                                        sizeof(CompressedImageATCIA)));
+    }
+    if (extensionEnabled("GL_EXT_texture_compression_dxt1"))
+    {
+        supportedFormats.push_back(Data(GL_COMPRESSED_RGB_S3TC_DXT1_EXT, CompressedImageDXT1,
+                                        sizeof(CompressedImageDXT1)));
+    }
+    if (extensionEnabled("GL_ANGLE_texture_compression_dxt5"))
+    {
+        supportedFormats.push_back(Data(GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, CompressedImageDXT5,
+                                        sizeof(CompressedImageDXT5)));
+    }
+    if (extensionEnabled("GL_OES_compressed_ETC1_RGB8_texture"))
+    {
+        supportedFormats.push_back(
+            Data(GL_ETC1_RGB8_OES, CompressedImageETC1, sizeof(CompressedImageETC1)));
+    }
+
+    for (const auto &supportedFormat : supportedFormats)
+    {
+        glBindTexture(GL_TEXTURE_2D, mTextures[0]);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+        glCompressedTexImage2D(GL_TEXTURE_2D, 0, supportedFormat.format, 4, 4, 0,
+                               supportedFormat.dataSize, supportedFormat.data);
+        ASSERT_GL_NO_ERROR();
+
+        glBindTexture(GL_TEXTURE_2D, mTextures[1]);
+        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+        glCompressedCopyTextureCHROMIUM(mTextures[0], mTextures[1]);
+        ASSERT_GL_NO_ERROR();
+    }
+}
+
+// Test that uncompressed textures generate errors when copying
+TEST_P(CopyCompressedTextureTest, InternalFormatNotSupported)
+{
+    if (!checkExtensions())
+    {
+        return;
+    }
+
+    glBindTexture(GL_TEXTURE_2D, mTextures[0]);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &GLColor::red);
+    ASSERT_GL_NO_ERROR();
+
+    glBindTexture(GL_TEXTURE_2D, mTextures[1]);
+    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    ASSERT_GL_NO_ERROR();
+
+    // Check that the GL_RGBA format reports an error.
+    glCompressedCopyTextureCHROMIUM(mTextures[0], mTextures[1]);
+    EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+}
+
+// Test validation of texture IDs
+TEST_P(CopyCompressedTextureTest, InvalidTextureIds)
+{
+    if (!checkExtensions())
+    {
+        return;
+    }
+
+    glBindTexture(GL_TEXTURE_2D, mTextures[0]);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, 4, 4, 0,
+                           sizeof(CompressedImageDXT1), CompressedImageDXT1);
+    ASSERT_GL_NO_ERROR();
+
+    glBindTexture(GL_TEXTURE_2D, mTextures[1]);
+    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    ASSERT_GL_NO_ERROR();
+
+    glCompressedCopyTextureCHROMIUM(mTextures[0], 99993);
+    EXPECT_GL_ERROR(GL_INVALID_VALUE);
+
+    glCompressedCopyTextureCHROMIUM(99994, mTextures[1]);
+    EXPECT_GL_ERROR(GL_INVALID_VALUE);
+
+    glCompressedCopyTextureCHROMIUM(99995, 99996);
+    EXPECT_GL_ERROR(GL_INVALID_VALUE);
+
+    glCompressedCopyTextureCHROMIUM(mTextures[0], mTextures[1]);
+    EXPECT_GL_NO_ERROR();
+}
+
+// Test that only 2D textures are valid
+TEST_P(CopyCompressedTextureTest, BindingPoints)
+{
+    if (!checkExtensions())
+    {
+        return;
+    }
+
+    glBindTexture(GL_TEXTURE_CUBE_MAP, mTextures[0]);
+    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    for (GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X; face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z;
+         face++)
+    {
+        glCompressedTexImage2D(face, 0, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, 4, 4, 0,
+                               sizeof(CompressedImageDXT1), CompressedImageDXT1);
+    }
+    ASSERT_GL_NO_ERROR();
+
+    glBindTexture(GL_TEXTURE_CUBE_MAP, mTextures[1]);
+    glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    ASSERT_GL_NO_ERROR();
+
+    glCompressedCopyTextureCHROMIUM(mTextures[0], mTextures[1]);
+    EXPECT_GL_ERROR(GL_INVALID_VALUE);
+}
+
+// Test the destination texture cannot be immutable
+TEST_P(CopyCompressedTextureTest, Immutable)
+{
+    if (!checkExtensions() || getClientMajorVersion() < 3)
+    {
+        return;
+    }
+
+    glBindTexture(GL_TEXTURE_2D, mTextures[0]);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, 4, 4, 0,
+                           sizeof(CompressedImageDXT1), CompressedImageDXT1);
+    ASSERT_GL_NO_ERROR();
+
+    glBindTexture(GL_TEXTURE_2D, mTextures[1]);
+    glTexStorage2D(GL_TEXTURE_2D, 1, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, 4, 4);
+    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    ASSERT_GL_NO_ERROR();
+
+    glCompressedCopyTextureCHROMIUM(mTextures[0], mTextures[1]);
+    EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+}
+
+// Use this to select which configurations (e.g. which renderer, which GLES major version) these
+// tests should be run against.
+ANGLE_INSTANTIATE_TEST(CopyCompressedTextureTest,
+                       ES2_D3D9(),
+                       ES2_D3D11(),
+                       ES3_D3D11(),
+                       ES2_OPENGL(),
+                       ES3_OPENGL(),
+                       ES2_OPENGLES(),
+                       ES3_OPENGLES());
+
+}  // namespace angle