Implemented 3D texture entry points.

TRAC #22705

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

git-svn-id: https://angleproject.googlecode.com/svn/branches/es3proto@2172 736b8ea6-26fd-11df-bfd4-992fa37f6226
diff --git a/src/libGLESv2/libGLESv2.cpp b/src/libGLESv2/libGLESv2.cpp
index 2ff8689..77a0855 100644
--- a/src/libGLESv2/libGLESv2.cpp
+++ b/src/libGLESv2/libGLESv2.cpp
@@ -222,45 +222,350 @@
     return true;
 }
 
-bool validateSubImageParams3D(bool compressed, GLsizei width, GLsizei height, GLsizei depth,
-                              GLint xoffset, GLint yoffset,GLint zoffset, GLint level, GLenum format, GLenum type,
-                              gl::Texture3D *texture)
+bool validateES3TexImageFormat(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 )
+    {
+        return gl::error(GL_INVALID_VALUE, false);
+    }
+
+    if (isCompressed)
+    {
+        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);
+        }
+    }
+
+    // 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::Texture *texture = NULL;
+    bool textureCompressed = false;
+    GLenum textureInternalFormat = GL_NONE;
+    GLint textureLevelWidth = 0;
+    GLint textureLevelHeight = 0;
+    GLint textureLevelDepth = 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 *texture2d = context->getTexture2D();
+            if (texture2d)
+            {
+                textureCompressed = texture2d->isCompressed(level);
+                textureInternalFormat = texture2d->getInternalFormat(level);
+                textureLevelWidth = texture2d->getWidth(level);
+                textureLevelHeight = texture2d->getHeight(level);
+                textureLevelDepth = 1;
+                texture = texture2d;
+            }
+        }
+        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, false);
+            }
+
+            if (width > (context->getMaximumCubeTextureDimension() >> level))
+            {
+                return gl::error(GL_INVALID_VALUE, false);
+            }
+
+            gl::TextureCubeMap *textureCube = context->getTextureCubeMap();
+            if (textureCube)
+            {
+                textureCompressed = textureCube->isCompressed(target, level);
+                textureInternalFormat = textureCube->getInternalFormat(target, level);
+                textureLevelWidth = textureCube->getWidth(target, level);
+                textureLevelHeight = textureCube->getHeight(target, level);
+                textureLevelDepth = 1;
+                texture = textureCube;
+            }
+        }
+        break;
+
+      case GL_TEXTURE_3D:
+        {
+            if (width > (context->getMaximum3DTextureDimension() >> level) ||
+                height > (context->getMaximum3DTextureDimension() >> level) ||
+                depth > (context->getMaximum3DTextureDimension() >> level))
+            {
+                return gl::error(GL_INVALID_VALUE, false);
+            }
+
+            gl::Texture3D *texture3d = context->getTexture3D();
+            if (texture3d)
+            {
+                textureCompressed = texture3d->isCompressed(level);
+                textureInternalFormat = texture3d->getInternalFormat(level);
+                textureLevelWidth = texture3d->getWidth(level);
+                textureLevelHeight = texture3d->getHeight(level);
+                textureLevelDepth = texture3d->getDepth(level);
+                texture = texture3d;
+            }
+        }
+        break;
+
+      default:
+        return gl::error(GL_INVALID_ENUM, false);
+    }
+
     if (!texture)
     {
         return gl::error(GL_INVALID_OPERATION, false);
     }
 
-    if (compressed != texture->isCompressed(level))
+    if (texture->isImmutable())
     {
         return gl::error(GL_INVALID_OPERATION, false);
     }
 
