Implement CHROMIUM_copy_texture for D3D11.

BUG=angleproject:1356

Change-Id: I70246762411dbeeb3e291e317854139a68d80070
Reviewed-on: https://chromium-review.googlesource.com/339434
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Geoff Lang <geofflang@chromium.org>
diff --git a/src/libANGLE/validationES2.cpp b/src/libANGLE/validationES2.cpp
index c25d1e5..0d2fe14 100644
--- a/src/libANGLE/validationES2.cpp
+++ b/src/libANGLE/validationES2.cpp
@@ -205,6 +205,84 @@
     return true;
 }
 
+bool IsValidCopyTextureFormat(Context *context, GLenum internalFormat)
+{
+    const InternalFormat &internalFormatInfo = GetInternalFormatInfo(internalFormat);
+    switch (internalFormatInfo.format)
+    {
+        case GL_ALPHA:
+        case GL_LUMINANCE:
+        case GL_LUMINANCE_ALPHA:
+        case GL_RGB:
+        case GL_RGBA:
+            return true;
+
+        case GL_RED:
+            return context->getClientMajorVersion() >= 3 || context->getExtensions().textureRG;
+
+        case GL_BGRA_EXT:
+            return context->getExtensions().textureFormatBGRA8888;
+
+        default:
+            return false;
+    }
+}
+
+bool IsValidCopyTextureDestinationFormatType(Context *context, GLint internalFormat, GLenum type)
+{
+    switch (internalFormat)
+    {
+        case GL_RGB:
+        case GL_RGBA:
+            break;
+
+        case GL_BGRA_EXT:
+            return context->getExtensions().textureFormatBGRA8888;
+
+        default:
+            return false;
+    }
+
+    switch (type)
+    {
+        case GL_UNSIGNED_BYTE:
+            break;
+
+        default:
+            return false;
+    }
+
+    return true;
+}
+
+bool IsValidCopyTextureDestinationTarget(Context *context, GLenum target)
+{
+    switch (target)
+    {
+        case GL_TEXTURE_2D:
+            return true;
+
+        // TODO(geofflang): accept GL_TEXTURE_RECTANGLE_ARB if the texture_rectangle extension is
+        // supported
+
+        default:
+            return false;
+    }
+}
+
+bool IsValidCopyTextureSourceTarget(Context *context, GLenum target)
+{
+    if (IsValidCopyTextureDestinationTarget(context, target))
+    {
+        return true;
+    }
+
+    // TODO(geofflang): accept GL_TEXTURE_EXTERNAL_OES if the texture_external extension is
+    // supported
+
+    return false;
+}
+
 }  // anonymous namespace
 
 bool ValidateES2TexImageParameters(Context *context, GLenum target, GLint level, GLenum internalformat, bool isCompressed, bool isSubImage,
@@ -3033,4 +3111,202 @@
     return true;
 }
 
