Refactored validation for gl*Tex*Image2D.

TRAC #22956

Signed-off-by: Jamie Madill
Signed-off-by: Shannon Woods
Author: Geoff Lang

git-svn-id: https://angleproject.googlecode.com/svn/branches/es3proto@2354 736b8ea6-26fd-11df-bfd4-992fa37f6226
diff --git a/src/libGLESv2/libGLESv2.cpp b/src/libGLESv2/libGLESv2.cpp
index 7eb2d0b..a615c16 100644
--- a/src/libGLESv2/libGLESv2.cpp
+++ b/src/libGLESv2/libGLESv2.cpp
@@ -22,14 +22,14 @@
 #include "libGLESv2/Query.h"
 #include "libGLESv2/Context.h"
 
-bool validImageSize(GLint level, GLsizei width, GLsizei height, GLsizei depth)
+bool validImageSize(const gl::Context *context, GLint level, GLsizei width, GLsizei height, GLsizei depth)
 {
     if (level < 0 || width < 0 || height < 0 || depth < 0)
     {
         return false;
     }
 
-    if (gl::getContext() && gl::getContext()->supportsNonPower2Texture())
+    if (context->supportsNonPower2Texture())
     {
         return true;
     }
@@ -47,6 +47,21 @@
     return false;
 }
 
+bool validCompressedImageSize(GLsizei width, GLsizei height)
+{
+    if (width != 1 && width != 2 && width % 4 != 0)
+    {
+        return false;
+    }
+
+    if (height != 1 && height != 2 && height % 4 != 0)
+    {
+        return false;
+    }
+
+    return true;
+}
+
 // Verify that format/type are one of the combinations from table 3.4.
 bool checkTextureFormatType(GLenum format, GLenum type)
 {
@@ -223,27 +238,342 @@
     return true;
 }
 