-    if (format != GL_NONE)
+    // Validate texture formats
+    GLenum actualInternalFormat = isSubImage ? textureInternalFormat : internalformat;
+    if (isCompressed)
     {
-        GLenum internalformat = gl::ConvertSizedInternalFormat(format, type);
-        if (internalformat != texture->getInternalFormat(level))
+        if (!gl::IsValidES3CompressedFormat(actualInternalFormat))
+        {
+            return gl::error(GL_INVALID_ENUM, false);
+        }
+
+        if (target == GL_TEXTURE_3D)
+        {
+            return gl::error(GL_INVALID_OPERATION, false);
+        }
+    }
+    else
+    {
+        GLenum err;
+        if (!gl::IsValidES3FormatCombination(actualInternalFormat, format, type, &err))
+        {
+            return gl::error(err, false);
+        }
+
+        if ((target == GL_TEXTURE_3D || target == GL_TEXTURE_2D_ARRAY) &&
+            (format == GL_DEPTH_COMPONENT || format == GL_DEPTH_STENCIL))
         {
             return gl::error(GL_INVALID_OPERATION, false);
         }
     }
 
-    if (compressed)
+    // Validate sub image parameters
+    if (isSubImage)
     {
-        if ((width % 4 != 0 && width != texture->getWidth(0)) ||
-            (height % 4 != 0 && height != texture->getHeight(0)))
+        if (isCompressed != textureCompressed)
         {
             return gl::error(GL_INVALID_OPERATION, false);
         }
+
+        if (format != GL_NONE)
+        {
+            GLenum internalformat = gl::ConvertSizedInternalFormat(format, type);
+            if (internalformat != textureInternalFormat)
+            {
+                return gl::error(GL_INVALID_OPERATION, false);
+            }
+        }
+
+        if (isCompressed)
+        {
+            if ((width % 4 != 0 && width != textureLevelWidth) ||
+                (height % 4 != 0 && height != textureLevelHeight))
+            {
+                return gl::error(GL_INVALID_OPERATION, false);
+            }
+        }
+
+        if (width == 0 || height == 0 || depth == 0)
+        {
+            return false;
+        }
+
+        if (xoffset < 0 || yoffset < 0 || zoffset < 0)
+        {
+            return gl::error(GL_INVALID_VALUE, false);
+        }
+
+        if (std::numeric_limits<GLsizei>::max() - xoffset < width ||
+            std::numeric_limits<GLsizei>::max() - yoffset < height ||
+            std::numeric_limits<GLsizei>::max() - zoffset < depth)
+        {
+            return gl::error(GL_INVALID_VALUE, false);
+        }
+
+        if (xoffset + width > textureLevelWidth ||
+            yoffset + height > textureLevelHeight ||
+            zoffset + depth > textureLevelDepth)
+        {
+            return gl::error(GL_INVALID_VALUE, false);
+        }
     }
 