+bool ValidateCopyTextureCHROMIUM(Context *context,
+                                 GLuint sourceId,
+                                 GLuint destId,
+                                 GLint internalFormat,
+                                 GLenum destType,
+                                 GLboolean unpackFlipY,
+                                 GLboolean unpackPremultiplyAlpha,
+                                 GLboolean unpackUnmultiplyAlpha)
+{
+    if (!context->getExtensions().copyTexture)
+    {
+        context->handleError(
+            Error(GL_INVALID_OPERATION, "GL_CHROMIUM_copy_texture extension not available."));
+        return false;
+    }
+
+    const gl::Texture *source = context->getTexture(sourceId);
+    if (source == nullptr)
+    {
+        context->handleError(
+            Error(GL_INVALID_VALUE, "Source texture is not a valid texture object."));
+        return false;
+    }
+
+    if (!IsValidCopyTextureSourceTarget(context, source->getTarget()))
+    {
+        context->handleError(Error(GL_INVALID_VALUE, "Source texture a valid texture type."));
+        return false;
+    }
+
+    GLenum sourceTarget = source->getTarget();
+    ASSERT(sourceTarget != GL_TEXTURE_CUBE_MAP);
+    if (source->getWidth(sourceTarget, 0) == 0 || source->getHeight(sourceTarget, 0) == 0)
+    {
+        context->handleError(
+            Error(GL_INVALID_VALUE, "Level 0 of the source texture must be defined."));
+        return false;
+    }
+
+    const gl::Format &sourceFormat = source->getFormat(sourceTarget, 0);
+    if (!IsValidCopyTextureFormat(context, sourceFormat.format))
+    {
+        context->handleError(
+            Error(GL_INVALID_OPERATION, "Source texture internal format is invalid."));
+        return false;
+    }
+
+    const gl::Texture *dest = context->getTexture(destId);
+    if (dest == nullptr)
+    {
+        context->handleError(
+            Error(GL_INVALID_VALUE, "Destination texture is not a valid texture object."));
+        return false;
+    }
+
+    if (!IsValidCopyTextureDestinationTarget(context, dest->getTarget()))
+    {
+        context->handleError(Error(GL_INVALID_VALUE, "Destination texture a valid texture type."));
+        return false;
+    }
+
+    if (!IsValidCopyTextureDestinationFormatType(context, internalFormat, destType))
+    {
+        context->handleError(
+            Error(GL_INVALID_OPERATION,
+                  "Destination internal format and type combination is not valid."));
+        return false;
+    }
+
+    if (dest->getImmutableFormat())
+    {
+        context->handleError(Error(GL_INVALID_OPERATION, "Destination texture is immutable."));
+        return false;
+    }
+
+    return true;
+}
+
+bool ValidateCopySubTextureCHROMIUM(Context *context,
+                                    GLuint sourceId,
+                                    GLuint destId,
+                                    GLint xoffset,
+                                    GLint yoffset,
+                                    GLint x,
+                                    GLint y,
+                                    GLsizei width,
+                                    GLsizei height,
+                                    GLboolean unpackFlipY,
+                                    GLboolean unpackPremultiplyAlpha,
+                                    GLboolean unpackUnmultiplyAlpha)
+{
+    if (!context->getExtensions().copyTexture)
+    {
+        context->handleError(
+            Error(GL_INVALID_OPERATION, "GL_CHROMIUM_copy_texture extension not available."));
+        return false;
+    }
+
+    const gl::Texture *source = context->getTexture(sourceId);
+    if (source == nullptr)
+    {
+        context->handleError(
+            Error(GL_INVALID_VALUE, "Source texture is not a valid texture object."));
+        return false;
+    }
+
+    if (!IsValidCopyTextureSourceTarget(context, source->getTarget()))
+    {
+        context->handleError(Error(GL_INVALID_VALUE, "Source texture a valid texture type."));
+        return false;
+    }
+
+    GLenum sourceTarget = source->getTarget();
+    ASSERT(sourceTarget != GL_TEXTURE_CUBE_MAP);
+    if (source->getWidth(sourceTarget, 0) == 0 || source->getHeight(sourceTarget, 0) == 0)
+    {
+        context->handleError(
+            Error(GL_INVALID_VALUE, "Level 0 of the source texture must be defined."));
+        return false;
+    }
+
+    if (x < 0 || y < 0)
+    {
+        context->handleError(Error(GL_INVALID_VALUE, "x and y cannot be negative."));
+        return false;
+    }
+
+    if (width < 0 || height < 0)
+    {
+        context->handleError(Error(GL_INVALID_VALUE, "width and height cannot be negative."));
+        return false;
+    }
+
+    if (static_cast<size_t>(x + width) > source->getWidth(sourceTarget, 0) ||
+        static_cast<size_t>(y + height) > source->getHeight(sourceTarget, 0))
+    {
+        context->handleError(
+            Error(GL_INVALID_VALUE, "Source texture not large enough to copy from."));
+        return false;
+    }
+
+    const gl::Format &sourceFormat = source->getFormat(sourceTarget, 0);
+    if (!IsValidCopyTextureFormat(context, sourceFormat.format))
+    {
+        context->handleError(
+            Error(GL_INVALID_OPERATION, "Source texture internal format is invalid."));
+        return false;
+    }
+
+    const gl::Texture *dest = context->getTexture(destId);
+    if (dest == nullptr)
+    {
+        context->handleError(
+            Error(GL_INVALID_VALUE, "Destination texture is not a valid texture object."));
+        return false;
+    }
+
+    if (!IsValidCopyTextureDestinationTarget(context, dest->getTarget()))
+    {
+        context->handleError(Error(GL_INVALID_VALUE, "Destination texture a valid texture type."));
+        return false;
+    }
+
+    GLenum destTarget = dest->getTarget();
+    ASSERT(destTarget != GL_TEXTURE_CUBE_MAP);
+    if (dest->getWidth(sourceTarget, 0) == 0 || dest->getHeight(sourceTarget, 0) == 0)
+    {
+        context->handleError(
+            Error(GL_INVALID_VALUE, "Level 0 of the destination texture must be defined."));
+        return false;
+    }
+
+    const gl::Format &destFormat = dest->getFormat(destTarget, 0);
+    if (!IsValidCopyTextureDestinationFormatType(context, destFormat.format, destFormat.type))
+    {
+        context->handleError(
+            Error(GL_INVALID_OPERATION,
+                  "Destination internal format and type combination is not valid."));
+        return false;
+    }
+
+    if (xoffset < 0 || yoffset < 0)
+    {
+        context->handleError(Error(GL_INVALID_VALUE, "xoffset and yoffset cannot be negative."));
+        return false;
+    }
+
+    if (static_cast<size_t>(xoffset + width) > dest->getWidth(destTarget, 0) ||
+        static_cast<size_t>(yoffset + height) > dest->getHeight(destTarget, 0))
+    {
+        context->handleError(
+            Error(GL_INVALID_VALUE, "Destination texture not large enough to copy to."));
+        return false;
+    }
+
+    return true;
+}
+
 }  // namespace gl