+bool validateES2TexImageParameters(gl::Context *context, GLenum target, GLint level, GLint internalformat, bool isCompressed, bool isSubImage,
+                                   GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
+                                   GLint border, GLenum format, GLenum type, const GLvoid *pixels)
+{
+    if (!validImageSize(context, level, width, height, 1))
+    {
+        return gl::error(GL_INVALID_VALUE, false);
+    }
+
+    if (isCompressed && !validCompressedImageSize(width, height))
+    {
+        return gl::error(GL_INVALID_OPERATION, false);
+    }
+
+    if (level < 0 || xoffset < 0 ||
+        std::numeric_limits<GLsizei>::max() - xoffset < width ||
+        std::numeric_limits<GLsizei>::max() - yoffset < height)
+    {
+        return gl::error(GL_INVALID_VALUE, false);
+    }
+
+    if (!isSubImage && !isCompressed && internalformat != GLint(format))
+    {
+        return gl::error(GL_INVALID_OPERATION, false);
+    }
+
+    gl::Texture *texture = NULL;
+    bool textureCompressed = false;
+    GLenum textureInternalFormat = GL_NONE;
+    GLint textureLevelWidth = 0;
+    GLint textureLevelHeight = 0;
+    switch (target)
+    {
+      case GL_TEXTURE_2D:
+        {
+            if (width > (context->getMaximum2DTextureDimension() >> level) ||
+                height > (context->getMaximum2DTextureDimension() >> level))
+            {
+                return gl::error(GL_INVALID_VALUE, false);
+            }
+
+            gl::Texture2D *tex2d = context->getTexture2D();
+            if (tex2d)
+            {
+                textureCompressed = tex2d->isCompressed(level);
+                textureInternalFormat = tex2d->getInternalFormat(level);
+                textureLevelWidth = tex2d->getWidth(level);
+                textureLevelHeight = tex2d->getHeight(level);
+                texture = tex2d;
+            }
+
+            if (isSubImage && !validateSubImageParams2D(isCompressed, width, height, xoffset, yoffset,
+                                                        level, format, type, tex2d))
+            {
+                return false;
+            }
+
+            texture = tex2d;
+        }
+        break;
+
+      case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
+      case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
+      case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
+      case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
+      case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
+      case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
+        {
+            if (!isSubImage && width != height)
+            {
+                return gl::error(GL_INVALID_VALUE, false);
+            }
+
+            if (width > (context->getMaximumCubeTextureDimension() >> level) ||
+                height > (context->getMaximumCubeTextureDimension() >> level))
+            {
+                return gl::error(GL_INVALID_VALUE, false);
+            }
+
+            gl::TextureCubeMap *texCube = context->getTextureCubeMap();
+            if (texCube)
+            {
+                textureCompressed = texCube->isCompressed(target, level);
+                textureInternalFormat = texCube->getInternalFormat(target, level);
+                textureLevelWidth = texCube->getWidth(target, level);
+                textureLevelHeight = texCube->getHeight(target, level);
+                texture = texCube;
+            }
+
+            if (isSubImage && !validateSubImageParamsCube(isCompressed, width, height, xoffset, yoffset,
+                                                          target, level, format, type, texCube))
+            {
+                return false;
+            }
+        }
+        break;
+
+      default:
+        return gl::error(GL_INVALID_ENUM, false);
+    }
+
+    if (!texture)
+    {
+        return gl::error(GL_INVALID_OPERATION, false);
+    }
+
+    if (!isSubImage && texture->isImmutable())
+    {
+        return gl::error(GL_INVALID_OPERATION, false);
+    }
+
+    // Verify zero border
+    if (border != 0)
+    {
+        return gl::error(GL_INVALID_VALUE, false);
+    }
+
+    // Verify texture is not requesting more mip levels than are available.
+    if (level > context->getMaximumTextureLevel())
+    {
+        return gl::error(GL_INVALID_VALUE, false);
+    }
+
+    GLenum actualInternalFormat = isSubImage ? textureInternalFormat : internalformat;
+    if (isCompressed)
+    {
+        switch (actualInternalFormat)
+        {
+          case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
+          case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+            if (!context->supportsDXT1Textures())
+            {
+                return gl::error(GL_INVALID_ENUM, false);
+            }
+            break;
+          case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
+            if (!context->supportsDXT3Textures())
+            {
+                return gl::error(GL_INVALID_ENUM, false);
+            }
+            break;
+          case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
+            if (!context->supportsDXT5Textures())
+            {
+                return gl::error(GL_INVALID_ENUM, false);
+            }
+            break;
+          default:
+            return gl::error(GL_INVALID_ENUM, false);
+        }
+    }
+    else
+    {
+        // validate <type> by itself (used as secondary key below)
+        switch (type)
+        {
+          case GL_UNSIGNED_BYTE:
+          case GL_UNSIGNED_SHORT_5_6_5:
+          case GL_UNSIGNED_SHORT_4_4_4_4:
+          case GL_UNSIGNED_SHORT_5_5_5_1:
+          case GL_UNSIGNED_SHORT:
+          case GL_UNSIGNED_INT:
+          case GL_UNSIGNED_INT_24_8_OES:
+          case GL_HALF_FLOAT_OES:
+          case GL_FLOAT:
+            break;
+          default:
+            return gl::error(GL_INVALID_ENUM, false);
+        }
+
+        // validate <format> + <type> combinations
+        // - invalid <format> -> sets INVALID_ENUM
+        // - invalid <format>+<type> combination -> sets INVALID_OPERATION
+        switch (format)
+        {
+          case GL_ALPHA:
+          case GL_LUMINANCE:
+          case GL_LUMINANCE_ALPHA:
+            switch (type)
+            {
+              case GL_UNSIGNED_BYTE:
+              case GL_FLOAT:
+              case GL_HALF_FLOAT_OES:
+                break;
+            default:
+                return gl::error(GL_INVALID_OPERATION, false);
+            }
+            break;
+          case GL_RGB:
+            switch (type)
+            {
+              case GL_UNSIGNED_BYTE:
+              case GL_UNSIGNED_SHORT_5_6_5:
+              case GL_FLOAT:
+              case GL_HALF_FLOAT_OES:
+                break;
+              default:
+                return gl::error(GL_INVALID_OPERATION, false);
+            }
+            break;
+          case GL_RGBA:
+            switch (type)
+            {
+              case GL_UNSIGNED_BYTE:
+              case GL_UNSIGNED_SHORT_4_4_4_4:
+              case GL_UNSIGNED_SHORT_5_5_5_1:
+              case GL_FLOAT:
+              case GL_HALF_FLOAT_OES:
+                break;
+              default:
+                return gl::error(GL_INVALID_OPERATION, false);
+            }
+            break;
+          case GL_BGRA_EXT:
+            switch (type)
+            {
+              case GL_UNSIGNED_BYTE:
+                break;
+              default:
+                return gl::error(GL_INVALID_OPERATION, false);
+            }
+            break;
+          case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:  // error cases for compressed textures are handled below
+          case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+          case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
+          case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
+            break;
+          case GL_DEPTH_COMPONENT:
+            switch (type)
+            {
+              case GL_UNSIGNED_SHORT:
+              case GL_UNSIGNED_INT:
+                break;
+              default:
+                return gl::error(GL_INVALID_OPERATION, false);
+            }
+            break;
+          case GL_DEPTH_STENCIL_OES:
+            switch (type)
+            {
+              case GL_UNSIGNED_INT_24_8_OES:
+                break;
+              default:
+                return gl::error(GL_INVALID_OPERATION, false);
+            }
+            break;
+          default:
+            return gl::error(GL_INVALID_ENUM, false);
+        }
+
+        switch (format)
+        {
+          case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
+          case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+            if (context->supportsDXT1Textures())
+            {
+                return gl::error(GL_INVALID_OPERATION, false);
+            }
+            else
+            {
+                return gl::error(GL_INVALID_ENUM, false);
+            }
+            break;
+          case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
+            if (context->supportsDXT3Textures())
+            {
+                return gl::error(GL_INVALID_OPERATION, false);
+            }
+            else
+            {
+                return gl::error(GL_INVALID_ENUM, false);
+            }
+            break;
+          case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
+            if (context->supportsDXT5Textures())
+            {
+                return gl::error(GL_INVALID_OPERATION, false);
+            }
+            else
+            {
+                return gl::error(GL_INVALID_ENUM, false);
+            }
+            break;
+          case GL_DEPTH_COMPONENT:
+          case GL_DEPTH_STENCIL_OES:
+            if (!context->supportsDepthTextures())
+            {
+                return gl::error(GL_INVALID_VALUE, false);
+            }
+            if (target != GL_TEXTURE_2D)
+            {
+                return gl::error(GL_INVALID_OPERATION, false);
+            }
+            // OES_depth_texture supports loading depth data and multiple levels,
+            // but ANGLE_depth_texture does not
+            if (pixels != NULL || level != 0)
+            {
+                return gl::error(GL_INVALID_OPERATION, false);
+            }
+            break;
+          default:
+            break;
+        }
+
+        if (type == GL_FLOAT)
+        {
+            if (!context->supportsFloat32Textures())
+            {
+                return gl::error(GL_INVALID_ENUM, false);
+            }
+        }
+        else if (type == GL_HALF_FLOAT_OES)
+        {
+            if (!context->supportsFloat16Textures())
+            {
+                return gl::error(GL_INVALID_ENUM, false);
+            }
+        }
+    }
+
+    return true;
+}
+
 bool validateES3TexImageParameters(gl::Context *context, GLenum target, GLint level, GLint internalformat, bool isCompressed, bool isSubImage,
                                    GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth,
                                    GLint border, GLenum format, GLenum type)
 {
     // Validate image size
-    if (level < 0 || width < 0 || height < 0 || depth < 0 )
+    if (!validImageSize(context, level, width, height, depth))
     {
         return gl::error(GL_INVALID_VALUE, false);
     }
 
-    if (isCompressed)
+    if (isCompressed && !validCompressedImageSize(width, height))
     {
-        if (width != 1 && width != 2 && width % 4 != 0)
-        {
-            return gl::error(GL_INVALID_OPERATION, false);
-        }
-
-        if (height != 1 && height != 2 && height % 4 != 0)
-        {
-            return gl::error(GL_INVALID_OPERATION, false);
-        }
+        return gl::error(GL_INVALID_OPERATION, false);
     }
 
     // Verify zero border
@@ -464,6 +794,262 @@
     return true;
 }
 