-    if (xoffset + width > texture->getWidth(level) ||
-        yoffset + height > texture->getHeight(level) ||
-        zoffset + depth > texture->getDepth(level))
+    return true;
+}
+
+bool validateCopyTexImageParameters(gl::Context *context, GLenum target, bool isCompressed, GLint level,
+                                    GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y,
+                                    GLsizei width, GLsizei height)
+{
+    if (level < 0 || xoffset < 0 || yoffset < 0 || zoffset < 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;
+    }
+
+    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);
+    }
+
+    gl::Renderbuffer *source = framebuffer->getReadColorbuffer();
+    GLenum colorbufferFormat = source->getInternalFormat();
+    gl::Texture *texture = NULL;
+    GLenum textureFormat = GL_RGBA;
+    bool textureCompressed = false;
+    GLint textureLevelWidth = 0;
+    GLint textureLevelHeight = 0;
+    GLint textureLevelDepth = 0;
+    switch (target)
+    {
+      case GL_TEXTURE_2D:
+        {
+            gl::Texture2D *texture2d = context->getTexture2D();
+            if (texture2d)
+            {
+                textureFormat = gl::ExtractFormat(texture2d->getInternalFormat(level));
+                textureCompressed = texture2d->isCompressed(level);
+                textureLevelWidth = texture2d->getWidth(level);
+                textureLevelHeight = texture2d->getHeight(level);
+                textureLevelDepth = 1;
+                texture = texture2d;
+            }
+        }
+        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 *textureCube = context->getTextureCubeMap();
+            if (textureCube)
+            {
+                textureFormat = gl::ExtractFormat(textureCube->getInternalFormat(target, level));
+                textureCompressed = textureCube->isCompressed(target, level);
+                textureLevelWidth = textureCube->getWidth(target, level);
+                textureLevelHeight = textureCube->getHeight(target, level);
+                textureLevelDepth = 1;
+                texture = textureCube;
+            }
+        }
+        break;
+
+      case GL_TEXTURE_3D:
+        {
+            gl::Texture3D *texture3d = context->getTexture3D();
+            if (texture3d)
+            {
+                textureFormat = gl::ExtractFormat(texture3d->getInternalFormat(level));
+                textureCompressed = texture3d->isCompressed(level);
+                textureLevelWidth = texture3d->getWidth(level);
+                textureLevelHeight = texture3d->getHeight(level);
+                textureLevelDepth = texture3d->getDepth(level);
+                texture = texture3d;
+            }
+        }
+        break;
+
+      default:
+        return gl::error(GL_INVALID_ENUM, false);
+    }
+
+    if (!texture)
+    {
+        return gl::error(GL_INVALID_OPERATION, false);
+    }
+
+    if (isCompressed != textureCompressed)
+    {
+        return gl::error(GL_INVALID_OPERATION, false);
+    }
+
+    if (isCompressed)
+    {
+        if ((width % 4 != 0 && width != textureLevelWidth) ||
+            (height % 4 != 0 && height != textureLevelHeight))
+        {
+            return gl::error(GL_INVALID_OPERATION, false);
+        }
+    }
+
+    if (xoffset + width > textureLevelWidth ||
+        yoffset + height > textureLevelHeight ||
+        zoffset >= textureLevelDepth)
+    {
+        return gl::error(GL_INVALID_VALUE, false);
+    }
+
+    if (!gl::IsValidES3CopyTexImageCombination(textureFormat, colorbufferFormat))
+    {
+        return gl::error(GL_INVALID_OPERATION, false);
+    }
+
     return true;
 }
 
@@ -7172,9 +7477,31 @@
             {
                 return gl::error(GL_INVALID_OPERATION);
             }
-        }
 
-        UNIMPLEMENTED();
+            // validateES3TexImageFormat sets the error code if there is an error
+            if (!validateES3TexImageFormat(context, target, level, internalformat, false, false,
+                                               0, 0, 0, width, height, depth, border, format, type))
+            {
+                return;
+            }
+
+            switch(target)
+            {
+              case GL_TEXTURE_3D:
+                {
+                    gl::Texture3D *texture = context->getTexture3D();
+                    texture->setImage(level, width, height, depth, format, type, context->getUnpackAlignment(), pixels);
+                }
+                break;
+
+              case GL_TEXTURE_2D_ARRAY:
+                UNIMPLEMENTED();
+                break;
+
+              default:
+                return gl::error(GL_INVALID_ENUM);
+            }
+        }
     }
     catch(std::bad_alloc&)
     {
@@ -7199,9 +7526,37 @@
             {
                 return gl::error(GL_INVALID_OPERATION);
             }
-        }
 
