Amends CopyTexImage validation to conform to new language in the ES 3.0.3 spec.

bug=angle:582

Also performs format checks for non-subimage copies, and permits unsized format
textures as destination for the copy.

Change-Id: Id647bb968416fbd35e1c70e5da21c81367e08f07
Reviewed-on: https://chromium-review.googlesource.com/189431
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Tested-by: Shannon Woods <shannonwoods@chromium.org>
diff --git a/src/libGLESv2/formatutils.cpp b/src/libGLESv2/formatutils.cpp
index bc303fe..4145ac8 100644
--- a/src/libGLESv2/formatutils.cpp
+++ b/src/libGLESv2/formatutils.cpp
@@ -7,9 +7,10 @@
 
 // formatutils.cpp: Queries for GL image formats.
 
+#include "common/mathutil.h"
 #include "libGLESv2/formatutils.h"
 #include "libGLESv2/Context.h"
-#include "common/mathutil.h"
+#include "libGLESv2/Framebuffer.h"
 #include "libGLESv2/renderer/Renderer.h"
 #include "libGLESv2/renderer/imageformats.h"
 #include "libGLESv2/renderer/copyimage.h"
@@ -965,6 +966,107 @@
     return set;
 }
 
+struct EffectiveInternalFormatInfo
+{
+    GLenum mEffectiveFormat;
+    GLenum mDestFormat;
+    GLuint mMinRedBits;
+    GLuint mMaxRedBits;
+    GLuint mMinGreenBits;
+    GLuint mMaxGreenBits;
+    GLuint mMinBlueBits;
+    GLuint mMaxBlueBits;
+    GLuint mMinAlphaBits;
+    GLuint mMaxAlphaBits;
+
+    EffectiveInternalFormatInfo(GLenum effectiveFormat, GLenum destFormat, GLuint minRedBits, GLuint maxRedBits,
+                                GLuint minGreenBits, GLuint maxGreenBits, GLuint minBlueBits, GLuint maxBlueBits,
+                                GLuint minAlphaBits, GLuint maxAlphaBits)
+        : mEffectiveFormat(effectiveFormat), mDestFormat(destFormat), mMinRedBits(minRedBits),
+          mMaxRedBits(maxRedBits), mMinGreenBits(minGreenBits), mMaxGreenBits(maxGreenBits),
+          mMinBlueBits(minBlueBits), mMaxBlueBits(maxBlueBits), mMinAlphaBits(minAlphaBits),
+          mMaxAlphaBits(maxAlphaBits) {};
+};
+
+typedef std::vector<EffectiveInternalFormatInfo> EffectiveInternalFormatList;
+
+static EffectiveInternalFormatList BuildSizedEffectiveInternalFormatList()
+{
+    EffectiveInternalFormatList list;
+
+    // OpenGL ES 3.0.3 Specification, Table 3.17, pg 141: Effective internal format coresponding to destination internal format and
+    //                                                    linear source buffer component sizes.
+    //                                                                            | Source channel min/max sizes |
+    //                                         Effective Internal Format |  N/A   |  R   |  G   |  B   |  A      |
+    list.push_back(EffectiveInternalFormatInfo(GL_ALPHA8_EXT,              GL_NONE, 0,  0, 0,  0, 0,  0, 1, 8));
+    list.push_back(EffectiveInternalFormatInfo(GL_R8,                      GL_NONE, 1,  8, 0,  0, 0,  0, 0, 0));
+    list.push_back(EffectiveInternalFormatInfo(GL_RG8,                     GL_NONE, 1,  8, 1,  8, 0,  0, 0, 0));
+    list.push_back(EffectiveInternalFormatInfo(GL_RGB565,                  GL_NONE, 1,  5, 1,  6, 1,  5, 0, 0));
+    list.push_back(EffectiveInternalFormatInfo(GL_RGB8,                    GL_NONE, 6,  8, 7,  8, 6,  8, 0, 0));
+    list.push_back(EffectiveInternalFormatInfo(GL_RGBA4,                   GL_NONE, 1,  4, 1,  4, 1,  4, 1, 4));
+    list.push_back(EffectiveInternalFormatInfo(GL_RGB5_A1,                 GL_NONE, 5,  5, 5,  5, 5,  5, 1, 1));
+    list.push_back(EffectiveInternalFormatInfo(GL_RGBA8,                   GL_NONE, 5,  8, 5,  8, 5,  8, 2, 8));
+    list.push_back(EffectiveInternalFormatInfo(GL_RGB10_A2,                GL_NONE, 9, 10, 9, 10, 9, 10, 2, 2));
+
+    return list;
+}
+
+
+static EffectiveInternalFormatList BuildUnsizedEffectiveInternalFormatList()
+{
+    EffectiveInternalFormatList list;
+
+    // OpenGL ES 3.0.3 Specification, Table 3.17, pg 141: Effective internal format coresponding to destination internal format and
+    //                                                    linear source buffer component sizes.
+    //                                                                                        |          Source channel min/max sizes            |
+    //                                         Effective Internal Format |    Dest Format     |     R     |      G     |      B     |      A     |
+    list.push_back(EffectiveInternalFormatInfo(GL_ALPHA8_EXT,              GL_ALPHA,           0, UINT_MAX, 0, UINT_MAX, 0, UINT_MAX, 1,        8));
+    list.push_back(EffectiveInternalFormatInfo(GL_LUMINANCE8_EXT,          GL_LUMINANCE,       1,        8, 0, UINT_MAX, 0, UINT_MAX, 0, UINT_MAX));
+    list.push_back(EffectiveInternalFormatInfo(GL_LUMINANCE8_ALPHA8_EXT,   GL_LUMINANCE_ALPHA, 1,        8, 0, UINT_MAX, 0, UINT_MAX, 1,        8));
+    list.push_back(EffectiveInternalFormatInfo(GL_RGB565,                  GL_RGB,             1,        5, 1,        6, 1,        5, 0, UINT_MAX));
+    list.push_back(EffectiveInternalFormatInfo(GL_RGB8,                    GL_RGB,             6,        8, 7,        8, 6,        8, 0, UINT_MAX));
+    list.push_back(EffectiveInternalFormatInfo(GL_RGBA4,                   GL_RGBA,            1,        4, 1,        4, 1,        4, 1,        4));
+    list.push_back(EffectiveInternalFormatInfo(GL_RGB5_A1,                 GL_RGBA,            5,        5, 5,        5, 5,        5, 1,        1));
+    list.push_back(EffectiveInternalFormatInfo(GL_RGBA8,                   GL_RGBA,            5,        8, 5,        8, 5,        8, 5,        8));
+
+    return list;
+}
+
+static bool GetEffectiveInternalFormat(const InternalFormatInfo &srcFormat, const InternalFormatInfo &destFormat,
+                                       GLuint clientVersion, GLenum *outEffectiveFormat)
+{
+    const EffectiveInternalFormatList *list = NULL;
+    GLenum targetFormat = GL_NONE;
+
+    if (gl::IsSizedInternalFormat(destFormat.mFormat, clientVersion))
+    {
+        static const EffectiveInternalFormatList sizedList = BuildSizedEffectiveInternalFormatList();
+        list = &sizedList;
+    }
+    else
+    {
+        static const EffectiveInternalFormatList unsizedList = BuildUnsizedEffectiveInternalFormatList();
+        list = &unsizedList;
+        targetFormat = destFormat.mFormat;
+    }
+
+    for (size_t curFormat = 0; curFormat < list->size(); ++curFormat)
+    {
+        const EffectiveInternalFormatInfo& formatInfo = list->at(curFormat);
+        if ((formatInfo.mDestFormat == targetFormat) &&
+            (formatInfo.mMinRedBits   <= srcFormat.mRedBits   && formatInfo.mMaxRedBits   >= srcFormat.mRedBits)   &&
+            (formatInfo.mMinGreenBits <= srcFormat.mGreenBits && formatInfo.mMaxGreenBits >= srcFormat.mGreenBits) &&
+            (formatInfo.mMinBlueBits  <= srcFormat.mBlueBits  && formatInfo.mMaxBlueBits  >= srcFormat.mBlueBits)  &&
+            (formatInfo.mMinAlphaBits <= srcFormat.mAlphaBits && formatInfo.mMaxAlphaBits >= srcFormat.mAlphaBits))
+        {
+            *outEffectiveFormat = formatInfo.mEffectiveFormat;
+            return true;
+        }
+    }
+
+    return false;
+}
+
 struct CopyConversion
 {
     GLenum mTextureFormat;
@@ -1104,7 +1206,7 @@
     }
 }
 