+
+bool validateES2CopyTexImageParameters(gl::Context* context, GLenum target, GLint level, GLenum internalformat, bool isSubImage,
+                                       GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height,
+                                       GLint border)
+{
+    if (!gl::IsInternalTextureTarget(target))
+    {
+        return gl::error(GL_INVALID_ENUM, false);
+    }
+
+    if (level < 0 || xoffset < 0 || yoffset < 0 || width < 0 || height < 0)
+    {
+        return gl::error(GL_INVALID_VALUE, false);
+    }
+
+    if (std::numeric_limits<GLsizei>::max() - xoffset < width || std::numeric_limits<GLsizei>::max() - yoffset < height)
+    {
+        return gl::error(GL_INVALID_VALUE, false);
+    }
+
+    if (width == 0 || height == 0)
+    {
+        return false;
+    }
+
+    // Verify zero border
+    if (border != 0)
+    {
+        return gl::error(GL_INVALID_VALUE, false);
+    }
+
+    // Validate dimensions based on Context limits and validate the texture
+    if (level > context->getMaximumTextureLevel())
+    {
+        return gl::error(GL_INVALID_VALUE, false);
+    }
+
+    gl::Framebuffer *framebuffer = context->getReadFramebuffer();
+
+    if (framebuffer->completeness() != GL_FRAMEBUFFER_COMPLETE)
+    {
+        return gl::error(GL_INVALID_FRAMEBUFFER_OPERATION, false);
+    }
+
+    if (context->getReadFramebufferHandle() != 0 && framebuffer->getSamples() != 0)
+    {
+        return gl::error(GL_INVALID_OPERATION, false);
+    }
+
+    GLenum colorbufferFormat = framebuffer->getReadColorbuffer()->getInternalFormat();
+    gl::Texture *texture = NULL;
+    GLenum textureFormat = GL_RGBA;
+
+    switch (target)
+    {
+      case GL_TEXTURE_2D:
+        {
+            if (width > (context->getMaximum2DTextureDimension() >> level) ||
+                height > (context->getMaximum2DTextureDimension() >> level))
+            {
+                return gl::error(GL_INVALID_VALUE, false);
+            }
+
+            gl::Texture2D *tex2d = context->getTexture2D();
+            if (tex2d)
+            {
+                if (isSubImage && !validateSubImageParams2D(false, width, height, xoffset, yoffset, level, GL_NONE, GL_NONE, tex2d))
+                {
+                    return false; // error already registered by validateSubImageParams
+                }
+                texture = tex2d;
+                textureFormat = gl::GetFormat(tex2d->getInternalFormat(level), context->getClientVersion());
+            }
+        }
+        break;
+
+      case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
+      case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
+      case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
+      case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
+      case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
+      case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
+        {
+            if (!isSubImage && width != height)
+            {
+                return gl::error(GL_INVALID_VALUE, false);
+            }
+
+            if (width > (context->getMaximumCubeTextureDimension() >> level) ||
+                height > (context->getMaximumCubeTextureDimension() >> level))
+            {
+                return gl::error(GL_INVALID_VALUE, false);
+            }
+
+            gl::TextureCubeMap *texcube = context->getTextureCubeMap();
+            if (texcube)
+            {
+                if (isSubImage && !validateSubImageParamsCube(false, width, height, xoffset, yoffset, target, level, GL_NONE, GL_NONE, texcube))
+                {
+                    return false; // error already registered by validateSubImageParams
+                }
+                texture = texcube;
+                textureFormat = gl::GetFormat(texcube->getInternalFormat(target, level), context->getClientVersion());
+            }
+        }
+        break;
+
+      default:
+        return gl::error(GL_INVALID_ENUM, false);
+    }
+
+    if (!texture)
+    {
+        return gl::error(GL_INVALID_OPERATION, false);
+    }
+
+    if (texture->isImmutable() && !isSubImage)
+    {
+        return gl::error(GL_INVALID_OPERATION, false);
+    }
+
+
+    // [OpenGL ES 2.0.24] table 3.9
+    if (isSubImage)
+    {
+        switch (textureFormat)
+        {
+          case GL_ALPHA:
+            if (colorbufferFormat != GL_ALPHA8_EXT &&
+                colorbufferFormat != GL_RGBA4 &&
+                colorbufferFormat != GL_RGB5_A1 &&
+                colorbufferFormat != GL_RGBA8_OES)
+            {
+                return gl::error(GL_INVALID_OPERATION, false);
+            }
+            break;
+          case GL_LUMINANCE:
+          case GL_RGB:
+            if (colorbufferFormat != GL_RGB565 &&
+                colorbufferFormat != GL_RGB8_OES &&
+                colorbufferFormat != GL_RGBA4 &&
+                colorbufferFormat != GL_RGB5_A1 &&
+                colorbufferFormat != GL_RGBA8_OES)
+            {
+                return gl::error(GL_INVALID_OPERATION, false);
+            }
+            break;
+          case GL_LUMINANCE_ALPHA:
+          case GL_RGBA:
+            if (colorbufferFormat != GL_RGBA4 &&
+                colorbufferFormat != GL_RGB5_A1 &&
+                colorbufferFormat != GL_RGBA8_OES)
+            {
+                return gl::error(GL_INVALID_OPERATION, false);
+            }
+            break;
+          case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
+          case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+          case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
+          case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
+            return gl::error(GL_INVALID_OPERATION, false);
+          case GL_DEPTH_COMPONENT:
+          case GL_DEPTH_STENCIL_OES:
+            return gl::error(GL_INVALID_OPERATION, false);
+          default:
+            return gl::error(GL_INVALID_OPERATION, false);
+        }
+    }
+    else
+    {
+        switch (internalformat)
+        {
+          case GL_ALPHA:
+            if (colorbufferFormat != GL_ALPHA8_EXT &&
+                colorbufferFormat != GL_RGBA4 &&
+                colorbufferFormat != GL_RGB5_A1 &&
+                colorbufferFormat != GL_BGRA8_EXT &&
+                colorbufferFormat != GL_RGBA8_OES)
+            {
+                return gl::error(GL_INVALID_OPERATION, false);
+            }
+            break;
+          case GL_LUMINANCE:
+          case GL_RGB:
+            if (colorbufferFormat != GL_RGB565 &&
+                colorbufferFormat != GL_RGB8_OES &&
+                colorbufferFormat != GL_RGBA4 &&
+                colorbufferFormat != GL_RGB5_A1 &&
+                colorbufferFormat != GL_BGRA8_EXT &&
+                colorbufferFormat != GL_RGBA8_OES)
+            {
+                return gl::error(GL_INVALID_OPERATION, false);
+            }
+            break;
+          case GL_LUMINANCE_ALPHA:
+          case GL_RGBA:
+            if (colorbufferFormat != GL_RGBA4 &&
+                colorbufferFormat != GL_RGB5_A1 &&
+                colorbufferFormat != GL_BGRA8_EXT &&
+                colorbufferFormat != GL_RGBA8_OES)
+            {
+                return gl::error(GL_INVALID_OPERATION, false);
+            }
+            break;
+          case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
+          case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+            if (context->supportsDXT1Textures())
+            {
+                return gl::error(GL_INVALID_OPERATION, false);
+            }
+            else
+            {
+                return gl::error(GL_INVALID_ENUM, false);
+            }
+            break;
+          case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
+            if (context->supportsDXT3Textures())
+            {
+                return gl::error(GL_INVALID_OPERATION, false);
+            }
+            else
+            {
+                return gl::error(GL_INVALID_ENUM, false);
+            }
+            break;
+          case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
+            if (context->supportsDXT5Textures())
+            {
+                return gl::error(GL_INVALID_OPERATION, false);
+            }
+            else
+            {
+                return gl::error(GL_INVALID_ENUM, false);
+            }
+            break;
+          case GL_DEPTH_COMPONENT:
+          case GL_DEPTH_COMPONENT16:
+          case GL_DEPTH_COMPONENT32_OES:
+          case GL_DEPTH_STENCIL_OES:
+          case GL_DEPTH24_STENCIL8_OES:
+            if (context->supportsDepthTextures())
+            {
+                return gl::error(GL_INVALID_OPERATION, false);
+            }
+            else
+            {
+                return gl::error(GL_INVALID_ENUM, false);
+            }
+          default:
+            return gl::error(GL_INVALID_ENUM, false);
+        }
+    }
+
+    return true;
+}
+
 bool validateES3CopyTexImageParameters(gl::Context *context, GLenum target, GLint level, GLenum internalformat,
                                        bool isSubImage, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y,
                                        GLsizei width, GLsizei height, GLint border)
@@ -550,6 +1136,21 @@
         }
         break;
 