-        UNIMPLEMENTED();
+            if (!pixels)
+            {
+                return gl::error(GL_INVALID_VALUE);
+            }
+
+            // validateES3TexImageFormat sets the error code if there is an error
+            if (!validateES3TexImageFormat(context, target, level, GL_NONE, false, true,
+                                               xoffset, yoffset, zoffset, width, height, depth, 0,
+                                               format, type))
+            {
+                return;
+            }
+
+            switch(target)
+            {
+              case GL_TEXTURE_3D:
+                {
+                    gl::Texture3D *texture = context->getTexture3D();
+                    texture->subImage(level, xoffset, yoffset, zoffset, width, height, depth, format, type, context->getUnpackAlignment(), pixels);
+                }
+                break;
+
+              case GL_TEXTURE_2D_ARRAY:
+                UNIMPLEMENTED();
+                break;
+
+              default:
+                return gl::error(GL_INVALID_ENUM);
+            }
+        }
     }
     catch(std::bad_alloc&)
     {
@@ -7225,9 +7580,31 @@
             {
                 return gl::error(GL_INVALID_OPERATION);
             }
-        }
 
-        UNIMPLEMENTED();
+            if (!validateCopyTexImageParameters(context, target, false, level, xoffset, yoffset, zoffset,
+                                                x, y, width, height))
+            {
+                return;
+            }
+
+            gl::Framebuffer *framebuffer = context->getReadFramebuffer();
+            gl::Texture *texture = NULL;
+            switch (target)
+            {
+              case GL_TEXTURE_3D:
+                texture = context->getTexture3D();
+                break;
+
+              case GL_TEXTURE_2D_ARRAY:
+                UNIMPLEMENTED();
+                break;
+
+              default:
+                return gl::error(GL_INVALID_ENUM);
+            }
+
+            texture->copySubImage(target, level, xoffset, yoffset, zoffset, x, y, width, height, framebuffer);
+        }
     }
     catch(std::bad_alloc&)
     {
@@ -7252,9 +7629,36 @@
             {
                 return gl::error(GL_INVALID_OPERATION);
             }
-        }
 
-        UNIMPLEMENTED();
+            if (imageSize < 0 || imageSize != gl::ComputeCompressedSize(width, height, internalformat))
+            {
+                return gl::error(GL_INVALID_VALUE);
+            }
+
+            // validateES3TexImageFormat sets the error code if there is an error
+            if (!validateES3TexImageFormat(context, target, level, internalformat, true, false,
+                                           0, 0, 0, width, height, depth, border, GL_NONE, GL_NONE))
+            {
+                return;
+            }
+
+            switch(target)
+            {
+              case GL_TEXTURE_3D:
+                {
+                    gl::Texture3D *texture = context->getTexture3D();
+                    texture->setCompressedImage(level, internalformat, width, height, depth, imageSize, data);
+                }
+                break;
+
+              case GL_TEXTURE_2D_ARRAY:
+                UNIMPLEMENTED();
+                break;
+
+              default:
+                return gl::error(GL_INVALID_ENUM);
+            }
+        }
     }
     catch(std::bad_alloc&)
     {
@@ -7279,9 +7683,42 @@
             {
                 return gl::error(GL_INVALID_OPERATION);
             }
-        }
 
-        UNIMPLEMENTED();
+            if (imageSize < 0 || imageSize != gl::ComputeCompressedSize(width, height, format))
+            {
+                return gl::error(GL_INVALID_VALUE);
+            }
+
+            if (!data)
+            {
+                return gl::error(GL_INVALID_VALUE);
+            }
+
+            // validateES3TexImageFormat sets the error code if there is an error
+            if (!validateES3TexImageFormat(context, target, level, GL_NONE, true, true,
+                                           0, 0, 0, width, height, depth, 0, GL_NONE, GL_NONE))
+            {
+                return;
+            }
+
+            switch(target)
+            {
+              case GL_TEXTURE_3D:
+                {
+                    gl::Texture3D *texture = context->getTexture3D();
+                    texture->subImageCompressed(level, xoffset, yoffset, zoffset, width, height, depth,
+                                                format, imageSize, data);
+                }
+                break;
+
+              case GL_TEXTURE_2D_ARRAY:
+                UNIMPLEMENTED();
+                break;
+
+            default:
+                return gl::error(GL_INVALID_ENUM);
+            }
+        }
     }
     catch(std::bad_alloc&)
     {