-bool IsValidCopyTexImageCombination(GLenum textureInternalFormat, GLenum frameBufferInternalFormat, GLuint clientVersion)
+bool IsValidCopyTexImageCombination(GLenum textureInternalFormat, GLenum frameBufferInternalFormat, GLuint readBufferHandle, GLuint clientVersion)
 {
     InternalFormatInfo textureInternalFormatInfo;
     InternalFormatInfo framebufferInternalFormatInfo;
@@ -1123,30 +1225,112 @@
                                                              framebufferInternalFormatInfo.mFormat);
             if (conversionSet.find(conversion) != conversionSet.end())
             {
-                // Section 3.8.5 of the GLES3 3.0.2 spec states that source and destination formats
-                // must both be signed or unsigned or fixed/floating point and both source and destinations
-                // must be either both SRGB or both not SRGB
+                // Section 3.8.5 of the GLES 3.0.3 spec states that source and destination formats
+                // must both be signed, unsigned, or fixed point and both source and destinations
+                // must be either both SRGB or both not SRGB. EXT_color_buffer_float adds allowed
+                // conversion between fixed and floating point.
 
                 if ((textureInternalFormatInfo.mColorEncoding == GL_SRGB) != (framebufferInternalFormatInfo.mColorEncoding == GL_SRGB))
                 {
                     return false;
                 }
 
-                if ((textureInternalFormatInfo.mComponentType == GL_INT          && framebufferInternalFormatInfo.mComponentType == GL_INT         ) ||
-                    (textureInternalFormatInfo.mComponentType == GL_UNSIGNED_INT && framebufferInternalFormatInfo.mComponentType == GL_UNSIGNED_INT))
+                if (((textureInternalFormatInfo.mComponentType == GL_INT) != (framebufferInternalFormatInfo.mComponentType == GL_INT)) ||
+                    ((textureInternalFormatInfo.mComponentType == GL_UNSIGNED_INT) != (framebufferInternalFormatInfo.mComponentType == GL_UNSIGNED_INT)))
                 {
-                    return true;
+                    return false;
                 }
 
-                if ((textureInternalFormatInfo.mComponentType     == GL_UNSIGNED_NORMALIZED ||
-                     textureInternalFormatInfo.mComponentType     == GL_SIGNED_NORMALIZED   ||
-                     textureInternalFormatInfo.mComponentType     == GL_FLOAT               ) &&
-                    (framebufferInternalFormatInfo.mComponentType == GL_UNSIGNED_NORMALIZED ||
-                     framebufferInternalFormatInfo.mComponentType == GL_SIGNED_NORMALIZED   ||
-                     framebufferInternalFormatInfo.mComponentType == GL_FLOAT               ))
+                if (gl::IsFloatOrFixedComponentType(textureInternalFormatInfo.mComponentType) &&
+                    !gl::IsFloatOrFixedComponentType(framebufferInternalFormatInfo.mComponentType))
                 {
-                    return true;
+                    return false;
                 }
+
+                // GLES specification 3.0.3, sec 3.8.5, pg 139-140:
+                // The effective internal format of the source buffer is determined with the following rules applied in order:
+                //    * If the source buffer is a texture or renderbuffer that was created with a sized internal format then the
+                //      effective internal format is the source buffer's sized internal format.
+                //    * If the source buffer is a texture that was created with an unsized base internal format, then the
+                //      effective internal format is the source image array's effective internal format, as specified by table
+                //      3.12, which is determined from the <format> and <type> that were used when the source image array was
+                //      specified by TexImage*.
+                //    * Otherwise the effective internal format is determined by the row in table 3.17 or 3.18 where
+                //      Destination Internal Format matches internalformat and where the [source channel sizes] are consistent
+                //      with the values of the source buffer's [channel sizes]. Table 3.17 is used if the
+                //      FRAMEBUFFER_ATTACHMENT_ENCODING is LINEAR and table 3.18 is used if the FRAMEBUFFER_ATTACHMENT_ENCODING
+                //      is SRGB.
+                InternalFormatInfo sourceEffectiveFormat;
+                if (readBufferHandle != 0)
+                {
+                    // Not the default framebuffer, therefore the read buffer must be a user-created texture or renderbuffer
+                    if (gl::IsSizedInternalFormat(framebufferInternalFormatInfo.mFormat, clientVersion))
+                    {
+                        sourceEffectiveFormat = framebufferInternalFormatInfo;
+                    }
+                    else
+                    {
+                        // Renderbuffers cannot be created with an unsized internal format, so this must be an unsized-format
+                        // texture. We can use the same table we use when creating textures to get its effective sized format.
+                        GLenum effectiveFormat = gl::GetSizedInternalFormat(framebufferInternalFormatInfo.mFormat,
+                                                                            framebufferInternalFormatInfo.mType, clientVersion);
+                        gl::GetInternalFormatInfo(effectiveFormat, clientVersion, &sourceEffectiveFormat);
+                    }
+                }
+                else
+                {
+                    // The effective internal format must be derived from the source framebuffer's channel sizes.
+                    // This is done in GetEffectiveInternalFormat for linear buffers (table 3.17)
+                    if (framebufferInternalFormatInfo.mColorEncoding == GL_LINEAR)
+                    {
+                        GLenum effectiveFormat;
+                        if (GetEffectiveInternalFormat(framebufferInternalFormatInfo, textureInternalFormatInfo, clientVersion, &effectiveFormat))
+                        {
+                            gl::GetInternalFormatInfo(effectiveFormat, clientVersion, &sourceEffectiveFormat);
+                        }
+                        else
+                        {
+                            return false;
+                        }
+                    }
+                    else if (framebufferInternalFormatInfo.mColorEncoding == GL_SRGB)
+                    {
+                        // SRGB buffers can only be copied to sized format destinations according to table 3.18
+                        if (gl::IsSizedInternalFormat(textureInternalFormat, clientVersion) &&
+                            (framebufferInternalFormatInfo.mRedBits   >= 1 && framebufferInternalFormatInfo.mRedBits   <= 8) &&
+                            (framebufferInternalFormatInfo.mGreenBits >= 1 && framebufferInternalFormatInfo.mGreenBits <= 8) &&
+                            (framebufferInternalFormatInfo.mBlueBits  >= 1 && framebufferInternalFormatInfo.mBlueBits  <= 8) &&
+                            (framebufferInternalFormatInfo.mAlphaBits >= 1 && framebufferInternalFormatInfo.mAlphaBits <= 8))
+                        {
+                            gl::GetInternalFormatInfo(GL_SRGB8_ALPHA8, clientVersion, &sourceEffectiveFormat);
+                        }
+                        else
+                        {
+                            return false;
+                        }
+                    }
+                    else
+                    {
+                        UNREACHABLE();
+                    }
+                }
+
+                if (gl::IsSizedInternalFormat(textureInternalFormatInfo.mFormat, clientVersion))
+                {
+                    // Section 3.8.5 of the GLES 3.0.3 spec, pg 139, requires that, if the destination format is sized,
+                    // component sizes of the source and destination formats must exactly match
+                    if (textureInternalFormatInfo.mRedBits != sourceEffectiveFormat.mRedBits ||
+                        textureInternalFormatInfo.mGreenBits != sourceEffectiveFormat.mGreenBits ||
+                        textureInternalFormatInfo.mBlueBits != sourceEffectiveFormat.mBlueBits ||
+                        textureInternalFormatInfo.mAlphaBits != sourceEffectiveFormat.mAlphaBits)
+                    {
+                        return false;
+                    }
+                }
+
+
+                return true; // A conversion function exists, and no rule in the specification has precluded conversion
+                             // between these formats.
             }
 
             return false;
@@ -1325,6 +1509,20 @@
     }
 }
 
+bool IsFloatOrFixedComponentType(GLenum type)
+{
+    if (type == GL_UNSIGNED_NORMALIZED ||
+        type == GL_SIGNED_NORMALIZED ||
+        type == GL_FLOAT)
+    {
+        return true;
+    }
+    else
+    {
+        return false;
+    }
+}
+
 GLenum GetFormat(GLenum internalFormat, GLuint clientVersion)
 {
     InternalFormatInfo internalFormatInfo;