+      case GL_TEXTURE_2D_ARRAY:
+        {
+            gl::Texture2DArray *texture2dArray = context->getTexture2DArray();
+            if (texture2dArray)
+            {
+                textureFormat = gl::GetFormat(texture2dArray->getInternalFormat(level), context->getClientVersion());
+                textureCompressed = texture2dArray->isCompressed(level);
+                textureLevelWidth = texture2dArray->getWidth(level);
+                textureLevelHeight = texture2dArray->getHeight(level);
+                textureLevelDepth = texture2dArray->getDepth(level);
+                texture = texture2dArray;
+            }
+        }
+        break;
+
       case GL_TEXTURE_3D:
         {
             gl::Texture3D *texture3d = context->getTexture3D();
@@ -1600,42 +2201,25 @@
 
     try
     {
-        if (!validImageSize(level, width, height, 1) || border != 0 || imageSize < 0)
-        {
-            return gl::error(GL_INVALID_VALUE);
-        }
-
-        switch (internalformat)
-        {
-          case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
-          case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
-          case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
-          case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
-            break;
-          default:
-            return gl::error(GL_INVALID_ENUM);
-        }
-
-        if (border != 0)
-        {
-            return gl::error(GL_INVALID_OPERATION);
-        }
-
-        if (width != 1 && width != 2 && width % 4 != 0)
-        {
-            return gl::error(GL_INVALID_OPERATION);
-        }
-
-        if (height != 1 && height != 2 && height % 4 != 0)
-        {
-            return gl::error(GL_INVALID_OPERATION);
-        }
-
         gl::Context *context = gl::getNonLostContext();
 
         if (context)
         {
-            if (level > context->getMaximumTextureLevel())
+            if (context->getClientVersion() < 3 &&
+                !validateES2TexImageParameters(context, target, level, internalformat, true, false,
+                                               0, 0, width, height, 0, GL_NONE, GL_NONE, data))
+            {
+                return;
+            }
+
+            if (context->getClientVersion() >= 3 &&
+                !validateES3TexImageParameters(context, target, level, internalformat, true, false,
+                                               0, 0, 0, width, height, 1, 0, GL_NONE, GL_NONE))
+            {
+                return;
+            }
+
+            if (imageSize < 0 || imageSize != (GLsizei)gl::GetBlockSize(internalformat, GL_UNSIGNED_BYTE, context->getClientVersion(), width, height))
             {
                 return gl::error(GL_INVALID_VALUE);
             }
@@ -1643,106 +2227,28 @@
             switch (target)
             {
               case GL_TEXTURE_2D:
-                if (width > (context->getMaximum2DTextureDimension() >> level) ||
-                    height > (context->getMaximum2DTextureDimension() >> level))
                 {
-                    return gl::error(GL_INVALID_VALUE);
+                    gl::Texture2D *texture = context->getTexture2D();
+                    texture->setCompressedImage(level, internalformat, width, height, imageSize, data);
                 }
                 break;
+
               case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
               case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
               case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
               case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
               case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
               case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
-                if (width != height)
                 {
-                    return gl::error(GL_INVALID_VALUE);
-                }
-
-                if (width > (context->getMaximumCubeTextureDimension() >> level) ||
-                    height > (context->getMaximumCubeTextureDimension() >> level))
-                {
-                    return gl::error(GL_INVALID_VALUE);
+                    gl::TextureCubeMap *texture = context->getTextureCubeMap();
+                    texture->setCompressedImage(target, level, internalformat, width, height, imageSize, data);
                 }
                 break;
+
               default:
                 return gl::error(GL_INVALID_ENUM);
             }
-
-            switch (internalformat) {
-              case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
-              case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
-                if (!context->supportsDXT1Textures())
-                {
-                    return gl::error(GL_INVALID_ENUM); // in this case, it's as though the internal format switch failed
-                }
-                break;
-              case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
-                if (!context->supportsDXT3Textures())
-                {
-                    return gl::error(GL_INVALID_ENUM); // in this case, it's as though the internal format switch failed
-                }
-                break;
-              case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
-                if (!context->supportsDXT5Textures())
-                {
-                    return gl::error(GL_INVALID_ENUM); // in this case, it's as though the internal format switch failed
-                }
-                break;
-              default: UNREACHABLE();
-            }
-
-            if (imageSize != (GLsizei)gl::GetBlockSize(internalformat, GL_UNSIGNED_BYTE, context->getClientVersion(), width, height))
-            {
-                return gl::error(GL_INVALID_VALUE);
-            }
-
-            if (target == GL_TEXTURE_2D)
-            {
-                gl::Texture2D *texture = context->getTexture2D();
-
-                if (!texture)
-                {
-                    return gl::error(GL_INVALID_OPERATION);
-                }
-
-                if (texture->isImmutable())
-                {
-                    return gl::error(GL_INVALID_OPERATION);
-                }
-
-                texture->setCompressedImage(level, internalformat, width, height, imageSize, data);
-            }
-            else
-            {
-                gl::TextureCubeMap *texture = context->getTextureCubeMap();
-
-                if (!texture)
-                {
-                    return gl::error(GL_INVALID_OPERATION);
-                }
-
-                if (texture->isImmutable())
-                {
-                    return gl::error(GL_INVALID_OPERATION);
-                }
-
-                switch (target)
-                {
-                  case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
-                  case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
-                  case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
-                  case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
-                  case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
-                  case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
-                    texture->setCompressedImage(target, level, internalformat, width, height, imageSize, data);
-                    break;
-                  default: UNREACHABLE();
-                }
-            }
         }
-
     }
     catch(std::bad_alloc&)
     {
@@ -1760,94 +2266,52 @@
 
     try
     {
-        if (!gl::IsInternalTextureTarget(target))
-        {
-            return gl::error(GL_INVALID_ENUM);
-        }
-
-        if (xoffset < 0 || yoffset < 0 || !validImageSize(level, width, height, 1) || imageSize < 0)
-        {
-            return gl::error(GL_INVALID_VALUE);
-        }
-
-        switch (format)
-        {
-          case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
-          case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
-          case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
-          case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
-            break;
-          default:
-            return gl::error(GL_INVALID_ENUM);
-        }
-
-        if (width == 0 || height == 0 || data == NULL)
-        {
-            return;
-        }
-
         gl::Context *context = gl::getNonLostContext();
 
         if (context)
         {
-            if (level > context->getMaximumTextureLevel())
+            if (context->getClientVersion() < 3 &&
+                !validateES2TexImageParameters(context, target, level, GL_NONE, true, true,
+                                               xoffset, yoffset, width, height, 0, GL_NONE, GL_NONE, data))
+            {
+                return;
+            }
+
+            if (context->getClientVersion() >= 3 &&
+                !validateES3TexImageParameters(context, target, level, GL_NONE, true, true,
+                                               xoffset, yoffset, 0, width, height, 1, 0, GL_NONE, GL_NONE))
+            {
+                return;
+            }
+
+            if (imageSize < 0 || imageSize != (GLsizei)gl::GetBlockSize(format, GL_UNSIGNED_BYTE, context->getClientVersion(), width, height))
             {
                 return gl::error(GL_INVALID_VALUE);
             }
 
-            switch (format) {
-              case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
-              case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
-                if (!context->supportsDXT1Textures())
-                {
-                    return gl::error(GL_INVALID_ENUM); // in this case, it's as though the internal format switch failed
-                }
-                break;
-              case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
-                if (!context->supportsDXT3Textures())
-                {
-                    return gl::error(GL_INVALID_ENUM); // in this case, it's as though the internal format switch failed
-                }
-                break;
-              case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
-                if (!context->supportsDXT5Textures())
-                {
-                    return gl::error(GL_INVALID_ENUM); // in this case, it's as though the internal format switch failed
-                }
-                break;
-              default: UNREACHABLE();
-            }
-
-            if (imageSize != (GLsizei)gl::GetBlockSize(format, GL_UNSIGNED_BYTE, context->getClientVersion(), width, height))
+            switch (target)
             {
-                return gl::error(GL_INVALID_VALUE);
-            }
-
-            if (xoffset % 4 != 0 || yoffset % 4 != 0)
-            {
-                return gl::error(GL_INVALID_OPERATION); // we wait to check the offsets until this point, because the multiple-of-four restriction
-                                                    // does not exist unless DXT textures are supported.
-            }
-
-            if (target == GL_TEXTURE_2D)
-            {
-                gl::Texture2D *texture = context->getTexture2D();
-                if (validateSubImageParams2D(true, width, height, xoffset, yoffset, level, format, GL_UNSIGNED_BYTE, texture))
+              case GL_TEXTURE_2D:
                 {
+                    gl::Texture2D *texture = context->getTexture2D();
                     texture->subImageCompressed(level, xoffset, yoffset, width, height, format, imageSize, data);
                 }
-            }
-            else if (gl::IsCubemapTextureTarget(target))
-            {
-                gl::TextureCubeMap *texture = context->getTextureCubeMap();
-                if (validateSubImageParamsCube(true, width, height, xoffset, yoffset, target, level, format, GL_UNSIGNED_BYTE, texture))
+                break;
+
+              case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
+              case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
+              case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
+              case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
+              case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
+              case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
                 {
+                    gl::TextureCubeMap *texture = context->getTextureCubeMap();
                     texture->subImageCompressed(target, level, xoffset, yoffset, width, height, format, imageSize, data);
                 }
-            }
-            else
-            {
-                UNREACHABLE();
+                break;
+
+              default:
+                return gl::error(GL_INVALID_ENUM);
             }
         }
     }
@@ -1865,186 +2329,50 @@
 
     try
     {
-        if (!validImageSize(level, width, height, 1))
-        {
-            return gl::error(GL_INVALID_VALUE);
-        }
-
-        if (border != 0)
-        {
-            return gl::error(GL_INVALID_VALUE);
-        }
-
         gl::Context *context = gl::getNonLostContext();
 
         if (context)
         {
-            if (level > context->getMaximumTextureLevel())
+            if (context->getClientVersion() < 3 &&
+                !validateES2CopyTexImageParameters(context, target, level, internalformat, false,
+                                                   0, 0, x, y, width, height, border))
             {
-                return gl::error(GL_INVALID_VALUE);
+                return;
             }
 
+            if (context->getClientVersion() >= 3 &&
+                !validateES3CopyTexImageParameters(context, target, level, internalformat, false,
+                                                   0, 0, 0, x, y, width, height, border))
+            {
+                return;
+            }
+
+            gl::Framebuffer *framebuffer = context->getReadFramebuffer();
+
             switch (target)
             {
               case GL_TEXTURE_2D:
-                if (width > (context->getMaximum2DTextureDimension() >> level) ||
-                    height > (context->getMaximum2DTextureDimension() >> level))
                 {
-                    return gl::error(GL_INVALID_VALUE);
+                    gl::Texture2D *texture = context->getTexture2D();
+                    texture->copyImage(level, internalformat, x, y, width, height, framebuffer);
                 }
                 break;
+
               case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
               case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
               case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
               case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
               case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
               case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
-                if (width != height)
                 {
-                    return gl::error(GL_INVALID_VALUE);
-                }
-
-                if (width > (context->getMaximumCubeTextureDimension() >> level) ||
-                    height > (context->getMaximumCubeTextureDimension() >> level))
-                {
-                    return gl::error(GL_INVALID_VALUE);
+                    gl::TextureCubeMap *texture = context->getTextureCubeMap();
+                    texture->copyImage(target, level, internalformat, x, y, width, height, framebuffer);
                 }
                 break;
-              default:
+
+             default:
                 return gl::error(GL_INVALID_ENUM);
             }
-
-            gl::Framebuffer *framebuffer = context->getReadFramebuffer();
-
-            if (framebuffer->completeness() != GL_FRAMEBUFFER_COMPLETE)
-            {
-                return gl::error(GL_INVALID_FRAMEBUFFER_OPERATION);
-            }
-
-            if (context->getReadFramebufferHandle() != 0 && framebuffer->getSamples() != 0)
-            {
-                return gl::error(GL_INVALID_OPERATION);
-            }
-
-            gl::Renderbuffer *source = framebuffer->getReadColorbuffer();
-            GLenum colorbufferFormat = source->getInternalFormat();
-
-            // [OpenGL ES 2.0.24] table 3.9
-            switch (internalformat)
-            {
-              case GL_ALPHA:
-                if (colorbufferFormat != GL_ALPHA8_EXT &&
-                    colorbufferFormat != GL_RGBA4 &&
-                    colorbufferFormat != GL_RGB5_A1 &&
-                    colorbufferFormat != GL_BGRA8_EXT &&
-                    colorbufferFormat != GL_RGBA8_OES)
-                {
-                    return gl::error(GL_INVALID_OPERATION);
-                }
-                break;
-              case GL_LUMINANCE:
-              case GL_RGB:
-                if (colorbufferFormat != GL_RGB565 &&
-                    colorbufferFormat != GL_RGB8_OES &&
-                    colorbufferFormat != GL_RGBA4 &&
-                    colorbufferFormat != GL_RGB5_A1 &&
-                    colorbufferFormat != GL_BGRA8_EXT &&
-                    colorbufferFormat != GL_RGBA8_OES)
-                {
-                    return gl::error(GL_INVALID_OPERATION);
-                }
-                break;
-              case GL_LUMINANCE_ALPHA:
-              case GL_RGBA:
-                if (colorbufferFormat != GL_RGBA4 &&
-                    colorbufferFormat != GL_RGB5_A1 &&
-                    colorbufferFormat != GL_BGRA8_EXT &&
-                    colorbufferFormat != GL_RGBA8_OES)
-                 {
-                     return gl::error(GL_INVALID_OPERATION);
-                 }
-                 break;
-              case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
-              case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
-                if (context->supportsDXT1Textures())
-                {
-                    return gl::error(GL_INVALID_OPERATION);
-                }
-                else
-                {
-                    return gl::error(GL_INVALID_ENUM);
-                }
-                break;
-              case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
-                if (context->supportsDXT3Textures())
-                {
-                    return gl::error(GL_INVALID_OPERATION);
-                }
-                else
-                {
-                    return gl::error(GL_INVALID_ENUM);
-                }
-                break;
-              case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
-                if (context->supportsDXT5Textures())
-                {
-                    return gl::error(GL_INVALID_OPERATION);
-                }
-                else
-                {
-                    return gl::error(GL_INVALID_ENUM);
-                }
-                break;
-              case GL_DEPTH_COMPONENT:
-              case GL_DEPTH_COMPONENT16:
-              case GL_DEPTH_COMPONENT32_OES:
-              case GL_DEPTH_STENCIL_OES:
-              case GL_DEPTH24_STENCIL8_OES:
-                  if (context->supportsDepthTextures())
-                  {
-                      return gl::error(GL_INVALID_OPERATION);
-                  }
-                  else
-                  {
-                      return gl::error(GL_INVALID_ENUM);
-                  }
-              default:
-                return gl::error(GL_INVALID_ENUM);
-            }
-
-            if (target == GL_TEXTURE_2D)
-            {
-                gl::Texture2D *texture = context->getTexture2D();
-
-                if (!texture)
-                {
-                    return gl::error(GL_INVALID_OPERATION);
-                }
-
-                if (texture->isImmutable())
-                {
-                    return gl::error(GL_INVALID_OPERATION);
-                }
-
-                texture->copyImage(level, internalformat, x, y, width, height, framebuffer);
-            }
-            else if (gl::IsCubemapTextureTarget(target))
-            {
-                gl::TextureCubeMap *texture = context->getTextureCubeMap();
-
-                if (!texture)
-                {
-                    return gl::error(GL_INVALID_OPERATION);
-                }
-
-                if (texture->isImmutable())
-                {
-                    return gl::error(GL_INVALID_OPERATION);
-                }
-
-                texture->copyImage(target, level, internalformat, x, y, width, height, framebuffer);
-            }
-            else UNREACHABLE();
         }
     }
     catch(std::bad_alloc&)
@@ -2061,121 +2389,50 @@
 
     try
     {
-        if (!gl::IsInternalTextureTarget(target))
-        {
-            return gl::error(GL_INVALID_ENUM);
-        }
-
-        if (level < 0 || xoffset < 0 || yoffset < 0 || width < 0 || height < 0)
-        {
-            return gl::error(GL_INVALID_VALUE);
-        }
-
-        if (std::numeric_limits<GLsizei>::max() - xoffset < width || std::numeric_limits<GLsizei>::max() - yoffset < height)
-        {
-            return gl::error(GL_INVALID_VALUE);
-        }
-
-        if (width == 0 || height == 0)
-        {
-            return;
-        }
-
         gl::Context *context = gl::getNonLostContext();
 
         if (context)
         {
-            if (level > context->getMaximumTextureLevel())
+            if (context->getClientVersion() < 3 &&
+                !validateES2CopyTexImageParameters(context, target, level, GL_NONE, true,
+                                                   xoffset, yoffset, x, y, width, height, 0))
             {
-                return gl::error(GL_INVALID_VALUE);
+                return;
+            }
+
+            if (context->getClientVersion() >= 3 &&
+                !validateES3CopyTexImageParameters(context, target, level, GL_NONE, true,
+                                                   xoffset, yoffset, 0, x, y, width, height, 0))
+            {
+                return;
             }
 
             gl::Framebuffer *framebuffer = context->getReadFramebuffer();
 
-            if (framebuffer->completeness() != GL_FRAMEBUFFER_COMPLETE)
+            switch (target)
             {
-                return gl::error(GL_INVALID_FRAMEBUFFER_OPERATION);
-            }
-
-            if (context->getReadFramebufferHandle() != 0 && framebuffer->getSamples() != 0)
-            {
-                return gl::error(GL_INVALID_OPERATION);
-            }
-
-            gl::Renderbuffer *source = framebuffer->getReadColorbuffer();
-            GLenum colorbufferFormat = source->getInternalFormat();
-            gl::Texture *texture = NULL;
-            GLenum textureFormat = GL_RGBA;
-
-            if (target == GL_TEXTURE_2D)
-            {
-                gl::Texture2D *tex2d = context->getTexture2D();
-
-                if (!validateSubImageParams2D(false, width, height, xoffset, yoffset, level, GL_NONE, GL_NONE, tex2d))
+              case GL_TEXTURE_2D:
                 {
-                    return; // error already registered by validateSubImageParams
-                }
-                textureFormat = gl::GetFormat(tex2d->getInternalFormat(level), context->getClientVersion());
-                texture = tex2d;
-            }
-            else if (gl::IsCubemapTextureTarget(target))
-            {
-                gl::TextureCubeMap *texcube = context->getTextureCubeMap();
-
-                if (!validateSubImageParamsCube(false, width, height, xoffset, yoffset, target, level, GL_NONE, GL_NONE, texcube))
-                {
-                    return; // error already registered by validateSubImageParams
-                }
-                textureFormat = gl::GetFormat(texcube->getInternalFormat(target, level), context->getClientVersion());
-                texture = texcube;
-            }
-            else UNREACHABLE();
-
-            // [OpenGL ES 2.0.24] table 3.9
-            switch (textureFormat)
-            {
-              case GL_ALPHA:
-                if (colorbufferFormat != GL_ALPHA8_EXT &&
-                    colorbufferFormat != GL_RGBA4 &&
-                    colorbufferFormat != GL_RGB5_A1 &&
-                    colorbufferFormat != GL_RGBA8_OES)
-                {
-                    return gl::error(GL_INVALID_OPERATION);
+                    gl::Texture2D *texture = context->getTexture2D();
+                    texture->copySubImage(target, level, xoffset, yoffset, 0, x, y, width, height, framebuffer);
                 }
                 break;
-              case GL_LUMINANCE:
-              case GL_RGB:
-                if (colorbufferFormat != GL_RGB565 &&
-                    colorbufferFormat != GL_RGB8_OES &&
-                    colorbufferFormat != GL_RGBA4 &&
-                    colorbufferFormat != GL_RGB5_A1 &&
-                    colorbufferFormat != GL_RGBA8_OES)
+
+              case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
+              case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
+              case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
+              case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
+              case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
+              case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
                 {
-                    return gl::error(GL_INVALID_OPERATION);
+                    gl::TextureCubeMap *texture = context->getTextureCubeMap();
+                    texture->copySubImage(target, level, xoffset, yoffset, 0, x, y, width, height, framebuffer);
                 }
                 break;
-              case GL_LUMINANCE_ALPHA:
-              case GL_RGBA:
-                if (colorbufferFormat != GL_RGBA4 &&
-                    colorbufferFormat != GL_RGB5_A1 &&
-                    colorbufferFormat != GL_RGBA8_OES)
-                {
-                    return gl::error(GL_INVALID_OPERATION);
-                }
-                break;
-              case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
-              case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
-              case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
-              case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
-                return gl::error(GL_INVALID_OPERATION);
-              case GL_DEPTH_COMPONENT:
-              case GL_DEPTH_STENCIL_OES:
-                return gl::error(GL_INVALID_OPERATION);
+
               default:
-                return gl::error(GL_INVALID_OPERATION);
+                return gl::error(GL_INVALID_ENUM);
             }
-
-            texture->copySubImage(target, level, xoffset, yoffset, 0, x, y, width, height, framebuffer);
         }
     }
 
@@ -5985,277 +6242,69 @@
 
     try
     {
-        if (!validImageSize(level, width, height, 1))
-        {
-            return gl::error(GL_INVALID_VALUE);
-        }
-
-        if (internalformat != GLint(format))
-        {
-            return gl::error(GL_INVALID_OPERATION);
-        }
-
-        // validate <type> by itself (used as secondary key below)
-        switch (type)
-        {
-          case GL_UNSIGNED_BYTE:
-          case GL_UNSIGNED_SHORT_5_6_5:
-          case GL_UNSIGNED_SHORT_4_4_4_4:
-          case GL_UNSIGNED_SHORT_5_5_5_1:
-          case GL_UNSIGNED_SHORT:
-          case GL_UNSIGNED_INT:
-          case GL_UNSIGNED_INT_24_8_OES:
-          case GL_HALF_FLOAT_OES:
-          case GL_FLOAT:
-            break;
-          default:
-            return gl::error(GL_INVALID_ENUM);
-        }
-
-        // validate <format> + <type> combinations
-        // - invalid <format> -> sets INVALID_ENUM
-        // - invalid <format>+<type> combination -> sets INVALID_OPERATION
-        switch (format)
-        {
-          case GL_ALPHA:
-          case GL_LUMINANCE:
-          case GL_LUMINANCE_ALPHA:
-            switch (type)
-            {
-              case GL_UNSIGNED_BYTE:
-              case GL_FLOAT:
-              case GL_HALF_FLOAT_OES:
-                break;
-              default:
-                return gl::error(GL_INVALID_OPERATION);
-            }
-            break;
-          case GL_RGB:
-            switch (type)
-            {
-              case GL_UNSIGNED_BYTE:
-              case GL_UNSIGNED_SHORT_5_6_5:
-              case GL_FLOAT:
-              case GL_HALF_FLOAT_OES:
-                break;
-              default:
-                return gl::error(GL_INVALID_OPERATION);
-            }
-            break;
-          case GL_RGBA:
-            switch (type)
-            {
-              case GL_UNSIGNED_BYTE:
-              case GL_UNSIGNED_SHORT_4_4_4_4:
-              case GL_UNSIGNED_SHORT_5_5_5_1:
-              case GL_FLOAT:
-              case GL_HALF_FLOAT_OES:
-                break;
-              default:
-                return gl::error(GL_INVALID_OPERATION);
-            }
-            break;
-          case GL_BGRA_EXT:
-            switch (type)
-            {
-              case GL_UNSIGNED_BYTE:
-                break;
-              default:
-                return gl::error(GL_INVALID_OPERATION);
-            }
-            break;
-          case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:  // error cases for compressed textures are handled below
-          case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
-          case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
-          case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
-            break; 
-          case GL_DEPTH_COMPONENT:
-            switch (type)
-            {
-              case GL_UNSIGNED_SHORT:
-              case GL_UNSIGNED_INT:
-                break;
-              default:
-                return gl::error(GL_INVALID_OPERATION);
-            }
-            break;
-          case GL_DEPTH_STENCIL_OES:
-            switch (type)
-            {
-              case GL_UNSIGNED_INT_24_8_OES:
-                break;
-              default:
-                return gl::error(GL_INVALID_OPERATION);
-            }
-            break;
-          default:
-            return gl::error(GL_INVALID_ENUM);
-        }
-
-        if (border != 0)
-        {
-            return gl::error(GL_INVALID_VALUE);
-        }
-
         gl::Context *context = gl::getNonLostContext();
 
         if (context)
         {
-            if (level > context->getMaximumTextureLevel())
+            if (context->getClientVersion() < 3 &&
+                !validateES2TexImageParameters(context, target, level, internalformat, false, false,
+                                               0, 0, width, height, border, format, type, pixels))
             {
-                return gl::error(GL_INVALID_VALUE);
+                return;
+            }
+
+            if (context->getClientVersion() >= 3 &&
+                !validateES3TexImageParameters(context, target, level, internalformat, false, false,
+                                               0, 0, 0, width, height, 1, border, format, type))
+            {
+                return;
             }
 
             switch (target)
             {
               case GL_TEXTURE_2D:
-                if (width > (context->getMaximum2DTextureDimension() >> level) ||
-                    height > (context->getMaximum2DTextureDimension() >> level))
                 {
-                    return gl::error(GL_INVALID_VALUE);
+                    gl::Texture2D *texture = context->getTexture2D();
+                    texture->setImage(level, width, height, internalformat, format, type, context->getUnpackAlignment(), pixels);
                 }
                 break;
               case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
-              case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
-              case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
-              case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
-              case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
-              case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
-                if (width != height)
                 {
-                    return gl::error(GL_INVALID_VALUE);
-                }
-
-                if (width > (context->getMaximumCubeTextureDimension() >> level) ||
-                    height > (context->getMaximumCubeTextureDimension() >> level))
-                {
-                    return gl::error(GL_INVALID_VALUE);
-                }
-                break;
-              default:
-                return gl::error(GL_INVALID_ENUM);
-            }
-
-            switch (format) {
-              case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
-              case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
-                if (context->supportsDXT1Textures())
-                {
-                    return gl::error(GL_INVALID_OPERATION);
-                }
-                else
-                {
-                    return gl::error(GL_INVALID_ENUM);
-                }
-                break;
-              case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
-                if (context->supportsDXT3Textures())
-                {
-                    return gl::error(GL_INVALID_OPERATION);
-                }
-                else
-                {
-                    return gl::error(GL_INVALID_ENUM);
-                }
-                break;
-              case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
-                if (context->supportsDXT5Textures())
-                {
-                    return gl::error(GL_INVALID_OPERATION);
-                }
-                else
-                {
-                    return gl::error(GL_INVALID_ENUM);
-                }
-                break;
-              case GL_DEPTH_COMPONENT:
-              case GL_DEPTH_STENCIL_OES:
-                if (!context->supportsDepthTextures())
-                {
-                    return gl::error(GL_INVALID_VALUE);
-                }
-                if (target != GL_TEXTURE_2D)
-                {
-                    return gl::error(GL_INVALID_OPERATION);
-                }
-                // OES_depth_texture supports loading depth data and multiple levels,
-                // but ANGLE_depth_texture does not
-                if (pixels != NULL || level != 0)
-                {
-                    return gl::error(GL_INVALID_OPERATION);
-                }
-                break;
-              default:
-                break;
-            }
-
-            if (type == GL_FLOAT)
-            {
-                if (!context->supportsFloat32Textures())
-                {
-                    return gl::error(GL_INVALID_ENUM);
-                }
-            }
-            else if (type == GL_HALF_FLOAT_OES)
-            {
-                if (!context->supportsFloat16Textures())
-                {
-                    return gl::error(GL_INVALID_ENUM);
-                }
-            }
-
-            if (target == GL_TEXTURE_2D)
-            {
-                gl::Texture2D *texture = context->getTexture2D();
-
-                if (!texture)
-                {
-                    return gl::error(GL_INVALID_OPERATION);
-                }
-
-                if (texture->isImmutable())
-                {
-                    return gl::error(GL_INVALID_OPERATION);
-                }
-
-                texture->setImage(level, width, height, internalformat, format, type, context->getUnpackAlignment(), pixels);
-            }
-            else
-            {
-                gl::TextureCubeMap *texture = context->getTextureCubeMap();
-
-                if (!texture)
-                {
-                    return gl::error(GL_INVALID_OPERATION);
-                }
-
-                if (texture->isImmutable())
-                {
-                    return gl::error(GL_INVALID_OPERATION);
-                }
-
-                switch (target)
-                {
-                  case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
+                    gl::TextureCubeMap *texture = context->getTextureCubeMap();
                     texture->setImagePosX(level, width, height, internalformat, format, type, context->getUnpackAlignment(), pixels);
-                    break;
-                  case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
-                    texture->setImageNegX(level, width, height, internalformat, format, type, context->getUnpackAlignment(), pixels);
-                    break;
-                  case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
-                    texture->setImagePosY(level, width, height, internalformat, format, type, context->getUnpackAlignment(), pixels);
-                    break;
-                  case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
-                    texture->setImageNegY(level, width, height, internalformat, format, type, context->getUnpackAlignment(), pixels);
-                    break;
-                  case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
-                    texture->setImagePosZ(level, width, height, internalformat, format, type, context->getUnpackAlignment(), pixels);
-                    break;
-                  case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
-                    texture->setImageNegZ(level, width, height, internalformat, format, type, context->getUnpackAlignment(), pixels);
-                    break;
-                  default: UNREACHABLE();
                 }
+                break;
+              case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
+                {
+                    gl::TextureCubeMap *texture = context->getTextureCubeMap();
+                    texture->setImageNegX(level, width, height, internalformat, format, type, context->getUnpackAlignment(), pixels);
+                }
+                break;
+              case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
+                {
+                    gl::TextureCubeMap *texture = context->getTextureCubeMap();
+                    texture->setImagePosY(level, width, height, internalformat, format, type, context->getUnpackAlignment(), pixels);
+                }
+                break;
+              case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
+                {
+                    gl::TextureCubeMap *texture = context->getTextureCubeMap();
+                    texture->setImageNegY(level, width, height, internalformat, format, type, context->getUnpackAlignment(), pixels);
+                }
+                break;
+              case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
+                {
+                    gl::TextureCubeMap *texture = context->getTextureCubeMap();
+                    texture->setImagePosZ(level, width, height, internalformat, format, type, context->getUnpackAlignment(), pixels);
+                }
+                break;
+              case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
+                {
+                    gl::TextureCubeMap *texture = context->getTextureCubeMap();
+                    texture->setImageNegZ(level, width, height, internalformat, format, type, context->getUnpackAlignment(), pixels);
+                }
+                break;
+              default: UNREACHABLE();
             }
         }
     }
@@ -6653,88 +6702,47 @@
 
     try
     {
-        if (!gl::IsInternalTextureTarget(target))
-        {
-            return gl::error(GL_INVALID_ENUM);
-        }
-
-        if (level < 0 || xoffset < 0 || yoffset < 0 || width < 0 || height < 0)
-        {
-            return gl::error(GL_INVALID_VALUE);
-        }
-
-        if (std::numeric_limits<GLsizei>::max() - xoffset < width || std::numeric_limits<GLsizei>::max() - yoffset < height)
-        {
-            return gl::error(GL_INVALID_VALUE);
-        }
-
-        if (!checkTextureFormatType(format, type))
-        {
-            return; // error is set by helper function
-        }
-
         gl::Context *context = gl::getNonLostContext();
 
         if (context)
         {
-            if (level > context->getMaximumTextureLevel())
-            {
-                return gl::error(GL_INVALID_VALUE);
-            }
-
-            if (type == GL_FLOAT)
-            {
-                if (!context->supportsFloat32Textures())
-                {
-                    return gl::error(GL_INVALID_ENUM);
-                }
-            }
-            else if (type == GL_HALF_FLOAT_OES)
-            {
-                if (!context->supportsFloat16Textures())
-                {
-                    return gl::error(GL_INVALID_ENUM);
-                }
-            }
-
-            if (format == GL_DEPTH_COMPONENT)
-            {
-                if (!context->supportsDepthTextures())
-                {
-                    return gl::error(GL_INVALID_ENUM);
-                }
-                if (target != GL_TEXTURE_2D)
-                {
-                    return gl::error(GL_INVALID_OPERATION);
-                }
-                // OES_depth_texture supports loading depth data, but ANGLE_depth_texture does not
-                return gl::error(GL_INVALID_OPERATION);
-            }
-
-            if (width == 0 || height == 0 || pixels == NULL)
+            if (context->getClientVersion() < 3 &&
+                !validateES2TexImageParameters(context, target, level, GL_NONE, false, true,
+                                               0, 0, width, height, 0, format, type, pixels))
             {
                 return;
             }
 
-            if (target == GL_TEXTURE_2D)
+            if (context->getClientVersion() >= 3 &&
+                !validateES3TexImageParameters(context, target, level, GL_NONE, false, true,
+                                               0, 0, 0, width, height, 1, 0, format, type))
             {
-                gl::Texture2D *texture = context->getTexture2D();
-                if (validateSubImageParams2D(false, width, height, xoffset, yoffset, level, format, type, texture))
+                return;
+            }
+
+            switch (target)
+            {
+              case GL_TEXTURE_2D:
                 {
+                    gl::Texture2D *texture = context->getTexture2D();
                     texture->subImage(level, xoffset, yoffset, width, height, format, type, context->getUnpackAlignment(), pixels);
                 }
-            }
-            else if (gl::IsCubemapTextureTarget(target))
-            {
-                gl::TextureCubeMap *texture = context->getTextureCubeMap();
-                if (validateSubImageParamsCube(false, width, height, xoffset, yoffset, target, level, format, type, texture))
+                break;
+
+              case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
+              case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
+              case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
+              case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
+              case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
+              case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
                 {
+                    gl::TextureCubeMap *texture = context->getTextureCubeMap();
                     texture->subImage(target, level, xoffset, yoffset, width, height, format, type, context->getUnpackAlignment(), pixels);
                 }
-            }
-            else
-            {
-                UNREACHABLE();
+                break;
+
+              default:
+                return gl::error(GL_INVALID_ENUM);
             }
         }
     }