Edit

kc3-lang/angle/src/libGLESv2/validationES3.cpp

Branch :

  • Show log

    Commit

  • Author : Shannon Woods
    Date : 2014-03-17 20:03:39
    Hash : 4dfed837
    Message : Validate target parameter in CopyTexImage calls before using to check mip level Would cause UNREACHABLE to be triggered if target was invalid, and INVALID_VALUE to be returned instead of INVALID_ENUM if it were the only erroneous argument. Change-Id: I2606e77379fa2832a50b3a299e6474a4b2f68afa Reviewed-on: https://chromium-review.googlesource.com/189701 Reviewed-by: Geoff Lang <geofflang@chromium.org> Reviewed-by: Jamie Madill <jmadill@chromium.org> Tested-by: Shannon Woods <shannonwoods@chromium.org>

  • src/libGLESv2/validationES3.cpp
  • #include "precompiled.h"
    //
    // Copyright (c) 2013 The ANGLE Project Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style license that can be
    // found in the LICENSE file.
    //
    
    // validationES3.cpp: Validation functions for OpenGL ES 3.0 entry point parameters
    
    #include "libGLESv2/validationES3.h"
    #include "libGLESv2/validationES.h"
    #include "libGLESv2/Context.h"
    #include "libGLESv2/Texture.h"
    #include "libGLESv2/Framebuffer.h"
    #include "libGLESv2/Renderbuffer.h"
    #include "libGLESv2/formatutils.h"
    #include "libGLESv2/main.h"
    
    #include "common/mathutil.h"
    
    namespace gl
    {
    
    bool ValidateES3TexImageParameters(gl::Context *context, GLenum target, GLint level, GLenum internalformat, bool isCompressed, bool isSubImage,
                                       GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth,
                                       GLint border, GLenum format, GLenum type, const GLvoid *pixels)
    {
        // Validate image size
        if (!ValidImageSize(context, target, level, width, height, depth))
        {
            return gl::error(GL_INVALID_VALUE, false);
        }
    
        // Verify zero border
        if (border != 0)
        {
            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 (!isSubImage && 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;
    
            case GL_TEXTURE_2D_ARRAY:
              {
                  if (width > (context->getMaximum2DTextureDimension() >> level) ||
                      height > (context->getMaximum2DTextureDimension() >> level) ||
                      depth > (context->getMaximum2DArrayTextureLayers() >> level))
                  {
                      return gl::error(GL_INVALID_VALUE, false);
                  }
    
                  gl::Texture2DArray *texture2darray = context->getTexture2DArray();
                  if (texture2darray)
                  {
                      textureCompressed = texture2darray->isCompressed(level);
                      textureInternalFormat = texture2darray->getInternalFormat(level);
                      textureLevelWidth = texture2darray->getWidth(level);
                      textureLevelHeight = texture2darray->getHeight(level);
                      textureLevelDepth = texture2darray->getLayers(level);
                      texture = texture2darray;
                  }
              }
              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);
        }
    
        // Validate texture formats
        GLenum actualInternalFormat = isSubImage ? textureInternalFormat : internalformat;
        if (isCompressed)
        {
            if (!ValidCompressedImageSize(context, actualInternalFormat, width, height))
            {
                return gl::error(GL_INVALID_OPERATION, false);
            }
    
            if (!gl::IsFormatCompressed(actualInternalFormat, context->getClientVersion()))
            {
                return gl::error(GL_INVALID_ENUM, false);
            }
    
            if (target == GL_TEXTURE_3D)
            {
                return gl::error(GL_INVALID_OPERATION, false);
            }
        }
        else
        {
            if (!gl::IsValidInternalFormat(actualInternalFormat, context) ||
                !gl::IsValidFormat(format, context->getClientVersion()) ||
                !gl::IsValidType(type, context->getClientVersion()))
            {
                return gl::error(GL_INVALID_ENUM, false);
            }
    
            if (!gl::IsValidFormatCombination(actualInternalFormat, format, type, context->getClientVersion()))
            {
                return gl::error(GL_INVALID_OPERATION, false);
            }
    
            if (target == GL_TEXTURE_3D && (format == GL_DEPTH_COMPONENT || format == GL_DEPTH_STENCIL))
            {
                return gl::error(GL_INVALID_OPERATION, false);
            }
        }
    
        // Validate sub image parameters
        if (isSubImage)
        {
            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 (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);
            }
        }
    
        // Check for pixel unpack buffer related API errors
        gl::Buffer *pixelUnpackBuffer = context->getPixelUnpackBuffer();
        if (pixelUnpackBuffer != NULL)
        {
            // ...the data would be unpacked from the buffer object such that the memory reads required
            // would exceed the data store size.
            size_t widthSize = static_cast<size_t>(width);
            size_t heightSize = static_cast<size_t>(height);
            size_t depthSize = static_cast<size_t>(depth);
            size_t pixelBytes = static_cast<size_t>(gl::GetPixelBytes(actualInternalFormat, context->getClientVersion()));
    
            if (!rx::IsUnsignedMultiplicationSafe(widthSize, heightSize) ||
                !rx::IsUnsignedMultiplicationSafe(widthSize * heightSize, depthSize) ||
                !rx::IsUnsignedMultiplicationSafe(widthSize * heightSize * depthSize, pixelBytes))
            {
                // Overflow past the end of the buffer
                return gl::error(GL_INVALID_OPERATION, false);
            }
    
            size_t copyBytes = widthSize * heightSize * depthSize * pixelBytes;
            size_t offset = reinterpret_cast<size_t>(pixels);
    
            if (!rx::IsUnsignedAdditionSafe(offset, copyBytes) || ((offset + copyBytes) > static_cast<size_t>(pixelUnpackBuffer->size())))
            {
                // Overflow past the end of the buffer
                return gl::error(GL_INVALID_OPERATION, false);
            }
    
            // ...data is not evenly divisible into the number of bytes needed to store in memory a datum
            // indicated by type.
            size_t dataBytesPerPixel = static_cast<size_t>(gl::GetTypeBytes(type));
    
            if ((offset % dataBytesPerPixel) != 0)
            {
                return gl::error(GL_INVALID_OPERATION, false);
            }
    
            // ...the buffer object's data store is currently mapped.
            if (pixelUnpackBuffer->mapped())
            {
                return gl::error(GL_INVALID_OPERATION, 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)
    {
        if (!ValidTexture2DDestinationTarget(context, target))
        {
            return gl::error(GL_INVALID_ENUM, false);
        }
    
        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 (border != 0)
        {
            return gl::error(GL_INVALID_VALUE, false);
        }
    
        if (!ValidMipLevel(context, target, level))
        {
            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 colorbufferInternalFormat = source->getInternalFormat();
        gl::Texture *texture = NULL;
        GLenum textureInternalFormat = GL_NONE;
        bool textureCompressed = false;
        bool textureIsDepth = false;
        GLint textureLevelWidth = 0;
        GLint textureLevelHeight = 0;
        GLint textureLevelDepth = 0;
        switch (target)
        {
          case GL_TEXTURE_2D:
            {
                gl::Texture2D *texture2d = context->getTexture2D();
                if (texture2d)
                {
                    textureInternalFormat = texture2d->getInternalFormat(level);
                    textureCompressed = texture2d->isCompressed(level);
                    textureIsDepth = texture2d->isDepth(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)
                {
                    textureInternalFormat = textureCube->getInternalFormat(target, level);
                    textureCompressed = textureCube->isCompressed(target, level);
                    textureIsDepth = false;
                    textureLevelWidth = textureCube->getWidth(target, level);
                    textureLevelHeight = textureCube->getHeight(target, level);
                    textureLevelDepth = 1;
                    texture = textureCube;
                }
            }
            break;
    
          case GL_TEXTURE_2D_ARRAY:
            {
                gl::Texture2DArray *texture2dArray = context->getTexture2DArray();
                if (texture2dArray)
                {
                    textureInternalFormat = texture2dArray->getInternalFormat(level);
                    textureCompressed = texture2dArray->isCompressed(level);
                    textureIsDepth = texture2dArray->isDepth(level);
                    textureLevelWidth = texture2dArray->getWidth(level);
                    textureLevelHeight = texture2dArray->getHeight(level);
                    textureLevelDepth = texture2dArray->getLayers(level);
                    texture = texture2dArray;
                }
            }
            break;
    
          case GL_TEXTURE_3D:
            {
                gl::Texture3D *texture3d = context->getTexture3D();
                if (texture3d)
                {
                    textureInternalFormat = texture3d->getInternalFormat(level);
                    textureCompressed = texture3d->isCompressed(level);
                    textureIsDepth = texture3d->isDepth(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 (texture->isImmutable() && !isSubImage)
        {
            return gl::error(GL_INVALID_OPERATION, false);
        }
    
        if (textureIsDepth)
        {
            return gl::error(GL_INVALID_OPERATION, false);
        }
    
        if (textureCompressed)
        {
            if ((width % 4 != 0 && width != textureLevelWidth) ||
                (height % 4 != 0 && height != textureLevelHeight))
            {
                return gl::error(GL_INVALID_OPERATION, false);
            }
        }
    
        if (isSubImage)
        {
            if (xoffset + width > textureLevelWidth ||
                yoffset + height > textureLevelHeight ||
                zoffset >= textureLevelDepth)
            {
                return gl::error(GL_INVALID_VALUE, false);
            }
    
            if (!gl::IsValidCopyTexImageCombination(textureInternalFormat, colorbufferInternalFormat, context->getReadFramebufferHandle(),
                                                    context->getClientVersion()))
            {
                return gl::error(GL_INVALID_OPERATION, false);
            }
        }
        else
        {
            if (!gl::IsValidCopyTexImageCombination(internalformat, colorbufferInternalFormat, context->getReadFramebufferHandle(),
                                                    context->getClientVersion()))
            {
                return gl::error(GL_INVALID_OPERATION, false);
            }
        }
    
    
    
        // If width or height is zero, it is a no-op.  Return false without setting an error.
        return (width > 0 && height > 0);
    }
    
    bool ValidateES3TexStorageParameters(gl::Context *context, GLenum target, GLsizei levels, GLenum internalformat,
                                         GLsizei width, GLsizei height, GLsizei depth)
    {
        if (width < 1 || height < 1 || depth < 1 || levels < 1)
        {
            return gl::error(GL_INVALID_VALUE, false);
        }
    
        if (levels > gl::log2(std::max(std::max(width, height), depth)) + 1)
        {
            return gl::error(GL_INVALID_OPERATION, false);
        }
    
        gl::Texture *texture = NULL;
        switch (target)
        {
          case GL_TEXTURE_2D:
            {
                texture = context->getTexture2D();
    
                if (width > (context->getMaximum2DTextureDimension()) ||
                    height > (context->getMaximum2DTextureDimension()))
                {
                    return gl::error(GL_INVALID_VALUE, false);
                }
            }
            break;
    
          case GL_TEXTURE_CUBE_MAP:
            {
                texture = context->getTextureCubeMap();
    
                if (width != height)
                {
                    return gl::error(GL_INVALID_VALUE, false);
                }
    
                if (width > (context->getMaximumCubeTextureDimension()))
                {
                    return gl::error(GL_INVALID_VALUE, false);
                }
            }
            break;
    
          case GL_TEXTURE_3D:
            {
                texture = context->getTexture3D();
    
                if (width > (context->getMaximum3DTextureDimension()) ||
                    height > (context->getMaximum3DTextureDimension()) ||
                    depth > (context->getMaximum3DTextureDimension()))
                {
                    return gl::error(GL_INVALID_VALUE, false);
                }
            }
            break;
    
          case GL_TEXTURE_2D_ARRAY:
            {
                texture = context->getTexture2DArray();
    
                if (width > (context->getMaximum2DTextureDimension()) ||
                    height > (context->getMaximum2DTextureDimension()) ||
                    depth > (context->getMaximum2DArrayTextureLayers()))
                {
                    return gl::error(GL_INVALID_VALUE, false);
                }
            }
            break;
    
          default:
            return gl::error(GL_INVALID_ENUM, false);
        }
    
        if (!texture || texture->id() == 0)
        {
            return gl::error(GL_INVALID_OPERATION, false);
        }
    
        if (texture->isImmutable())
        {
            return gl::error(GL_INVALID_OPERATION, false);
        }
    
        if (!gl::IsValidInternalFormat(internalformat, context))
        {
            return gl::error(GL_INVALID_ENUM, false);
        }
    
        if (!gl::IsSizedInternalFormat(internalformat, context->getClientVersion()))
        {
            return gl::error(GL_INVALID_ENUM, false);
        }
    
        return true;
    }
    
    bool ValidateES3FramebufferTextureParameters(gl::Context *context, GLenum target, GLenum attachment,
                                                 GLenum textarget, GLuint texture, GLint level, GLint layer,
                                                 bool layerCall)
    {
        if (target != GL_FRAMEBUFFER && target != GL_DRAW_FRAMEBUFFER && target != GL_READ_FRAMEBUFFER)
        {
            return gl::error(GL_INVALID_ENUM, false);
        }
    
        if (attachment >= GL_COLOR_ATTACHMENT0 && attachment <= GL_COLOR_ATTACHMENT15)
        {
            const unsigned int colorAttachment = (attachment - GL_COLOR_ATTACHMENT0);
            if (colorAttachment >= context->getMaximumRenderTargets())
            {
                return gl::error(GL_INVALID_VALUE, false);
            }
        }
        else
        {
            switch (attachment)
            {
              case GL_DEPTH_ATTACHMENT:
              case GL_STENCIL_ATTACHMENT:
              case GL_DEPTH_STENCIL_ATTACHMENT:
                break;
              default:
                return gl::error(GL_INVALID_ENUM, false);
            }
        }
    
        if (texture != 0)
        {
            gl::Texture *tex = context->getTexture(texture);
    
            if (tex == NULL)
            {
                return gl::error(GL_INVALID_OPERATION, false);
            }
    
            if (level < 0)
            {
                return gl::error(GL_INVALID_VALUE, false);
            }
    
            if (layer < 0)
            {
                return gl::error(GL_INVALID_VALUE, false);
            }
    
            if (!layerCall)
            {
                switch (textarget)
                {
                  case GL_TEXTURE_2D:
                    {
                        if (level > gl::log2(context->getMaximum2DTextureDimension()))
                        {
                            return gl::error(GL_INVALID_VALUE, false);
                        }
                        if (tex->getTarget() != GL_TEXTURE_2D)
                        {
                            return gl::error(GL_INVALID_OPERATION, false);
                        }
                        gl::Texture2D *tex2d = static_cast<gl::Texture2D *>(tex);
                        if (tex2d->isCompressed(level))
                        {
                            return gl::error(GL_INVALID_OPERATION, false);
                        }
                        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 (level > gl::log2(context->getMaximumCubeTextureDimension()))
                        {
                            return gl::error(GL_INVALID_VALUE, false);
                        }
                        if (tex->getTarget() != GL_TEXTURE_CUBE_MAP)
                        {
                            return gl::error(GL_INVALID_OPERATION, false);
                        }
                        gl::TextureCubeMap *texcube = static_cast<gl::TextureCubeMap *>(tex);
                        if (texcube->isCompressed(textarget, level))
                        {
                            return gl::error(GL_INVALID_OPERATION, false);
                        }
                        break;
                    }
    
                  default:
                    return gl::error(GL_INVALID_ENUM, false);
                }
            }
            else
            {
                switch (tex->getTarget())
                {
                  case GL_TEXTURE_2D_ARRAY:
                    {
                        if (level > gl::log2(context->getMaximum2DTextureDimension()))
                        {
                            return gl::error(GL_INVALID_VALUE, false);
                        }
    
                        if (layer >= context->getMaximum2DArrayTextureLayers())
                        {
                            return gl::error(GL_INVALID_VALUE, false);
                        }
    
                        gl::Texture2DArray *texArray = static_cast<gl::Texture2DArray *>(tex);
                        if (texArray->isCompressed(level))
                        {
                            return gl::error(GL_INVALID_OPERATION, false);
                        }
    
                        break;
                    }
    
                  case GL_TEXTURE_3D:
                    {
                        if (level > gl::log2(context->getMaximum3DTextureDimension()))
                        {
                            return gl::error(GL_INVALID_VALUE, false);
                        }
    
                        if (layer >= context->getMaximum3DTextureDimension())
                        {
                            return gl::error(GL_INVALID_VALUE, false);
                        }
    
                        gl::Texture3D *tex3d = static_cast<gl::Texture3D *>(tex);
                        if (tex3d->isCompressed(level))
                        {
                            return gl::error(GL_INVALID_OPERATION, false);
                        }
    
                        break;
                    }
    
                  default:
                    return gl::error(GL_INVALID_OPERATION, false);
                }
            }
        }
    
        gl::Framebuffer *framebuffer = NULL;
        GLuint framebufferHandle = 0;
        if (target == GL_READ_FRAMEBUFFER)
        {
            framebuffer = context->getReadFramebuffer();
            framebufferHandle = context->getReadFramebufferHandle();
        }
        else
        {
            framebuffer = context->getDrawFramebuffer();
            framebufferHandle = context->getDrawFramebufferHandle();
        }
    
        if (framebufferHandle == 0 || !framebuffer)
        {
            return gl::error(GL_INVALID_OPERATION, false);
        }
    
        return true;
    }
    
    bool ValidES3ReadFormatType(GLenum internalFormat, GLenum format, GLenum type)
    {
        switch (format)
        {
          case GL_RGBA:
            switch (type)
            {
              case GL_UNSIGNED_BYTE:
                break;
              case GL_UNSIGNED_INT_2_10_10_10_REV:
                if (internalFormat != GL_RGB10_A2)
                {
                    return false;
                }
                break;
              case GL_FLOAT:
                if (gl::GetComponentType(internalFormat, 3) != GL_FLOAT)
                {
                    return false;
                }
                break;
              default:
                return false;
            }
            break;
          case GL_RGBA_INTEGER:
            switch (type)
            {
              case GL_INT:
                if (gl::GetComponentType(internalFormat, 3) != GL_INT)
                {
                    return false;
                }
                break;
              case GL_UNSIGNED_INT:
                if (gl::GetComponentType(internalFormat, 3) != GL_UNSIGNED_INT)
                {
                    return false;
                }
                break;
              default:
                return false;
            }
            break;
          case GL_BGRA_EXT:
            switch (type)
            {
              case GL_UNSIGNED_BYTE:
              case GL_UNSIGNED_SHORT_4_4_4_4_REV_EXT:
              case GL_UNSIGNED_SHORT_1_5_5_5_REV_EXT:
                break;
              default:
                return false;
            }
            break;
          default:
            return false;
        }
        return true;
    }
    
    bool ValidateInvalidateFramebufferParameters(gl::Context *context, GLenum target, GLsizei numAttachments,
                                                 const GLenum* attachments)
    {
        bool defaultFramebuffer = false;
    
        switch (target)
        {
          case GL_DRAW_FRAMEBUFFER:
          case GL_FRAMEBUFFER:
            defaultFramebuffer = context->getDrawFramebufferHandle() == 0;
            break;
          case GL_READ_FRAMEBUFFER:
            defaultFramebuffer = context->getReadFramebufferHandle() == 0;
            break;
          default:
            return gl::error(GL_INVALID_ENUM, false);
        }
    
        for (int i = 0; i < numAttachments; ++i)
        {
            if (attachments[i] >= GL_COLOR_ATTACHMENT0 && attachments[i] <= GL_COLOR_ATTACHMENT15)
            {
                if (defaultFramebuffer)
                {
                    return gl::error(GL_INVALID_ENUM, false);
                }
    
                if (attachments[i] >= GL_COLOR_ATTACHMENT0 + context->getMaximumRenderTargets())
                {
                    return gl::error(GL_INVALID_OPERATION, false);
                }
            }
            else
            {
                switch (attachments[i])
                {
                  case GL_DEPTH_ATTACHMENT:
                  case GL_STENCIL_ATTACHMENT:
                  case GL_DEPTH_STENCIL_ATTACHMENT:
                    if (defaultFramebuffer)
                    {
                        return gl::error(GL_INVALID_ENUM, false);
                    }
                    break;
                  case GL_COLOR:
                  case GL_DEPTH:
                  case GL_STENCIL:
                    if (!defaultFramebuffer)
                    {
                        return gl::error(GL_INVALID_ENUM, false);
                    }
                    break;
                  default:
                    return gl::error(GL_INVALID_ENUM, false);
                }
            }
        }
    
        return true;
    }
    
    }