Edit

kc3-lang/angle/src/libANGLE/validationES2.cpp

Branch :

  • Show log

    Commit

  • Author : Corentin Wallez
    Date : 2017-07-26 11:01:02
    Hash : 26cf35a8
    Message : Revert "Fix BlitFramebuffer validation for BGRA sources and targets." This reverts commit 17d270311efcd8c72f251a67e726518278e81c26. Reason for revert: - New test fails on NVIDIA and AMD on Windows when using the backbuffer FAST_PATH. - SetUp code ASSERT_GL_NO_ERROR (line 269) but a GL error happens on all Windows and Linux Intel OpenGL. Original change's description: > Fix BlitFramebuffer validation for BGRA sources and targets. > > It is legal to blit between GL_RGBA8 and GL_BGRA8 sources and > destinations when resolving multisampled renderbuffers. Expand > BlitFramebuffer's validation to handle this case. > > Work around a bug in macOS' OpenGL driver querying the number of > samples for GL_BGRA8. > > Query GL_MAX_VERTEX_OUTPUT_COMPONENTS on the Core Profile to work > around an error generated on macOS when querying > GL_MAX_VARYING_COMPONENTS. > > Expand the BlitFramebuffer tests to cover these cases and start > running them on the OpenGL backend. > > BUG=angleproject:891 > > Change-Id: I4829585d2b6428ce0bc7509c4734d33709a0930b > Reviewed-on: https://chromium-review.googlesource.com/582268 > Commit-Queue: Geoff Lang <geofflang@chromium.org> > Reviewed-by: Kenneth Russell <kbr@chromium.org> > Reviewed-by: Geoff Lang <geofflang@chromium.org> TBR=geofflang@chromium.org,kbr@chromium.org Change-Id: I220bc482194cf7fad5e7e732a6d043ce0d504d79 No-Presubmit: true No-Tree-Checks: true No-Try: true Bug: angleproject:891 Reviewed-on: https://chromium-review.googlesource.com/586428 Reviewed-by: Corentin Wallez <cwallez@chromium.org> Commit-Queue: Corentin Wallez <cwallez@chromium.org>

  • src/libANGLE/validationES2.cpp
  • //
    // Copyright (c) 2013-2014 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.
    //
    
    // validationES2.cpp: Validation functions for OpenGL ES 2.0 entry point parameters
    
    #include "libANGLE/validationES2.h"
    
    #include <cstdint>
    
    #include "common/mathutil.h"
    #include "common/string_utils.h"
    #include "common/utilities.h"
    #include "libANGLE/Context.h"
    #include "libANGLE/ErrorStrings.h"
    #include "libANGLE/Framebuffer.h"
    #include "libANGLE/FramebufferAttachment.h"
    #include "libANGLE/Renderbuffer.h"
    #include "libANGLE/Shader.h"
    #include "libANGLE/Texture.h"
    #include "libANGLE/Uniform.h"
    #include "libANGLE/VertexArray.h"
    #include "libANGLE/formatutils.h"
    #include "libANGLE/validationES.h"
    #include "libANGLE/validationES3.h"
    
    namespace gl
    {
    
    namespace
    {
    
    bool IsPartialBlit(gl::Context *context,
                       const FramebufferAttachment *readBuffer,
                       const FramebufferAttachment *writeBuffer,
                       GLint srcX0,
                       GLint srcY0,
                       GLint srcX1,
                       GLint srcY1,
                       GLint dstX0,
                       GLint dstY0,
                       GLint dstX1,
                       GLint dstY1)
    {
        const Extents &writeSize = writeBuffer->getSize();
        const Extents &readSize  = readBuffer->getSize();
    
        if (srcX0 != 0 || srcY0 != 0 || dstX0 != 0 || dstY0 != 0 || dstX1 != writeSize.width ||
            dstY1 != writeSize.height || srcX1 != readSize.width || srcY1 != readSize.height)
        {
            return true;
        }
    
        if (context->getGLState().isScissorTestEnabled())
        {
            const Rectangle &scissor = context->getGLState().getScissor();
            return scissor.x > 0 || scissor.y > 0 || scissor.width < writeSize.width ||
                   scissor.height < writeSize.height;
        }
    
        return false;
    }
    
    template <typename T>
    bool ValidatePathInstances(gl::Context *context,
                               GLsizei numPaths,
                               const void *paths,
                               GLuint pathBase)
    {
        const auto *array = static_cast<const T *>(paths);
    
        for (GLsizei i = 0; i < numPaths; ++i)
        {
            const GLuint pathName = array[i] + pathBase;
            if (context->hasPath(pathName) && !context->hasPathData(pathName))
            {
                context->handleError(InvalidOperation() << "No such path object.");
                return false;
            }
        }
        return true;
    }
    
    bool ValidateInstancedPathParameters(gl::Context *context,
                                         GLsizei numPaths,
                                         GLenum pathNameType,
                                         const void *paths,
                                         GLuint pathBase,
                                         GLenum transformType,
                                         const GLfloat *transformValues)
    {
        if (!context->getExtensions().pathRendering)
        {
            context->handleError(InvalidOperation() << "GL_CHROMIUM_path_rendering is not available.");
            return false;
        }
    
        if (paths == nullptr)
        {
            context->handleError(InvalidValue() << "No path name array.");
            return false;
        }
    
        if (numPaths < 0)
        {
            context->handleError(InvalidValue() << "Invalid (negative) numPaths.");
            return false;
        }
    
        if (!angle::IsValueInRangeForNumericType<std::uint32_t>(numPaths))
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), IntegerOverflow);
            return false;
        }
    
        std::uint32_t pathNameTypeSize = 0;
        std::uint32_t componentCount   = 0;
    
        switch (pathNameType)
        {
            case GL_UNSIGNED_BYTE:
                pathNameTypeSize = sizeof(GLubyte);
                if (!ValidatePathInstances<GLubyte>(context, numPaths, paths, pathBase))
                    return false;
                break;
    
            case GL_BYTE:
                pathNameTypeSize = sizeof(GLbyte);
                if (!ValidatePathInstances<GLbyte>(context, numPaths, paths, pathBase))
                    return false;
                break;
    
            case GL_UNSIGNED_SHORT:
                pathNameTypeSize = sizeof(GLushort);
                if (!ValidatePathInstances<GLushort>(context, numPaths, paths, pathBase))
                    return false;
                break;
    
            case GL_SHORT:
                pathNameTypeSize = sizeof(GLshort);
                if (!ValidatePathInstances<GLshort>(context, numPaths, paths, pathBase))
                    return false;
                break;
    
            case GL_UNSIGNED_INT:
                pathNameTypeSize = sizeof(GLuint);
                if (!ValidatePathInstances<GLuint>(context, numPaths, paths, pathBase))
                    return false;
                break;
    
            case GL_INT:
                pathNameTypeSize = sizeof(GLint);
                if (!ValidatePathInstances<GLint>(context, numPaths, paths, pathBase))
                    return false;
                break;
    
            default:
                context->handleError(InvalidEnum() << "Invalid path name type.");
                return false;
        }
    
        switch (transformType)
        {
            case GL_NONE:
                componentCount = 0;
                break;
            case GL_TRANSLATE_X_CHROMIUM:
            case GL_TRANSLATE_Y_CHROMIUM:
                componentCount = 1;
                break;
            case GL_TRANSLATE_2D_CHROMIUM:
                componentCount = 2;
                break;
            case GL_TRANSLATE_3D_CHROMIUM:
                componentCount = 3;
                break;
            case GL_AFFINE_2D_CHROMIUM:
            case GL_TRANSPOSE_AFFINE_2D_CHROMIUM:
                componentCount = 6;
                break;
            case GL_AFFINE_3D_CHROMIUM:
            case GL_TRANSPOSE_AFFINE_3D_CHROMIUM:
                componentCount = 12;
                break;
            default:
                context->handleError(InvalidEnum() << "Invalid transformation.");
                return false;
        }
        if (componentCount != 0 && transformValues == nullptr)
        {
            context->handleError(InvalidValue() << "No transform array given.");
            return false;
        }
    
        angle::CheckedNumeric<std::uint32_t> checkedSize(0);
        checkedSize += (numPaths * pathNameTypeSize);
        checkedSize += (numPaths * sizeof(GLfloat) * componentCount);
        if (!checkedSize.IsValid())
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), IntegerOverflow);
            return false;
        }
    
        return true;
    }
    
    bool IsValidCopyTextureSourceInternalFormatEnum(GLenum internalFormat)
    {
        // Table 1.1 from the CHROMIUM_copy_texture spec
        switch (GetUnsizedFormat(internalFormat))
        {
            case GL_RED:
            case GL_ALPHA:
            case GL_LUMINANCE:
            case GL_LUMINANCE_ALPHA:
            case GL_RGB:
            case GL_RGBA:
            case GL_RGB8:
            case GL_RGBA8:
            case GL_BGRA_EXT:
            case GL_BGRA8_EXT:
                return true;
    
            default:
                return false;
        }
    }
    
    bool IsValidCopySubTextureSourceInternalFormat(GLenum internalFormat)
    {
        return IsValidCopyTextureSourceInternalFormatEnum(internalFormat);
    }
    
    bool IsValidCopyTextureDestinationInternalFormatEnum(GLint internalFormat)
    {
        // Table 1.0 from the CHROMIUM_copy_texture spec
        switch (internalFormat)
        {
            case GL_RGB:
            case GL_RGBA:
            case GL_RGB8:
            case GL_RGBA8:
            case GL_BGRA_EXT:
            case GL_BGRA8_EXT:
            case GL_SRGB_EXT:
            case GL_SRGB_ALPHA_EXT:
            case GL_R8:
            case GL_R8UI:
            case GL_RG8:
            case GL_RG8UI:
            case GL_SRGB8:
            case GL_RGB565:
            case GL_RGB8UI:
            case GL_RGB10_A2:
            case GL_SRGB8_ALPHA8:
            case GL_RGB5_A1:
            case GL_RGBA4:
            case GL_RGBA8UI:
            case GL_RGB9_E5:
            case GL_R16F:
            case GL_R32F:
            case GL_RG16F:
            case GL_RG32F:
            case GL_RGB16F:
            case GL_RGB32F:
            case GL_RGBA16F:
            case GL_RGBA32F:
            case GL_R11F_G11F_B10F:
            case GL_LUMINANCE:
            case GL_LUMINANCE_ALPHA:
            case GL_ALPHA:
                return true;
    
            default:
                return false;
        }
    }
    
    bool IsValidCopySubTextureDestionationInternalFormat(GLenum internalFormat)
    {
        return IsValidCopyTextureDestinationInternalFormatEnum(internalFormat);
    }
    
    bool IsValidCopyTextureDestinationFormatType(Context *context, GLint internalFormat, GLenum type)
    {
        if (!IsValidCopyTextureDestinationInternalFormatEnum(internalFormat))
        {
            return false;
        }
    
        const InternalFormat &internalFormatInfo = GetInternalFormatInfo(internalFormat, type);
        if (!internalFormatInfo.textureSupport(context->getClientVersion(), context->getExtensions()))
        {
            return false;
        }
    
        return true;
    }
    
    bool IsValidCopyTextureDestinationTarget(Context *context, GLenum textureType, GLenum target)
    {
        switch (target)
        {
            case GL_TEXTURE_2D:
                return textureType == GL_TEXTURE_2D;
    
            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 textureType == GL_TEXTURE_CUBE_MAP;
    
            // TODO(geofflang): accept GL_TEXTURE_RECTANGLE_ARB if the texture_rectangle extension is
            // supported
    
            default:
                return false;
        }
    }
    
    bool IsValidCopyTextureSourceTarget(Context *context, GLenum target)
    {
        switch (target)
        {
            case GL_TEXTURE_2D:
                return true;
    
            // TODO(geofflang): accept GL_TEXTURE_RECTANGLE_ARB if the texture_rectangle extension is
            // supported
    
            // TODO(geofflang): accept GL_TEXTURE_EXTERNAL_OES if the texture_external extension is
            // supported
    
            default:
                return false;
        }
    }
    
    bool IsValidCopyTextureSourceLevel(Context *context, GLenum target, GLint level)
    {
        if (!ValidMipLevel(context, target, level))
        {
            return false;
        }
    
        if (level > 0 && context->getClientVersion() < ES_3_0)
        {
            return false;
        }
    
        return true;
    }
    
    bool IsValidCopyTextureDestinationLevel(Context *context,
                                            GLenum target,
                                            GLint level,
                                            GLsizei width,
                                            GLsizei height)
    {
        if (!ValidMipLevel(context, target, level))
        {
            return false;
        }
    
        const Caps &caps = context->getCaps();
        if (target == GL_TEXTURE_2D)
        {
            if (static_cast<GLuint>(width) > (caps.max2DTextureSize >> level) ||
                static_cast<GLuint>(height) > (caps.max2DTextureSize >> level))
            {
                return false;
            }
        }
        else if (IsCubeMapTextureTarget(target))
        {
            if (static_cast<GLuint>(width) > (caps.maxCubeMapTextureSize >> level) ||
                static_cast<GLuint>(height) > (caps.maxCubeMapTextureSize >> level))
            {
                return false;
            }
        }
    
        return true;
    }
    
    bool IsValidStencilFunc(GLenum func)
    {
        switch (func)
        {
            case GL_NEVER:
            case GL_ALWAYS:
            case GL_LESS:
            case GL_LEQUAL:
            case GL_EQUAL:
            case GL_GEQUAL:
            case GL_GREATER:
            case GL_NOTEQUAL:
                return true;
    
            default:
                return false;
        }
    }
    
    bool IsValidStencilFace(GLenum face)
    {
        switch (face)
        {
            case GL_FRONT:
            case GL_BACK:
            case GL_FRONT_AND_BACK:
                return true;
    
            default:
                return false;
        }
    }
    
    bool IsValidStencilOp(GLenum op)
    {
        switch (op)
        {
            case GL_ZERO:
            case GL_KEEP:
            case GL_REPLACE:
            case GL_INCR:
            case GL_DECR:
            case GL_INVERT:
            case GL_INCR_WRAP:
            case GL_DECR_WRAP:
                return true;
    
            default:
                return false;
        }
    }
    
    bool ValidateES2CopyTexImageParameters(ValidationContext *context,
                                           GLenum target,
                                           GLint level,
                                           GLenum internalformat,
                                           bool isSubImage,
                                           GLint xoffset,
                                           GLint yoffset,
                                           GLint x,
                                           GLint y,
                                           GLsizei width,
                                           GLsizei height,
                                           GLint border)
    {
        if (!ValidTexture2DDestinationTarget(context, target))
        {
            ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidTextureTarget);
            return false;
        }
    
        if (!ValidImageSizeParameters(context, target, level, width, height, 1, isSubImage))
        {
            context->handleError(InvalidValue() << "Invalid texture dimensions.");
            return false;
        }
    
        Format textureFormat = Format::Invalid();
        if (!ValidateCopyTexImageParametersBase(context, target, level, internalformat, isSubImage,
                                                xoffset, yoffset, 0, x, y, width, height, border,
                                                &textureFormat))
        {
            return false;
        }
    
        const gl::Framebuffer *framebuffer = context->getGLState().getReadFramebuffer();
        GLenum colorbufferFormat =
            framebuffer->getReadColorbuffer()->getFormat().info->sizedInternalFormat;
        const auto &formatInfo = *textureFormat.info;
    
        // [OpenGL ES 2.0.24] table 3.9
        if (isSubImage)
        {
            switch (formatInfo.format)
            {
                case GL_ALPHA:
                    if (colorbufferFormat != GL_ALPHA8_EXT && colorbufferFormat != GL_RGBA4 &&
                        colorbufferFormat != GL_RGB5_A1 && colorbufferFormat != GL_RGBA8_OES &&
                        colorbufferFormat != GL_BGRA8_EXT && colorbufferFormat != GL_BGR5_A1_ANGLEX)
                    {
                        ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidFormat);
                        return false;
                    }
                    break;
                case GL_LUMINANCE:
                    if (colorbufferFormat != GL_R8_EXT && colorbufferFormat != GL_RG8_EXT &&
                        colorbufferFormat != GL_RGB565 && colorbufferFormat != GL_RGB8_OES &&
                        colorbufferFormat != GL_RGBA4 && colorbufferFormat != GL_RGB5_A1 &&
                        colorbufferFormat != GL_RGBA8_OES && colorbufferFormat != GL_BGRA8_EXT &&
                        colorbufferFormat != GL_BGR5_A1_ANGLEX)
                    {
                        ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidFormat);
                        return false;
                    }
                    break;
                case GL_RED_EXT:
                    if (colorbufferFormat != GL_R8_EXT && colorbufferFormat != GL_RG8_EXT &&
                        colorbufferFormat != GL_RGB565 && colorbufferFormat != GL_RGB8_OES &&
                        colorbufferFormat != GL_RGBA4 && colorbufferFormat != GL_RGB5_A1 &&
                        colorbufferFormat != GL_RGBA8_OES && colorbufferFormat != GL_R32F &&
                        colorbufferFormat != GL_RG32F && colorbufferFormat != GL_RGB32F &&
                        colorbufferFormat != GL_RGBA32F && colorbufferFormat != GL_BGRA8_EXT &&
                        colorbufferFormat != GL_BGR5_A1_ANGLEX)
                    {
                        ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidFormat);
                        return false;
                    }
                    break;
                case GL_RG_EXT:
                    if (colorbufferFormat != GL_RG8_EXT && colorbufferFormat != GL_RGB565 &&
                        colorbufferFormat != GL_RGB8_OES && colorbufferFormat != GL_RGBA4 &&
                        colorbufferFormat != GL_RGB5_A1 && colorbufferFormat != GL_RGBA8_OES &&
                        colorbufferFormat != GL_RG32F && colorbufferFormat != GL_RGB32F &&
                        colorbufferFormat != GL_RGBA32F && colorbufferFormat != GL_BGRA8_EXT &&
                        colorbufferFormat != GL_BGR5_A1_ANGLEX)
                    {
                        ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidFormat);
                        return false;
                    }
                    break;
                case GL_RGB:
                    if (colorbufferFormat != GL_RGB565 && colorbufferFormat != GL_RGB8_OES &&
                        colorbufferFormat != GL_RGBA4 && colorbufferFormat != GL_RGB5_A1 &&
                        colorbufferFormat != GL_RGBA8_OES && colorbufferFormat != GL_RGB32F &&
                        colorbufferFormat != GL_RGBA32F && colorbufferFormat != GL_BGRA8_EXT &&
                        colorbufferFormat != GL_BGR5_A1_ANGLEX)
                    {
                        ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidFormat);
                        return false;
                    }
                    break;
                case GL_LUMINANCE_ALPHA:
                case GL_RGBA:
                    if (colorbufferFormat != GL_RGBA4 && colorbufferFormat != GL_RGB5_A1 &&
                        colorbufferFormat != GL_RGBA8_OES && colorbufferFormat != GL_RGBA32F &&
                        colorbufferFormat != GL_BGRA8_EXT && colorbufferFormat != GL_BGR5_A1_ANGLEX)
                    {
                        ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidFormat);
                        return 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:
                case GL_ETC1_RGB8_OES:
                case GL_ETC1_RGB8_LOSSY_DECODE_ANGLE:
                case GL_COMPRESSED_RGB8_LOSSY_DECODE_ETC2_ANGLE:
                case GL_COMPRESSED_SRGB8_LOSSY_DECODE_ETC2_ANGLE:
                case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_LOSSY_DECODE_ETC2_ANGLE:
                case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_LOSSY_DECODE_ETC2_ANGLE:
                    ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidFormat);
                    return false;
                case GL_DEPTH_COMPONENT:
                case GL_DEPTH_STENCIL_OES:
                    ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidFormat);
                    return false;
                default:
                    ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidFormat);
                    return false;
            }
    
            if (formatInfo.type == GL_FLOAT && !context->getExtensions().textureFloat)
            {
                ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidFormat);
                return 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 && colorbufferFormat != GL_BGR5_A1_ANGLEX)
                    {
                        ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidFormat);
                        return false;
                    }
                    break;
                case GL_LUMINANCE:
                    if (colorbufferFormat != GL_R8_EXT && colorbufferFormat != GL_RG8_EXT &&
                        colorbufferFormat != GL_RGB565 && colorbufferFormat != GL_RGB8_OES &&
                        colorbufferFormat != GL_RGBA4 && colorbufferFormat != GL_RGB5_A1 &&
                        colorbufferFormat != GL_BGRA8_EXT && colorbufferFormat != GL_RGBA8_OES &&
                        colorbufferFormat != GL_BGR5_A1_ANGLEX)
                    {
                        ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidFormat);
                        return false;
                    }
                    break;
                case GL_RED_EXT:
                    if (colorbufferFormat != GL_R8_EXT && colorbufferFormat != GL_RG8_EXT &&
                        colorbufferFormat != GL_RGB565 && colorbufferFormat != GL_RGB8_OES &&
                        colorbufferFormat != GL_RGBA4 && colorbufferFormat != GL_RGB5_A1 &&
                        colorbufferFormat != GL_BGRA8_EXT && colorbufferFormat != GL_RGBA8_OES &&
                        colorbufferFormat != GL_BGR5_A1_ANGLEX)
                    {
                        ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidFormat);
                        return false;
                    }
                    break;
                case GL_RG_EXT:
                    if (colorbufferFormat != GL_RG8_EXT && colorbufferFormat != GL_RGB565 &&
                        colorbufferFormat != GL_RGB8_OES && colorbufferFormat != GL_RGBA4 &&
                        colorbufferFormat != GL_RGB5_A1 && colorbufferFormat != GL_BGRA8_EXT &&
                        colorbufferFormat != GL_RGBA8_OES && colorbufferFormat != GL_BGR5_A1_ANGLEX)
                    {
                        ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidFormat);
                        return false;
                    }
                    break;
                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 &&
                        colorbufferFormat != GL_BGR5_A1_ANGLEX)
                    {
                        ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidFormat);
                        return 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 &&
                        colorbufferFormat != GL_BGR5_A1_ANGLEX)
                    {
                        ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidFormat);
                        return false;
                    }
                    break;
                case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
                case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
                    if (context->getExtensions().textureCompressionDXT1)
                    {
                        ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidFormat);
                        return false;
                    }
                    else
                    {
                        ANGLE_VALIDATION_ERR(context, InvalidEnum(), EnumNotSupported);
                        return false;
                    }
                    break;
                case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
                    if (context->getExtensions().textureCompressionDXT3)
                    {
                        ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidFormat);
                        return false;
                    }
                    else
                    {
                        ANGLE_VALIDATION_ERR(context, InvalidEnum(), EnumNotSupported);
                        return false;
                    }
                    break;
                case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
                    if (context->getExtensions().textureCompressionDXT5)
                    {
                        ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidFormat);
                        return false;
                    }
                    else
                    {
                        ANGLE_VALIDATION_ERR(context, InvalidEnum(), EnumNotSupported);
                        return false;
                    }
                    break;
                case GL_ETC1_RGB8_OES:
                    if (context->getExtensions().compressedETC1RGB8Texture)
                    {
                        context->handleError(InvalidOperation());
                        return false;
                    }
                    else
                    {
                        context->handleError(InvalidEnum());
                        return false;
                    }
                    break;
                case GL_ETC1_RGB8_LOSSY_DECODE_ANGLE:
                case GL_COMPRESSED_RGB8_LOSSY_DECODE_ETC2_ANGLE:
                case GL_COMPRESSED_SRGB8_LOSSY_DECODE_ETC2_ANGLE:
                case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_LOSSY_DECODE_ETC2_ANGLE:
                case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_LOSSY_DECODE_ETC2_ANGLE:
                    if (context->getExtensions().lossyETCDecode)
                    {
                        context->handleError(InvalidOperation()
                                             << "ETC lossy decode formats can't be copied to.");
                        return false;
                    }
                    else
                    {
                        context->handleError(InvalidEnum()
                                             << "ANGLE_lossy_etc_decode extension is not supported.");
                        return 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->getExtensions().depthTextures)
                    {
                        context->handleError(InvalidOperation());
                        return false;
                    }
                    else
                    {
                        context->handleError(InvalidEnum());
                        return false;
                    }
                default:
                    context->handleError(InvalidEnum());
                    return false;
            }
        }
    
        // If width or height is zero, it is a no-op.  Return false without setting an error.
        return (width > 0 && height > 0);
    }
    
    bool ValidCap(const Context *context, GLenum cap, bool queryOnly)
    {
        switch (cap)
        {
            // EXT_multisample_compatibility
            case GL_MULTISAMPLE_EXT:
            case GL_SAMPLE_ALPHA_TO_ONE_EXT:
                return context->getExtensions().multisampleCompatibility;
    
            case GL_CULL_FACE:
            case GL_POLYGON_OFFSET_FILL:
            case GL_SAMPLE_ALPHA_TO_COVERAGE:
            case GL_SAMPLE_COVERAGE:
            case GL_SCISSOR_TEST:
            case GL_STENCIL_TEST:
            case GL_DEPTH_TEST:
            case GL_BLEND:
            case GL_DITHER:
                return true;
    
            case GL_PRIMITIVE_RESTART_FIXED_INDEX:
            case GL_RASTERIZER_DISCARD:
                return (context->getClientMajorVersion() >= 3);
    
            case GL_DEBUG_OUTPUT_SYNCHRONOUS:
            case GL_DEBUG_OUTPUT:
                return context->getExtensions().debug;
    
            case GL_BIND_GENERATES_RESOURCE_CHROMIUM:
                return queryOnly && context->getExtensions().bindGeneratesResource;
    
            case GL_CLIENT_ARRAYS_ANGLE:
                return queryOnly && context->getExtensions().clientArrays;
    
            case GL_FRAMEBUFFER_SRGB_EXT:
                return context->getExtensions().sRGBWriteControl;
    
            case GL_SAMPLE_MASK:
                return context->getClientVersion() >= Version(3, 1);
    
            case GL_CONTEXT_ROBUST_RESOURCE_INITIALIZATION_ANGLE:
                return queryOnly && context->getExtensions().robustResourceInitialization;
    
            default:
                return false;
        }
    }
    
    // Return true if a character belongs to the ASCII subset as defined in GLSL ES 1.0 spec section
    // 3.1.
    bool IsValidESSLCharacter(unsigned char c)
    {
        // Printing characters are valid except " $ ` @ \ ' DEL.
        if (c >= 32 && c <= 126 && c != '"' && c != '$' && c != '`' && c != '@' && c != '\\' &&
            c != '\'')
        {
            return true;
        }
    
        // Horizontal tab, line feed, vertical tab, form feed, carriage return are also valid.
        if (c >= 9 && c <= 13)
        {
            return true;
        }
    
        return false;
    }
    
    bool IsValidESSLString(const char *str, size_t len)
    {
        for (size_t i = 0; i < len; i++)
        {
            if (!IsValidESSLCharacter(str[i]))
            {
                return false;
            }
        }
    
        return true;
    }
    
    bool IsValidESSLShaderSourceString(const char *str, size_t len, bool lineContinuationAllowed)
    {
        enum class ParseState
        {
            // Have not seen an ASCII non-whitespace character yet on
            // this line. Possible that we might see a preprocessor
            // directive.
            BEGINING_OF_LINE,
    
            // Have seen at least one ASCII non-whitespace character
            // on this line.
            MIDDLE_OF_LINE,
    
            // Handling a preprocessor directive. Passes through all
            // characters up to the end of the line. Disables comment
            // processing.
            IN_PREPROCESSOR_DIRECTIVE,
    
            // Handling a single-line comment. The comment text is
            // replaced with a single space.
            IN_SINGLE_LINE_COMMENT,
    
            // Handling a multi-line comment. Newlines are passed
            // through to preserve line numbers.
            IN_MULTI_LINE_COMMENT
        };
    
        ParseState state = ParseState::BEGINING_OF_LINE;
        size_t pos       = 0;
    
        while (pos < len)
        {
            char c    = str[pos];
            char next = pos + 1 < len ? str[pos + 1] : 0;
    
            // Check for newlines
            if (c == '\n' || c == '\r')
            {
                if (state != ParseState::IN_MULTI_LINE_COMMENT)
                {
                    state = ParseState::BEGINING_OF_LINE;
                }
    
                pos++;
                continue;
            }
    
            switch (state)
            {
                case ParseState::BEGINING_OF_LINE:
                    if (c == ' ')
                    {
                        // Maintain the BEGINING_OF_LINE state until a non-space is seen
                        pos++;
                    }
                    else if (c == '#')
                    {
                        state = ParseState::IN_PREPROCESSOR_DIRECTIVE;
                        pos++;
                    }
                    else
                    {
                        // Don't advance, re-process this character with the MIDDLE_OF_LINE state
                        state = ParseState::MIDDLE_OF_LINE;
                    }
                    break;
    
                case ParseState::MIDDLE_OF_LINE:
                    if (c == '/' && next == '/')
                    {
                        state = ParseState::IN_SINGLE_LINE_COMMENT;
                        pos++;
                    }
                    else if (c == '/' && next == '*')
                    {
                        state = ParseState::IN_MULTI_LINE_COMMENT;
                        pos++;
                    }
                    else if (lineContinuationAllowed && c == '\\' && (next == '\n' || next == '\r'))
                    {
                        // Skip line continuation characters
                    }
                    else if (!IsValidESSLCharacter(c))
                    {
                        return false;
                    }
                    pos++;
                    break;
    
                case ParseState::IN_PREPROCESSOR_DIRECTIVE:
                    // No matter what the character is, just pass it
                    // through. Do not parse comments in this state.
                    pos++;
                    break;
    
                case ParseState::IN_SINGLE_LINE_COMMENT:
                    // Line-continuation characters are processed before comment processing.
                    // Advance string if a new line character is immediately behind
                    // line-continuation character.
                    if (c == '\\' && (next == '\n' || next == '\r'))
                    {
                        pos++;
                    }
                    pos++;
                    break;
    
                case ParseState::IN_MULTI_LINE_COMMENT:
                    if (c == '*' && next == '/')
                    {
                        state = ParseState::MIDDLE_OF_LINE;
                        pos++;
                    }
                    pos++;
                    break;
            }
        }
    
        return true;
    }
    
    }  // anonymous namespace
    
    bool ValidateES2TexImageParameters(Context *context,
                                       GLenum target,
                                       GLint level,
                                       GLenum internalformat,
                                       bool isCompressed,
                                       bool isSubImage,
                                       GLint xoffset,
                                       GLint yoffset,
                                       GLsizei width,
                                       GLsizei height,
                                       GLint border,
                                       GLenum format,
                                       GLenum type,
                                       GLsizei imageSize,
                                       const void *pixels)
    {
        if (!ValidTexture2DDestinationTarget(context, target))
        {
            ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidTextureTarget);
            return false;
        }
    
        if (!ValidImageSizeParameters(context, target, level, width, height, 1, isSubImage))
        {
            context->handleError(InvalidValue());
            return false;
        }
    
        if (!ValidMipLevel(context, target, level))
        {
            ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidMipLevel);
            return false;
        }
    
        if (xoffset < 0 || std::numeric_limits<GLsizei>::max() - xoffset < width ||
            std::numeric_limits<GLsizei>::max() - yoffset < height)
        {
            ANGLE_VALIDATION_ERR(context, InvalidValue(), ResourceMaxTextureSize);
            return false;
        }
    
        // From GL_CHROMIUM_color_buffer_float_rgb[a]:
        // GL_RGB[A] / GL_RGB[A]32F becomes an allowable format / internalformat parameter pair for
        // TexImage2D. The restriction in section 3.7.1 of the OpenGL ES 2.0 spec that the
        // internalformat parameter and format parameter of TexImage2D must match is lifted for this
        // case.
        bool nonEqualFormatsAllowed =
            (internalformat == GL_RGB32F && context->getExtensions().colorBufferFloatRGB) ||
            (internalformat == GL_RGBA32F && context->getExtensions().colorBufferFloatRGBA);
    
        if (!isSubImage && !isCompressed && internalformat != format && !nonEqualFormatsAllowed)
        {
            context->handleError(InvalidOperation());
            return false;
        }
    
        const gl::Caps &caps = context->getCaps();
    
        if (target == GL_TEXTURE_2D)
        {
            if (static_cast<GLuint>(width) > (caps.max2DTextureSize >> level) ||
                static_cast<GLuint>(height) > (caps.max2DTextureSize >> level))
            {
                context->handleError(InvalidValue());
                return false;
            }
        }
        else if (IsCubeMapTextureTarget(target))
        {
            if (!isSubImage && width != height)
            {
                ANGLE_VALIDATION_ERR(context, InvalidValue(), CubemapFacesEqualDimensions);
                return false;
            }
    
            if (static_cast<GLuint>(width) > (caps.maxCubeMapTextureSize >> level) ||
                static_cast<GLuint>(height) > (caps.maxCubeMapTextureSize >> level))
            {
                context->handleError(InvalidValue());
                return false;
            }
        }
        else
        {
            context->handleError(InvalidEnum());
            return false;
        }
    
        gl::Texture *texture =
            context->getTargetTexture(IsCubeMapTextureTarget(target) ? GL_TEXTURE_CUBE_MAP : target);
        if (!texture)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), BufferNotBound);
            return false;
        }
    
        if (isSubImage)
        {
            const InternalFormat &textureInternalFormat = *texture->getFormat(target, level).info;
            if (textureInternalFormat.internalFormat == GL_NONE)
            {
                context->handleError(InvalidOperation() << "Texture level does not exist.");
                return false;
            }
    
            if (format != GL_NONE)
            {
                if (GetInternalFormatInfo(format, type).sizedInternalFormat !=
                    textureInternalFormat.sizedInternalFormat)
                {
                    ANGLE_VALIDATION_ERR(context, InvalidOperation(), TypeMustMatchOriginalType);
                    return false;
                }
            }
    
            if (static_cast<size_t>(xoffset + width) > texture->getWidth(target, level) ||
                static_cast<size_t>(yoffset + height) > texture->getHeight(target, level))
            {
                context->handleError(InvalidValue());
                return false;
            }
        }
        else
        {
            if (texture->getImmutableFormat())
            {
                context->handleError(InvalidOperation());
                return false;
            }
        }
    
        // Verify zero border
        if (border != 0)
        {
            ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidBorder);
            return false;
        }
    
        if (isCompressed)
        {
            GLenum actualInternalFormat =
                isSubImage ? texture->getFormat(target, level).info->sizedInternalFormat
                           : internalformat;
            switch (actualInternalFormat)
            {
                case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
                case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
                    if (!context->getExtensions().textureCompressionDXT1)
                    {
                        ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidInternalFormat);
                        return false;
                    }
                    break;
                case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
                    if (!context->getExtensions().textureCompressionDXT1)
                    {
                        ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidInternalFormat);
                        return false;
                    }
                    break;
                case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
                    if (!context->getExtensions().textureCompressionDXT5)
                    {
                        ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidInternalFormat);
                        return false;
                    }
                    break;
                case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:
                case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
                case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:
                case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
                    if (!context->getExtensions().textureCompressionS3TCsRGB)
                    {
                        ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidInternalFormat);
                        return false;
                    }
                    break;
                case GL_ETC1_RGB8_OES:
                    if (!context->getExtensions().compressedETC1RGB8Texture)
                    {
                        ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidInternalFormat);
                        return false;
                    }
                    break;
                case GL_ETC1_RGB8_LOSSY_DECODE_ANGLE:
                case GL_COMPRESSED_RGB8_LOSSY_DECODE_ETC2_ANGLE:
                case GL_COMPRESSED_SRGB8_LOSSY_DECODE_ETC2_ANGLE:
                case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_LOSSY_DECODE_ETC2_ANGLE:
                case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_LOSSY_DECODE_ETC2_ANGLE:
                    if (!context->getExtensions().lossyETCDecode)
                    {
                        context->handleError(InvalidEnum()
                                             << "ANGLE_lossy_etc_decode extension is not supported");
                        return false;
                    }
                    break;
                default:
                    ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidInternalFormat);
                    return false;
            }
    
            if (isSubImage)
            {
                if (!ValidCompressedSubImageSize(context, actualInternalFormat, xoffset, yoffset, width,
                                                 height, texture->getWidth(target, level),
                                                 texture->getHeight(target, level)))
                {
                    context->handleError(InvalidOperation() << "Invalid compressed format dimension.");
                    return false;
                }
    
                if (format != actualInternalFormat)
                {
                    ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidFormat);
                    return false;
                }
            }
            else
            {
                if (!ValidCompressedImageSize(context, actualInternalFormat, level, width, height))
                {
                    context->handleError(InvalidOperation() << "Invalid compressed format dimension.");
                    return 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:
                    ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidType);
                    return 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:
                            ANGLE_VALIDATION_ERR(context, InvalidOperation(), MismatchedTypeAndFormat);
                            return false;
                    }
                    break;
                case GL_RED:
                case GL_RG:
                    if (!context->getExtensions().textureRG)
                    {
                        context->handleError(InvalidEnum());
                        return false;
                    }
                    switch (type)
                    {
                        case GL_UNSIGNED_BYTE:
                        case GL_FLOAT:
                        case GL_HALF_FLOAT_OES:
                            break;
                        default:
                            ANGLE_VALIDATION_ERR(context, InvalidOperation(), MismatchedTypeAndFormat);
                            return 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:
                            ANGLE_VALIDATION_ERR(context, InvalidOperation(), MismatchedTypeAndFormat);
                            return 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:
                            ANGLE_VALIDATION_ERR(context, InvalidOperation(), MismatchedTypeAndFormat);
                            return false;
                    }
                    break;
                case GL_BGRA_EXT:
                    switch (type)
                    {
                        case GL_UNSIGNED_BYTE:
                            break;
                        default:
                            ANGLE_VALIDATION_ERR(context, InvalidOperation(), MismatchedTypeAndFormat);
                            return false;
                    }
                    break;
                case GL_SRGB_EXT:
                case GL_SRGB_ALPHA_EXT:
                    if (!context->getExtensions().sRGB)
                    {
                        context->handleError(InvalidEnum());
                        return false;
                    }
                    switch (type)
                    {
                        case GL_UNSIGNED_BYTE:
                            break;
                        default:
                            ANGLE_VALIDATION_ERR(context, InvalidOperation(), MismatchedTypeAndFormat);
                            return 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:
                            ANGLE_VALIDATION_ERR(context, InvalidOperation(), MismatchedTypeAndFormat);
                            return false;
                    }
                    break;
                case GL_DEPTH_STENCIL_OES:
                    switch (type)
                    {
                        case GL_UNSIGNED_INT_24_8_OES:
                            break;
                        default:
                            ANGLE_VALIDATION_ERR(context, InvalidOperation(), MismatchedTypeAndFormat);
                            return false;
                    }
                    break;
                default:
                    context->handleError(InvalidEnum());
                    return false;
            }
    
            switch (format)
            {
                case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
                case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
                    if (context->getExtensions().textureCompressionDXT1)
                    {
                        context->handleError(InvalidOperation());
                        return false;
                    }
                    else
                    {
                        context->handleError(InvalidEnum());
                        return false;
                    }
                    break;
                case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
                    if (context->getExtensions().textureCompressionDXT3)
                    {
                        context->handleError(InvalidOperation());
                        return false;
                    }
                    else
                    {
                        context->handleError(InvalidEnum());
                        return false;
                    }
                    break;
                case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
                    if (context->getExtensions().textureCompressionDXT5)
                    {
                        context->handleError(InvalidOperation());
                        return false;
                    }
                    else
                    {
                        context->handleError(InvalidEnum());
                        return false;
                    }
                    break;
                case GL_ETC1_RGB8_OES:
                    if (context->getExtensions().compressedETC1RGB8Texture)
                    {
                        context->handleError(InvalidOperation());
                        return false;
                    }
                    else
                    {
                        context->handleError(InvalidEnum());
                        return false;
                    }
                    break;
                case GL_ETC1_RGB8_LOSSY_DECODE_ANGLE:
                case GL_COMPRESSED_RGB8_LOSSY_DECODE_ETC2_ANGLE:
                case GL_COMPRESSED_SRGB8_LOSSY_DECODE_ETC2_ANGLE:
                case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_LOSSY_DECODE_ETC2_ANGLE:
                case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_LOSSY_DECODE_ETC2_ANGLE:
                    if (context->getExtensions().lossyETCDecode)
                    {
                        context->handleError(InvalidOperation()
                                             << "ETC lossy decode formats can't work with this type.");
                        return false;
                    }
                    else
                    {
                        context->handleError(InvalidEnum()
                                             << "ANGLE_lossy_etc_decode extension is not supported.");
                        return false;
                    }
                    break;
                case GL_DEPTH_COMPONENT:
                case GL_DEPTH_STENCIL_OES:
                    if (!context->getExtensions().depthTextures)
                    {
                        context->handleError(InvalidValue());
                        return false;
                    }
                    if (target != GL_TEXTURE_2D)
                    {
                        context->handleError(InvalidOperation());
                        return false;
                    }
                    // OES_depth_texture supports loading depth data and multiple levels,
                    // but ANGLE_depth_texture does not
                    if (pixels != nullptr || level != 0)
                    {
                        context->handleError(InvalidOperation());
                        return false;
                    }
                    break;
                default:
                    break;
            }
    
            if (!isSubImage)
            {
                switch (internalformat)
                {
                    case GL_RGBA32F:
                        if (!context->getExtensions().colorBufferFloatRGBA)
                        {
                            context->handleError(InvalidValue()
                                                 << "Sized GL_RGBA32F internal format requires "
                                                    "GL_CHROMIUM_color_buffer_float_rgba");
                            return false;
                        }
                        if (type != GL_FLOAT)
                        {
                            ANGLE_VALIDATION_ERR(context, InvalidOperation(), MismatchedTypeAndFormat);
                            return false;
                        }
                        if (format != GL_RGBA)
                        {
                            ANGLE_VALIDATION_ERR(context, InvalidOperation(), MismatchedTypeAndFormat);
                            return false;
                        }
                        break;
    
                    case GL_RGB32F:
                        if (!context->getExtensions().colorBufferFloatRGB)
                        {
                            context->handleError(InvalidValue()
                                                 << "Sized GL_RGB32F internal format requires "
                                                    "GL_CHROMIUM_color_buffer_float_rgb");
                            return false;
                        }
                        if (type != GL_FLOAT)
                        {
                            ANGLE_VALIDATION_ERR(context, InvalidOperation(), MismatchedTypeAndFormat);
                            return false;
                        }
                        if (format != GL_RGB)
                        {
                            ANGLE_VALIDATION_ERR(context, InvalidOperation(), MismatchedTypeAndFormat);
                            return false;
                        }
                        break;
    
                    default:
                        break;
                }
            }
    
            if (type == GL_FLOAT)
            {
                if (!context->getExtensions().textureFloat)
                {
                    context->handleError(InvalidEnum());
                    return false;
                }
            }
            else if (type == GL_HALF_FLOAT_OES)
            {
                if (!context->getExtensions().textureHalfFloat)
                {
                    context->handleError(InvalidEnum());
                    return false;
                }
            }
        }
    
        GLenum sizeCheckFormat = isSubImage ? format : internalformat;
        if (!ValidImageDataSize(context, target, width, height, 1, sizeCheckFormat, type, pixels,
                                imageSize))
        {
            return false;
        }
    
        return true;
    }
    
    bool ValidateES2TexStorageParameters(Context *context,
                                         GLenum target,
                                         GLsizei levels,
                                         GLenum internalformat,
                                         GLsizei width,
                                         GLsizei height)
    {
        if (target != GL_TEXTURE_2D && target != GL_TEXTURE_CUBE_MAP)
        {
            context->handleError(InvalidEnum());
            return false;
        }
    
        if (width < 1 || height < 1 || levels < 1)
        {
            context->handleError(InvalidValue());
            return false;
        }
    
        if (target == GL_TEXTURE_CUBE_MAP && width != height)
        {
            context->handleError(InvalidValue());
            return false;
        }
    
        if (levels != 1 && levels != gl::log2(std::max(width, height)) + 1)
        {
            context->handleError(InvalidOperation());
            return false;
        }
    
        const gl::InternalFormat &formatInfo = gl::GetSizedInternalFormatInfo(internalformat);
        if (formatInfo.format == GL_NONE || formatInfo.type == GL_NONE)
        {
            context->handleError(InvalidEnum());
            return false;
        }
    
        const gl::Caps &caps = context->getCaps();
    
        switch (target)
        {
            case GL_TEXTURE_2D:
                if (static_cast<GLuint>(width) > caps.max2DTextureSize ||
                    static_cast<GLuint>(height) > caps.max2DTextureSize)
                {
                    context->handleError(InvalidValue());
                    return false;
                }
                break;
            case GL_TEXTURE_CUBE_MAP:
                if (static_cast<GLuint>(width) > caps.maxCubeMapTextureSize ||
                    static_cast<GLuint>(height) > caps.maxCubeMapTextureSize)
                {
                    context->handleError(InvalidValue());
                    return false;
                }
                break;
            default:
                context->handleError(InvalidEnum());
                return false;
        }
    
        if (levels != 1 && !context->getExtensions().textureNPOT)
        {
            if (!gl::isPow2(width) || !gl::isPow2(height))
            {
                context->handleError(InvalidOperation());
                return false;
            }
        }
    
        switch (internalformat)
        {
            case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
            case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
                if (!context->getExtensions().textureCompressionDXT1)
                {
                    context->handleError(InvalidEnum());
                    return false;
                }
                break;
            case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
                if (!context->getExtensions().textureCompressionDXT3)
                {
                    context->handleError(InvalidEnum());
                    return false;
                }
                break;
            case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
                if (!context->getExtensions().textureCompressionDXT5)
                {
                    context->handleError(InvalidEnum());
                    return false;
                }
                break;
            case GL_ETC1_RGB8_OES:
                if (!context->getExtensions().compressedETC1RGB8Texture)
                {
                    context->handleError(InvalidEnum());
                    return false;
                }
                break;
            case GL_ETC1_RGB8_LOSSY_DECODE_ANGLE:
            case GL_COMPRESSED_RGB8_LOSSY_DECODE_ETC2_ANGLE:
            case GL_COMPRESSED_SRGB8_LOSSY_DECODE_ETC2_ANGLE:
            case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_LOSSY_DECODE_ETC2_ANGLE:
            case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_LOSSY_DECODE_ETC2_ANGLE:
                if (!context->getExtensions().lossyETCDecode)
                {
                    context->handleError(InvalidEnum()
                                         << "ANGLE_lossy_etc_decode extension is not supported.");
                    return false;
                }
                break;
            case GL_RGBA32F_EXT:
            case GL_RGB32F_EXT:
            case GL_ALPHA32F_EXT:
            case GL_LUMINANCE32F_EXT:
            case GL_LUMINANCE_ALPHA32F_EXT:
                if (!context->getExtensions().textureFloat)
                {
                    context->handleError(InvalidEnum());
                    return false;
                }
                break;
            case GL_RGBA16F_EXT:
            case GL_RGB16F_EXT:
            case GL_ALPHA16F_EXT:
            case GL_LUMINANCE16F_EXT:
            case GL_LUMINANCE_ALPHA16F_EXT:
                if (!context->getExtensions().textureHalfFloat)
                {
                    context->handleError(InvalidEnum());
                    return false;
                }
                break;
            case GL_R8_EXT:
            case GL_RG8_EXT:
                if (!context->getExtensions().textureRG)
                {
                    context->handleError(InvalidEnum());
                    return false;
                }
                break;
            case GL_R16F_EXT:
            case GL_RG16F_EXT:
                if (!context->getExtensions().textureRG || !context->getExtensions().textureHalfFloat)
                {
                    context->handleError(InvalidEnum());
                    return false;
                }
                break;
            case GL_R32F_EXT:
            case GL_RG32F_EXT:
                if (!context->getExtensions().textureRG || !context->getExtensions().textureFloat)
                {
                    context->handleError(InvalidEnum());
                    return false;
                }
                break;
            case GL_DEPTH_COMPONENT16:
            case GL_DEPTH_COMPONENT32_OES:
            case GL_DEPTH24_STENCIL8_OES:
                if (!context->getExtensions().depthTextures)
                {
                    context->handleError(InvalidEnum());
                    return false;
                }
                if (target != GL_TEXTURE_2D)
                {
                    context->handleError(InvalidOperation());
                    return false;
                }
                // ANGLE_depth_texture only supports 1-level textures
                if (levels != 1)
                {
                    context->handleError(InvalidOperation());
                    return false;
                }
                break;
            default:
                break;
        }
    
        gl::Texture *texture = context->getTargetTexture(target);
        if (!texture || texture->id() == 0)
        {
            context->handleError(InvalidOperation());
            return false;
        }
    
        if (texture->getImmutableFormat())
        {
            context->handleError(InvalidOperation());
            return false;
        }
    
        return true;
    }
    
    bool ValidateDiscardFramebufferEXT(Context *context,
                                       GLenum target,
                                       GLsizei numAttachments,
                                       const GLenum *attachments)
    {
        if (!context->getExtensions().discardFramebuffer)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ExtensionNotEnabled);
            return false;
        }
    
        bool defaultFramebuffer = false;
    
        switch (target)
        {
            case GL_FRAMEBUFFER:
                defaultFramebuffer =
                    (context->getGLState().getTargetFramebuffer(GL_FRAMEBUFFER)->id() == 0);
                break;
            default:
                ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidFramebufferTarget);
                return false;
        }
    
        return ValidateDiscardFramebufferBase(context, target, numAttachments, attachments,
                                              defaultFramebuffer);
    }
    
    bool ValidateBindVertexArrayOES(Context *context, GLuint array)
    {
        if (!context->getExtensions().vertexArrayObject)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ExtensionNotEnabled);
            return false;
        }
    
        return ValidateBindVertexArrayBase(context, array);
    }
    
    bool ValidateDeleteVertexArraysOES(Context *context, GLsizei n)
    {
        if (!context->getExtensions().vertexArrayObject)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ExtensionNotEnabled);
            return false;
        }
    
        return ValidateGenOrDelete(context, n);
    }
    
    bool ValidateGenVertexArraysOES(Context *context, GLsizei n)
    {
        if (!context->getExtensions().vertexArrayObject)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ExtensionNotEnabled);
            return false;
        }
    
        return ValidateGenOrDelete(context, n);
    }
    
    bool ValidateIsVertexArrayOES(Context *context)
    {
        if (!context->getExtensions().vertexArrayObject)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ExtensionNotEnabled);
            return false;
        }
    
        return true;
    }
    
    bool ValidateProgramBinaryOES(Context *context,
                                  GLuint program,
                                  GLenum binaryFormat,
                                  const void *binary,
                                  GLint length)
    {
        if (!context->getExtensions().getProgramBinary)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ExtensionNotEnabled);
            return false;
        }
    
        return ValidateProgramBinaryBase(context, program, binaryFormat, binary, length);
    }
    
    bool ValidateGetProgramBinaryOES(Context *context,
                                     GLuint program,
                                     GLsizei bufSize,
                                     GLsizei *length,
                                     GLenum *binaryFormat,
                                     void *binary)
    {
        if (!context->getExtensions().getProgramBinary)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ExtensionNotEnabled);
            return false;
        }
    
        return ValidateGetProgramBinaryBase(context, program, bufSize, length, binaryFormat, binary);
    }
    
    static bool ValidDebugSource(GLenum source, bool mustBeThirdPartyOrApplication)
    {
        switch (source)
        {
            case GL_DEBUG_SOURCE_API:
            case GL_DEBUG_SOURCE_SHADER_COMPILER:
            case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
            case GL_DEBUG_SOURCE_OTHER:
                // Only THIRD_PARTY and APPLICATION sources are allowed to be manually inserted
                return !mustBeThirdPartyOrApplication;
    
            case GL_DEBUG_SOURCE_THIRD_PARTY:
            case GL_DEBUG_SOURCE_APPLICATION:
                return true;
    
            default:
                return false;
        }
    }
    
    static bool ValidDebugType(GLenum type)
    {
        switch (type)
        {
            case GL_DEBUG_TYPE_ERROR:
            case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
            case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
            case GL_DEBUG_TYPE_PERFORMANCE:
            case GL_DEBUG_TYPE_PORTABILITY:
            case GL_DEBUG_TYPE_OTHER:
            case GL_DEBUG_TYPE_MARKER:
            case GL_DEBUG_TYPE_PUSH_GROUP:
            case GL_DEBUG_TYPE_POP_GROUP:
                return true;
    
            default:
                return false;
        }
    }
    
    static bool ValidDebugSeverity(GLenum severity)
    {
        switch (severity)
        {
            case GL_DEBUG_SEVERITY_HIGH:
            case GL_DEBUG_SEVERITY_MEDIUM:
            case GL_DEBUG_SEVERITY_LOW:
            case GL_DEBUG_SEVERITY_NOTIFICATION:
                return true;
    
            default:
                return false;
        }
    }
    
    bool ValidateDebugMessageControlKHR(Context *context,
                                        GLenum source,
                                        GLenum type,
                                        GLenum severity,
                                        GLsizei count,
                                        const GLuint *ids,
                                        GLboolean enabled)
    {
        if (!context->getExtensions().debug)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ExtensionNotEnabled);
            return false;
        }
    
        if (!ValidDebugSource(source, false) && source != GL_DONT_CARE)
        {
            context->handleError(InvalidEnum() << "Invalid debug source.");
            return false;
        }
    
        if (!ValidDebugType(type) && type != GL_DONT_CARE)
        {
            context->handleError(InvalidEnum() << "Invalid debug type.");
            return false;
        }
    
        if (!ValidDebugSeverity(severity) && severity != GL_DONT_CARE)
        {
            context->handleError(InvalidEnum() << "Invalid debug severity.");
            return false;
        }
    
        if (count > 0)
        {
            if (source == GL_DONT_CARE || type == GL_DONT_CARE)
            {
                context->handleError(
                    InvalidOperation()
                    << "If count is greater than zero, source and severity cannot be GL_DONT_CARE.");
                return false;
            }
    
            if (severity != GL_DONT_CARE)
            {
                context->handleError(
                    InvalidOperation()
                    << "If count is greater than zero, severity must be GL_DONT_CARE.");
                return false;
            }
        }
    
        return true;
    }
    
    bool ValidateDebugMessageInsertKHR(Context *context,
                                       GLenum source,
                                       GLenum type,
                                       GLuint id,
                                       GLenum severity,
                                       GLsizei length,
                                       const GLchar *buf)
    {
        if (!context->getExtensions().debug)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ExtensionNotEnabled);
            return false;
        }
    
        if (!context->getGLState().getDebug().isOutputEnabled())
        {
            // If the DEBUG_OUTPUT state is disabled calls to DebugMessageInsert are discarded and do
            // not generate an error.
            return false;
        }
    
        if (!ValidDebugSeverity(severity))
        {
            context->handleError(InvalidEnum() << "Invalid debug severity.");
            return false;
        }
    
        if (!ValidDebugType(type))
        {
            context->handleError(InvalidEnum() << "Invalid debug type.");
            return false;
        }
    
        if (!ValidDebugSource(source, true))
        {
            context->handleError(InvalidEnum() << "Invalid debug source.");
            return false;
        }
    
        size_t messageLength = (length < 0) ? strlen(buf) : length;
        if (messageLength > context->getExtensions().maxDebugMessageLength)
        {
            context->handleError(InvalidValue()
                                 << "Message length is larger than GL_MAX_DEBUG_MESSAGE_LENGTH.");
            return false;
        }
    
        return true;
    }
    
    bool ValidateDebugMessageCallbackKHR(Context *context,
                                         GLDEBUGPROCKHR callback,
                                         const void *userParam)
    {
        if (!context->getExtensions().debug)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ExtensionNotEnabled);
            return false;
        }
    
        return true;
    }
    
    bool ValidateGetDebugMessageLogKHR(Context *context,
                                       GLuint count,
                                       GLsizei bufSize,
                                       GLenum *sources,
                                       GLenum *types,
                                       GLuint *ids,
                                       GLenum *severities,
                                       GLsizei *lengths,
                                       GLchar *messageLog)
    {
        if (!context->getExtensions().debug)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ExtensionNotEnabled);
            return false;
        }
    
        if (bufSize < 0 && messageLog != nullptr)
        {
            ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeBufferSize);
            return false;
        }
    
        return true;
    }
    
    bool ValidatePushDebugGroupKHR(Context *context,
                                   GLenum source,
                                   GLuint id,
                                   GLsizei length,
                                   const GLchar *message)
    {
        if (!context->getExtensions().debug)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ExtensionNotEnabled);
            return false;
        }
    
        if (!ValidDebugSource(source, true))
        {
            context->handleError(InvalidEnum() << "Invalid debug source.");
            return false;
        }
    
        size_t messageLength = (length < 0) ? strlen(message) : length;
        if (messageLength > context->getExtensions().maxDebugMessageLength)
        {
            context->handleError(InvalidValue()
                                 << "Message length is larger than GL_MAX_DEBUG_MESSAGE_LENGTH.");
            return false;
        }
    
        size_t currentStackSize = context->getGLState().getDebug().getGroupStackDepth();
        if (currentStackSize >= context->getExtensions().maxDebugGroupStackDepth)
        {
            context
                ->handleError(StackOverflow()
                              << "Cannot push more than GL_MAX_DEBUG_GROUP_STACK_DEPTH debug groups.");
            return false;
        }
    
        return true;
    }
    
    bool ValidatePopDebugGroupKHR(Context *context)
    {
        if (!context->getExtensions().debug)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ExtensionNotEnabled);
            return false;
        }
    
        size_t currentStackSize = context->getGLState().getDebug().getGroupStackDepth();
        if (currentStackSize <= 1)
        {
            context->handleError(StackUnderflow() << "Cannot pop the default debug group.");
            return false;
        }
    
        return true;
    }
    
    static bool ValidateObjectIdentifierAndName(Context *context, GLenum identifier, GLuint name)
    {
        switch (identifier)
        {
            case GL_BUFFER:
                if (context->getBuffer(name) == nullptr)
                {
                    context->handleError(InvalidValue() << "name is not a valid buffer.");
                    return false;
                }
                return true;
    
            case GL_SHADER:
                if (context->getShader(name) == nullptr)
                {
                    context->handleError(InvalidValue() << "name is not a valid shader.");
                    return false;
                }
                return true;
    
            case GL_PROGRAM:
                if (context->getProgram(name) == nullptr)
                {
                    context->handleError(InvalidValue() << "name is not a valid program.");
                    return false;
                }
                return true;
    
            case GL_VERTEX_ARRAY:
                if (context->getVertexArray(name) == nullptr)
                {
                    context->handleError(InvalidValue() << "name is not a valid vertex array.");
                    return false;
                }
                return true;
    
            case GL_QUERY:
                if (context->getQuery(name) == nullptr)
                {
                    context->handleError(InvalidValue() << "name is not a valid query.");
                    return false;
                }
                return true;
    
            case GL_TRANSFORM_FEEDBACK:
                if (context->getTransformFeedback(name) == nullptr)
                {
                    context->handleError(InvalidValue() << "name is not a valid transform feedback.");
                    return false;
                }
                return true;
    
            case GL_SAMPLER:
                if (context->getSampler(name) == nullptr)
                {
                    context->handleError(InvalidValue() << "name is not a valid sampler.");
                    return false;
                }
                return true;
    
            case GL_TEXTURE:
                if (context->getTexture(name) == nullptr)
                {
                    context->handleError(InvalidValue() << "name is not a valid texture.");
                    return false;
                }
                return true;
    
            case GL_RENDERBUFFER:
                if (context->getRenderbuffer(name) == nullptr)
                {
                    context->handleError(InvalidValue() << "name is not a valid renderbuffer.");
                    return false;
                }
                return true;
    
            case GL_FRAMEBUFFER:
                if (context->getFramebuffer(name) == nullptr)
                {
                    context->handleError(InvalidValue() << "name is not a valid framebuffer.");
                    return false;
                }
                return true;
    
            default:
                context->handleError(InvalidEnum() << "Invalid identifier.");
                return false;
        }
    }
    
    static bool ValidateLabelLength(Context *context, GLsizei length, const GLchar *label)
    {
        size_t labelLength = 0;
    
        if (length < 0)
        {
            if (label != nullptr)
            {
                labelLength = strlen(label);
            }
        }
        else
        {
            labelLength = static_cast<size_t>(length);
        }
    
        if (labelLength > context->getExtensions().maxLabelLength)
        {
            context->handleError(InvalidValue() << "Label length is larger than GL_MAX_LABEL_LENGTH.");
            return false;
        }
    
        return true;
    }
    
    bool ValidateObjectLabelKHR(Context *context,
                                GLenum identifier,
                                GLuint name,
                                GLsizei length,
                                const GLchar *label)
    {
        if (!context->getExtensions().debug)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ExtensionNotEnabled);
            return false;
        }
    
        if (!ValidateObjectIdentifierAndName(context, identifier, name))
        {
            return false;
        }
    
        if (!ValidateLabelLength(context, length, label))
        {
            return false;
        }
    
        return true;
    }
    
    bool ValidateGetObjectLabelKHR(Context *context,
                                   GLenum identifier,
                                   GLuint name,
                                   GLsizei bufSize,
                                   GLsizei *length,
                                   GLchar *label)
    {
        if (!context->getExtensions().debug)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ExtensionNotEnabled);
            return false;
        }
    
        if (bufSize < 0)
        {
            ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeBufferSize);
            return false;
        }
    
        if (!ValidateObjectIdentifierAndName(context, identifier, name))
        {
            return false;
        }
    
        return true;
    }
    
    static bool ValidateObjectPtrName(Context *context, const void *ptr)
    {
        if (context->getFenceSync(reinterpret_cast<GLsync>(const_cast<void *>(ptr))) == nullptr)
        {
            context->handleError(InvalidValue() << "name is not a valid sync.");
            return false;
        }
    
        return true;
    }
    
    bool ValidateObjectPtrLabelKHR(Context *context,
                                   const void *ptr,
                                   GLsizei length,
                                   const GLchar *label)
    {
        if (!context->getExtensions().debug)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ExtensionNotEnabled);
            return false;
        }
    
        if (!ValidateObjectPtrName(context, ptr))
        {
            return false;
        }
    
        if (!ValidateLabelLength(context, length, label))
        {
            return false;
        }
    
        return true;
    }
    
    bool ValidateGetObjectPtrLabelKHR(Context *context,
                                      const void *ptr,
                                      GLsizei bufSize,
                                      GLsizei *length,
                                      GLchar *label)
    {
        if (!context->getExtensions().debug)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ExtensionNotEnabled);
            return false;
        }
    
        if (bufSize < 0)
        {
            ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeBufferSize);
            return false;
        }
    
        if (!ValidateObjectPtrName(context, ptr))
        {
            return false;
        }
    
        return true;
    }
    
    bool ValidateGetPointervKHR(Context *context, GLenum pname, void **params)
    {
        if (!context->getExtensions().debug)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ExtensionNotEnabled);
            return false;
        }
    
        // TODO: represent this in Context::getQueryParameterInfo.
        switch (pname)
        {
            case GL_DEBUG_CALLBACK_FUNCTION:
            case GL_DEBUG_CALLBACK_USER_PARAM:
                break;
    
            default:
                ANGLE_VALIDATION_ERR(context, InvalidEnum(), EnumNotSupported);
                return false;
        }
    
        return true;
    }
    
    bool ValidateBlitFramebufferANGLE(Context *context,
                                      GLint srcX0,
                                      GLint srcY0,
                                      GLint srcX1,
                                      GLint srcY1,
                                      GLint dstX0,
                                      GLint dstY0,
                                      GLint dstX1,
                                      GLint dstY1,
                                      GLbitfield mask,
                                      GLenum filter)
    {
        if (!context->getExtensions().framebufferBlit)
        {
            context->handleError(InvalidOperation() << "Blit extension not available.");
            return false;
        }
    
        if (srcX1 - srcX0 != dstX1 - dstX0 || srcY1 - srcY0 != dstY1 - dstY0)
        {
            // TODO(jmadill): Determine if this should be available on other implementations.
            context->handleError(InvalidOperation() << "Scaling and flipping in "
                                                       "BlitFramebufferANGLE not supported by this "
                                                       "implementation.");
            return false;
        }
    
        if (filter == GL_LINEAR)
        {
            context->handleError(InvalidEnum() << "Linear blit not supported in this extension");
            return false;
        }
    
        Framebuffer *readFramebuffer = context->getGLState().getReadFramebuffer();
        Framebuffer *drawFramebuffer = context->getGLState().getDrawFramebuffer();
    
        if (mask & GL_COLOR_BUFFER_BIT)
        {
            const FramebufferAttachment *readColorAttachment = readFramebuffer->getReadColorbuffer();
            const FramebufferAttachment *drawColorAttachment = drawFramebuffer->getFirstColorbuffer();
    
            if (readColorAttachment && drawColorAttachment)
            {
                if (!(readColorAttachment->type() == GL_TEXTURE &&
                      readColorAttachment->getTextureImageIndex().type == GL_TEXTURE_2D) &&
                    readColorAttachment->type() != GL_RENDERBUFFER &&
                    readColorAttachment->type() != GL_FRAMEBUFFER_DEFAULT)
                {
                    context->handleError(InvalidOperation());
                    return false;
                }
    
                for (size_t drawbufferIdx = 0;
                     drawbufferIdx < drawFramebuffer->getDrawbufferStateCount(); ++drawbufferIdx)
                {
                    const FramebufferAttachment *attachment =
                        drawFramebuffer->getDrawBuffer(drawbufferIdx);
                    if (attachment)
                    {
                        if (!(attachment->type() == GL_TEXTURE &&
                              attachment->getTextureImageIndex().type == GL_TEXTURE_2D) &&
                            attachment->type() != GL_RENDERBUFFER &&
                            attachment->type() != GL_FRAMEBUFFER_DEFAULT)
                        {
                            context->handleError(InvalidOperation());
                            return false;
                        }
    
                        // Return an error if the destination formats do not match
                        if (!Format::SameSized(attachment->getFormat(),
                                               readColorAttachment->getFormat()))
                        {
                            context->handleError(InvalidOperation());
                            return false;
                        }
                    }
                }
    
                if (readFramebuffer->getSamples(context) != 0 &&
                    IsPartialBlit(context, readColorAttachment, drawColorAttachment, srcX0, srcY0,
                                  srcX1, srcY1, dstX0, dstY0, dstX1, dstY1))
                {
                    context->handleError(InvalidOperation());
                    return false;
                }
            }
        }
    
        GLenum masks[]       = {GL_DEPTH_BUFFER_BIT, GL_STENCIL_BUFFER_BIT};
        GLenum attachments[] = {GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT};
        for (size_t i = 0; i < 2; i++)
        {
            if (mask & masks[i])
            {
                const FramebufferAttachment *readBuffer =
                    readFramebuffer->getAttachment(attachments[i]);
                const FramebufferAttachment *drawBuffer =
                    drawFramebuffer->getAttachment(attachments[i]);
    
                if (readBuffer && drawBuffer)
                {
                    if (IsPartialBlit(context, readBuffer, drawBuffer, srcX0, srcY0, srcX1, srcY1,
                                      dstX0, dstY0, dstX1, dstY1))
                    {
                        // only whole-buffer copies are permitted
                        context->handleError(InvalidOperation() << "Only whole-buffer depth and "
                                                                   "stencil blits are supported by "
                                                                   "this extension.");
                        return false;
                    }
    
                    if (readBuffer->getSamples() != 0 || drawBuffer->getSamples() != 0)
                    {
                        context->handleError(InvalidOperation());
                        return false;
                    }
                }
            }
        }
    
        return ValidateBlitFramebufferParameters(context, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0,
                                                 dstX1, dstY1, mask, filter);
    }
    
    bool ValidateClear(ValidationContext *context, GLbitfield mask)
    {
        auto fbo = context->getGLState().getDrawFramebuffer();
        if (fbo->checkStatus(context) != GL_FRAMEBUFFER_COMPLETE)
        {
            context->handleError(InvalidFramebufferOperation());
            return false;
        }
    
        if ((mask & ~(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)) != 0)
        {
            ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidClearMask);
            return false;
        }
    
        if (context->getExtensions().webglCompatibility && (mask & GL_COLOR_BUFFER_BIT) != 0)
        {
            constexpr GLenum validComponentTypes[] = {GL_FLOAT, GL_UNSIGNED_NORMALIZED,
                                                      GL_SIGNED_NORMALIZED};
    
            for (GLuint drawBufferIdx = 0; drawBufferIdx < fbo->getDrawbufferStateCount();
                 drawBufferIdx++)
            {
                if (!ValidateWebGLFramebufferAttachmentClearType(
                        context, drawBufferIdx, validComponentTypes, ArraySize(validComponentTypes)))
                {
                    return false;
                }
            }
        }
    
        return true;
    }
    
    bool ValidateDrawBuffersEXT(ValidationContext *context, GLsizei n, const GLenum *bufs)
    {
        if (!context->getExtensions().drawBuffers)
        {
            context->handleError(InvalidOperation() << "Extension not supported.");
            return false;
        }
    
        return ValidateDrawBuffersBase(context, n, bufs);
    }
    
    bool ValidateTexImage2D(Context *context,
                            GLenum target,
                            GLint level,
                            GLint internalformat,
                            GLsizei width,
                            GLsizei height,
                            GLint border,
                            GLenum format,
                            GLenum type,
                            const void *pixels)
    {
        if (context->getClientMajorVersion() < 3)
        {
            return ValidateES2TexImageParameters(context, target, level, internalformat, false, false,
                                                 0, 0, width, height, border, format, type, -1, pixels);
        }
    
        ASSERT(context->getClientMajorVersion() >= 3);
        return ValidateES3TexImage2DParameters(context, target, level, internalformat, false, false, 0,
                                               0, 0, width, height, 1, border, format, type, -1,
                                               pixels);
    }
    
    bool ValidateTexImage2DRobust(Context *context,
                                  GLenum target,
                                  GLint level,
                                  GLint internalformat,
                                  GLsizei width,
                                  GLsizei height,
                                  GLint border,
                                  GLenum format,
                                  GLenum type,
                                  GLsizei bufSize,
                                  const void *pixels)
    {
        if (!ValidateRobustEntryPoint(context, bufSize))
        {
            return false;
        }
    
        if (context->getClientMajorVersion() < 3)
        {
            return ValidateES2TexImageParameters(context, target, level, internalformat, false, false,
                                                 0, 0, width, height, border, format, type, bufSize,
                                                 pixels);
        }
    
        ASSERT(context->getClientMajorVersion() >= 3);
        return ValidateES3TexImage2DParameters(context, target, level, internalformat, false, false, 0,
                                               0, 0, width, height, 1, border, format, type, bufSize,
                                               pixels);
    }
    
    bool ValidateTexSubImage2D(Context *context,
                               GLenum target,
                               GLint level,
                               GLint xoffset,
                               GLint yoffset,
                               GLsizei width,
                               GLsizei height,
                               GLenum format,
                               GLenum type,
                               const void *pixels)
    {
    
        if (context->getClientMajorVersion() < 3)
        {
            return ValidateES2TexImageParameters(context, target, level, GL_NONE, false, true, xoffset,
                                                 yoffset, width, height, 0, format, type, -1, pixels);
        }
    
        ASSERT(context->getClientMajorVersion() >= 3);
        return ValidateES3TexImage2DParameters(context, target, level, GL_NONE, false, true, xoffset,
                                               yoffset, 0, width, height, 1, 0, format, type, -1,
                                               pixels);
    }
    
    bool ValidateTexSubImage2DRobustANGLE(Context *context,
                                          GLenum target,
                                          GLint level,
                                          GLint xoffset,
                                          GLint yoffset,
                                          GLsizei width,
                                          GLsizei height,
                                          GLenum format,
                                          GLenum type,
                                          GLsizei bufSize,
                                          const void *pixels)
    {
        if (!ValidateRobustEntryPoint(context, bufSize))
        {
            return false;
        }
    
        if (context->getClientMajorVersion() < 3)
        {
            return ValidateES2TexImageParameters(context, target, level, GL_NONE, false, true, xoffset,
                                                 yoffset, width, height, 0, format, type, bufSize,
                                                 pixels);
        }
    
        ASSERT(context->getClientMajorVersion() >= 3);
        return ValidateES3TexImage2DParameters(context, target, level, GL_NONE, false, true, xoffset,
                                               yoffset, 0, width, height, 1, 0, format, type, bufSize,
                                               pixels);
    }
    
    bool ValidateCompressedTexImage2D(Context *context,
                                      GLenum target,
                                      GLint level,
                                      GLenum internalformat,
                                      GLsizei width,
                                      GLsizei height,
                                      GLint border,
                                      GLsizei imageSize,
                                      const void *data)
    {
        if (context->getClientMajorVersion() < 3)
        {
            if (!ValidateES2TexImageParameters(context, target, level, internalformat, true, false, 0,
                                               0, width, height, border, GL_NONE, GL_NONE, -1, data))
            {
                return false;
            }
        }
        else
        {
            ASSERT(context->getClientMajorVersion() >= 3);
            if (!ValidateES3TexImage2DParameters(context, target, level, internalformat, true, false, 0,
                                                 0, 0, width, height, 1, border, GL_NONE, GL_NONE, -1,
                                                 data))
            {
                return false;
            }
        }
    
        const InternalFormat &formatInfo = GetSizedInternalFormatInfo(internalformat);
        auto blockSizeOrErr =
            formatInfo.computeCompressedImageSize(GL_UNSIGNED_BYTE, gl::Extents(width, height, 1));
        if (blockSizeOrErr.isError())
        {
            context->handleError(blockSizeOrErr.getError());
            return false;
        }
    
        if (imageSize < 0 || static_cast<GLuint>(imageSize) != blockSizeOrErr.getResult())
        {
            context->handleError(InvalidValue());
            return false;
        }
    
        return true;
    }
    
    bool ValidateCompressedTexImage2DRobustANGLE(Context *context,
                                                 GLenum target,
                                                 GLint level,
                                                 GLenum internalformat,
                                                 GLsizei width,
                                                 GLsizei height,
                                                 GLint border,
                                                 GLsizei imageSize,
                                                 GLsizei dataSize,
                                                 const void *data)
    {
        if (!ValidateRobustCompressedTexImageBase(context, imageSize, dataSize))
        {
            return false;
        }
    
        return ValidateCompressedTexImage2D(context, target, level, internalformat, width, height,
                                            border, imageSize, data);
    }
    bool ValidateCompressedTexSubImage2DRobustANGLE(Context *context,
                                                    GLenum target,
                                                    GLint level,
                                                    GLint xoffset,
                                                    GLint yoffset,
                                                    GLsizei width,
                                                    GLsizei height,
                                                    GLenum format,
                                                    GLsizei imageSize,
                                                    GLsizei dataSize,
                                                    const void *data)
    {
        if (!ValidateRobustCompressedTexImageBase(context, imageSize, dataSize))
        {
            return false;
        }
    
        return ValidateCompressedTexSubImage2D(context, target, level, xoffset, yoffset, width, height,
                                               format, imageSize, data);
    }
    
    bool ValidateCompressedTexSubImage2D(Context *context,
                                         GLenum target,
                                         GLint level,
                                         GLint xoffset,
                                         GLint yoffset,
                                         GLsizei width,
                                         GLsizei height,
                                         GLenum format,
                                         GLsizei imageSize,
                                         const void *data)
    {
        if (context->getClientMajorVersion() < 3)
        {
            if (!ValidateES2TexImageParameters(context, target, level, GL_NONE, true, true, xoffset,
                                               yoffset, width, height, 0, format, GL_NONE, -1, data))
            {
                return false;
            }
        }
        else
        {
            ASSERT(context->getClientMajorVersion() >= 3);
            if (!ValidateES3TexImage2DParameters(context, target, level, GL_NONE, true, true, xoffset,
                                                 yoffset, 0, width, height, 1, 0, format, GL_NONE, -1,
                                                 data))
            {
                return false;
            }
        }
    
        const InternalFormat &formatInfo = GetSizedInternalFormatInfo(format);
        auto blockSizeOrErr =
            formatInfo.computeCompressedImageSize(GL_UNSIGNED_BYTE, gl::Extents(width, height, 1));
        if (blockSizeOrErr.isError())
        {
            context->handleError(blockSizeOrErr.getError());
            return false;
        }
    
        if (imageSize < 0 || static_cast<GLuint>(imageSize) != blockSizeOrErr.getResult())
        {
            context->handleError(InvalidValue());
            return false;
        }
    
        return true;
    }
    
    bool ValidateGetBufferPointervOES(Context *context, GLenum target, GLenum pname, void **params)
    {
        return ValidateGetBufferPointervBase(context, target, pname, nullptr, params);
    }
    
    bool ValidateMapBufferOES(Context *context, GLenum target, GLenum access)
    {
        if (!context->getExtensions().mapBuffer)
        {
            context->handleError(InvalidOperation() << "Map buffer extension not available.");
            return false;
        }
    
        if (!ValidBufferTarget(context, target))
        {
            ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidBufferTypes);
            return false;
        }
    
        Buffer *buffer = context->getGLState().getTargetBuffer(target);
    
        if (buffer == nullptr)
        {
            context->handleError(InvalidOperation() << "Attempted to map buffer object zero.");
            return false;
        }
    
        if (access != GL_WRITE_ONLY_OES)
        {
            context->handleError(InvalidEnum() << "Non-write buffer mapping not supported.");
            return false;
        }
    
        if (buffer->isMapped())
        {
            context->handleError(InvalidOperation() << "Buffer is already mapped.");
            return false;
        }
    
        return true;
    }
    
    bool ValidateUnmapBufferOES(Context *context, GLenum target)
    {
        if (!context->getExtensions().mapBuffer)
        {
            context->handleError(InvalidOperation() << "Map buffer extension not available.");
            return false;
        }
    
        return ValidateUnmapBufferBase(context, target);
    }
    
    bool ValidateMapBufferRangeEXT(Context *context,
                                   GLenum target,
                                   GLintptr offset,
                                   GLsizeiptr length,
                                   GLbitfield access)
    {
        if (!context->getExtensions().mapBufferRange)
        {
            context->handleError(InvalidOperation() << "Map buffer range extension not available.");
            return false;
        }
    
        return ValidateMapBufferRangeBase(context, target, offset, length, access);
    }
    
    bool ValidateFlushMappedBufferRangeEXT(Context *context,
                                           GLenum target,
                                           GLintptr offset,
                                           GLsizeiptr length)
    {
        if (!context->getExtensions().mapBufferRange)
        {
            context->handleError(InvalidOperation() << "Map buffer range extension not available.");
            return false;
        }
    
        return ValidateFlushMappedBufferRangeBase(context, target, offset, length);
    }
    
    bool ValidateBindTexture(Context *context, GLenum target, GLuint texture)
    {
        Texture *textureObject = context->getTexture(texture);
        if (textureObject && textureObject->getTarget() != target && texture != 0)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), TypeMustMatchOriginalType);
            return false;
        }
    
        if (!context->getGLState().isBindGeneratesResourceEnabled() &&
            !context->isTextureGenerated(texture))
        {
            context->handleError(InvalidOperation() << "Texture was not generated");
            return false;
        }
    
        switch (target)
        {
            case GL_TEXTURE_2D:
            case GL_TEXTURE_CUBE_MAP:
                break;
    
            case GL_TEXTURE_3D:
            case GL_TEXTURE_2D_ARRAY:
                if (context->getClientMajorVersion() < 3)
                {
                    context->handleError(InvalidEnum() << "GLES 3.0 disabled");
                    return false;
                }
                break;
    
            case GL_TEXTURE_2D_MULTISAMPLE:
                if (context->getClientVersion() < Version(3, 1))
                {
                    context->handleError(InvalidEnum() << "Context does not support GLES3.1");
                    return false;
                }
                break;
    
            case GL_TEXTURE_EXTERNAL_OES:
                if (!context->getExtensions().eglImageExternal &&
                    !context->getExtensions().eglStreamConsumerExternal)
                {
                    context->handleError(InvalidEnum() << "External texture extension not enabled");
                    return false;
                }
                break;
            default:
                ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidTextureTarget);
                return false;
        }
    
        return true;
    }
    
    bool ValidateBindUniformLocationCHROMIUM(Context *context,
                                             GLuint program,
                                             GLint location,
                                             const GLchar *name)
    {
        if (!context->getExtensions().bindUniformLocation)
        {
            context->handleError(InvalidOperation()
                                 << "GL_CHROMIUM_bind_uniform_location is not available.");
            return false;
        }
    
        Program *programObject = GetValidProgram(context, program);
        if (!programObject)
        {
            return false;
        }
    
        if (location < 0)
        {
            context->handleError(InvalidValue() << "Location cannot be less than 0.");
            return false;
        }
    
        const Caps &caps = context->getCaps();
        if (static_cast<size_t>(location) >=
            (caps.maxVertexUniformVectors + caps.maxFragmentUniformVectors) * 4)
        {
            context->handleError(InvalidValue() << "Location must be less than "
                                                   "(MAX_VERTEX_UNIFORM_VECTORS + "
                                                   "MAX_FRAGMENT_UNIFORM_VECTORS) * 4");
            return false;
        }
    
        // The WebGL spec (section 6.20) disallows strings containing invalid ESSL characters for
        // shader-related entry points
        if (context->getExtensions().webglCompatibility && !IsValidESSLString(name, strlen(name)))
        {
            ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidNameCharacters);
            return false;
        }
    
        if (strncmp(name, "gl_", 3) == 0)
        {
            ANGLE_VALIDATION_ERR(context, InvalidValue(), NameBeginsWithGL);
            return false;
        }
    
        return true;
    }
    
    bool ValidateCoverageModulationCHROMIUM(Context *context, GLenum components)
    {
        if (!context->getExtensions().framebufferMixedSamples)
        {
            context->handleError(InvalidOperation()
                                 << "GL_CHROMIUM_framebuffer_mixed_samples is not available.");
            return false;
        }
        switch (components)
        {
            case GL_RGB:
            case GL_RGBA:
            case GL_ALPHA:
            case GL_NONE:
                break;
            default:
                context->handleError(
                    InvalidEnum()
                    << "GLenum components is not one of GL_RGB, GL_RGBA, GL_ALPHA or GL_NONE.");
                return false;
        }
    
        return true;
    }
    
    // CHROMIUM_path_rendering
    
    bool ValidateMatrix(Context *context, GLenum matrixMode, const GLfloat *matrix)
    {
        if (!context->getExtensions().pathRendering)
        {
            context->handleError(InvalidOperation() << "GL_CHROMIUM_path_rendering is not available.");
            return false;
        }
        if (matrixMode != GL_PATH_MODELVIEW_CHROMIUM && matrixMode != GL_PATH_PROJECTION_CHROMIUM)
        {
            context->handleError(InvalidEnum() << "Invalid matrix mode.");
            return false;
        }
        if (matrix == nullptr)
        {
            context->handleError(InvalidOperation() << "Invalid matrix.");
            return false;
        }
        return true;
    }
    
    bool ValidateMatrixMode(Context *context, GLenum matrixMode)
    {
        if (!context->getExtensions().pathRendering)
        {
            context->handleError(InvalidOperation() << "GL_CHROMIUM_path_rendering is not available.");
            return false;
        }
        if (matrixMode != GL_PATH_MODELVIEW_CHROMIUM && matrixMode != GL_PATH_PROJECTION_CHROMIUM)
        {
            context->handleError(InvalidEnum() << "Invalid matrix mode.");
            return false;
        }
        return true;
    }
    
    bool ValidateGenPaths(Context *context, GLsizei range)
    {
        if (!context->getExtensions().pathRendering)
        {
            context->handleError(InvalidOperation() << "GL_CHROMIUM_path_rendering is not available.");
            return false;
        }
    
        // range = 0 is undefined in NV_path_rendering.
        // we add stricter semantic check here and require a non zero positive range.
        if (range <= 0)
        {
            context->handleError(InvalidValue() << "Invalid range.");
            return false;
        }
    
        if (!angle::IsValueInRangeForNumericType<std::uint32_t>(range))
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), IntegerOverflow);
            return false;
        }
    
        return true;
    }
    
    bool ValidateDeletePaths(Context *context, GLuint path, GLsizei range)
    {
        if (!context->getExtensions().pathRendering)
        {
            context->handleError(InvalidOperation() << "GL_CHROMIUM_path_rendering is not available.");
            return false;
        }
    
        // range = 0 is undefined in NV_path_rendering.
        // we add stricter semantic check here and require a non zero positive range.
        if (range <= 0)
        {
            context->handleError(InvalidValue() << "Invalid range.");
            return false;
        }
    
        angle::CheckedNumeric<std::uint32_t> checkedRange(path);
        checkedRange += range;
    
        if (!angle::IsValueInRangeForNumericType<std::uint32_t>(range) || !checkedRange.IsValid())
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), IntegerOverflow);
            return false;
        }
        return true;
    }
    
    bool ValidatePathCommands(Context *context,
                              GLuint path,
                              GLsizei numCommands,
                              const GLubyte *commands,
                              GLsizei numCoords,
                              GLenum coordType,
                              const void *coords)
    {
        if (!context->getExtensions().pathRendering)
        {
            context->handleError(InvalidOperation() << "GL_CHROMIUM_path_rendering is not available.");
            return false;
        }
        if (!context->hasPath(path))
        {
            context->handleError(InvalidOperation() << "No such path object.");
            return false;
        }
    
        if (numCommands < 0)
        {
            context->handleError(InvalidValue() << "Invalid number of commands.");
            return false;
        }
        else if (numCommands > 0)
        {
            if (!commands)
            {
                context->handleError(InvalidValue() << "No commands array given.");
                return false;
            }
        }
    
        if (numCoords < 0)
        {
            context->handleError(InvalidValue() << "Invalid number of coordinates.");
            return false;
        }
        else if (numCoords > 0)
        {
            if (!coords)
            {
                context->handleError(InvalidValue() << "No coordinate array given.");
                return false;
            }
        }
    
        std::uint32_t coordTypeSize = 0;
        switch (coordType)
        {
            case GL_BYTE:
                coordTypeSize = sizeof(GLbyte);
                break;
    
            case GL_UNSIGNED_BYTE:
                coordTypeSize = sizeof(GLubyte);
                break;
    
            case GL_SHORT:
                coordTypeSize = sizeof(GLshort);
                break;
    
            case GL_UNSIGNED_SHORT:
                coordTypeSize = sizeof(GLushort);
                break;
    
            case GL_FLOAT:
                coordTypeSize = sizeof(GLfloat);
                break;
    
            default:
                context->handleError(InvalidEnum() << "Invalid coordinate type.");
                return false;
        }
    
        angle::CheckedNumeric<std::uint32_t> checkedSize(numCommands);
        checkedSize += (coordTypeSize * numCoords);
        if (!checkedSize.IsValid())
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), IntegerOverflow);
            return false;
        }
    
        // early return skips command data validation when it doesn't exist.
        if (!commands)
            return true;
    
        GLsizei expectedNumCoords = 0;
        for (GLsizei i = 0; i < numCommands; ++i)
        {
            switch (commands[i])
            {
                case GL_CLOSE_PATH_CHROMIUM:  // no coordinates.
                    break;
                case GL_MOVE_TO_CHROMIUM:
                case GL_LINE_TO_CHROMIUM:
                    expectedNumCoords += 2;
                    break;
                case GL_QUADRATIC_CURVE_TO_CHROMIUM:
                    expectedNumCoords += 4;
                    break;
                case GL_CUBIC_CURVE_TO_CHROMIUM:
                    expectedNumCoords += 6;
                    break;
                case GL_CONIC_CURVE_TO_CHROMIUM:
                    expectedNumCoords += 5;
                    break;
                default:
                    context->handleError(InvalidEnum() << "Invalid command.");
                    return false;
            }
        }
        if (expectedNumCoords != numCoords)
        {
            context->handleError(InvalidValue() << "Invalid number of coordinates.");
            return false;
        }
    
        return true;
    }
    
    bool ValidateSetPathParameter(Context *context, GLuint path, GLenum pname, GLfloat value)
    {
        if (!context->getExtensions().pathRendering)
        {
            context->handleError(InvalidOperation() << "GL_CHROMIUM_path_rendering is not available.");
            return false;
        }
        if (!context->hasPath(path))
        {
            context->handleError(InvalidOperation() << "No such path object.");
            return false;
        }
    
        switch (pname)
        {
            case GL_PATH_STROKE_WIDTH_CHROMIUM:
                if (value < 0.0f)
                {
                    context->handleError(InvalidValue() << "Invalid stroke width.");
                    return false;
                }
                break;
            case GL_PATH_END_CAPS_CHROMIUM:
                switch (static_cast<GLenum>(value))
                {
                    case GL_FLAT_CHROMIUM:
                    case GL_SQUARE_CHROMIUM:
                    case GL_ROUND_CHROMIUM:
                        break;
                    default:
                        context->handleError(InvalidEnum() << "Invalid end caps.");
                        return false;
                }
                break;
            case GL_PATH_JOIN_STYLE_CHROMIUM:
                switch (static_cast<GLenum>(value))
                {
                    case GL_MITER_REVERT_CHROMIUM:
                    case GL_BEVEL_CHROMIUM:
                    case GL_ROUND_CHROMIUM:
                        break;
                    default:
                        context->handleError(InvalidEnum() << "Invalid join style.");
                        return false;
                }
            case GL_PATH_MITER_LIMIT_CHROMIUM:
                if (value < 0.0f)
                {
                    context->handleError(InvalidValue() << "Invalid miter limit.");
                    return false;
                }
                break;
    
            case GL_PATH_STROKE_BOUND_CHROMIUM:
                // no errors, only clamping.
                break;
    
            default:
                context->handleError(InvalidEnum() << "Invalid path parameter.");
                return false;
        }
        return true;
    }
    
    bool ValidateGetPathParameter(Context *context, GLuint path, GLenum pname, GLfloat *value)
    {
        if (!context->getExtensions().pathRendering)
        {
            context->handleError(InvalidOperation() << "GL_CHROMIUM_path_rendering is not available.");
            return false;
        }
    
        if (!context->hasPath(path))
        {
            context->handleError(InvalidOperation() << "No such path object.");
            return false;
        }
        if (!value)
        {
            context->handleError(InvalidValue() << "No value array.");
            return false;
        }
    
        switch (pname)
        {
            case GL_PATH_STROKE_WIDTH_CHROMIUM:
            case GL_PATH_END_CAPS_CHROMIUM:
            case GL_PATH_JOIN_STYLE_CHROMIUM:
            case GL_PATH_MITER_LIMIT_CHROMIUM:
            case GL_PATH_STROKE_BOUND_CHROMIUM:
                break;
    
            default:
                context->handleError(InvalidEnum() << "Invalid path parameter.");
                return false;
        }
    
        return true;
    }
    
    bool ValidatePathStencilFunc(Context *context, GLenum func, GLint ref, GLuint mask)
    {
        if (!context->getExtensions().pathRendering)
        {
            context->handleError(InvalidOperation() << "GL_CHROMIUM_path_rendering is not available.");
            return false;
        }
    
        switch (func)
        {
            case GL_NEVER:
            case GL_ALWAYS:
            case GL_LESS:
            case GL_LEQUAL:
            case GL_EQUAL:
            case GL_GEQUAL:
            case GL_GREATER:
            case GL_NOTEQUAL:
                break;
            default:
                ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidStencil);
                return false;
        }
    
        return true;
    }
    
    // Note that the spec specifies that for the path drawing commands
    // if the path object is not an existing path object the command
    // does nothing and no error is generated.
    // However if the path object exists but has not been specified any
    // commands then an error is generated.
    
    bool ValidateStencilFillPath(Context *context, GLuint path, GLenum fillMode, GLuint mask)
    {
        if (!context->getExtensions().pathRendering)
        {
            context->handleError(InvalidOperation() << "GL_CHROMIUM_path_rendering is not available.");
            return false;
        }
        if (context->hasPath(path) && !context->hasPathData(path))
        {
            context->handleError(InvalidOperation() << "No such path object.");
            return false;
        }
    
        switch (fillMode)
        {
            case GL_COUNT_UP_CHROMIUM:
            case GL_COUNT_DOWN_CHROMIUM:
                break;
            default:
                context->handleError(InvalidEnum() << "Invalid fill mode.");
                return false;
        }
    
        if (!isPow2(mask + 1))
        {
            context->handleError(InvalidValue() << "Invalid stencil bit mask.");
            return false;
        }
    
        return true;
    }
    
    bool ValidateStencilStrokePath(Context *context, GLuint path, GLint reference, GLuint mask)
    {
        if (!context->getExtensions().pathRendering)
        {
            context->handleError(InvalidOperation() << "GL_CHROMIUM_path_rendering is not available.");
            return false;
        }
        if (context->hasPath(path) && !context->hasPathData(path))
        {
            context->handleError(InvalidOperation() << "No such path or path has no data.");
            return false;
        }
    
        return true;
    }
    
    bool ValidateCoverPath(Context *context, GLuint path, GLenum coverMode)
    {
        if (!context->getExtensions().pathRendering)
        {
            context->handleError(InvalidOperation() << "GL_CHROMIUM_path_rendering is not available.");
            return false;
        }
        if (context->hasPath(path) && !context->hasPathData(path))
        {
            context->handleError(InvalidOperation() << "No such path object.");
            return false;
        }
    
        switch (coverMode)
        {
            case GL_CONVEX_HULL_CHROMIUM:
            case GL_BOUNDING_BOX_CHROMIUM:
                break;
            default:
                context->handleError(InvalidEnum() << "Invalid cover mode.");
                return false;
        }
        return true;
    }
    
    bool ValidateStencilThenCoverFillPath(Context *context,
                                          GLuint path,
                                          GLenum fillMode,
                                          GLuint mask,
                                          GLenum coverMode)
    {
        return ValidateStencilFillPath(context, path, fillMode, mask) &&
               ValidateCoverPath(context, path, coverMode);
    }
    
    bool ValidateStencilThenCoverStrokePath(Context *context,
                                            GLuint path,
                                            GLint reference,
                                            GLuint mask,
                                            GLenum coverMode)
    {
        return ValidateStencilStrokePath(context, path, reference, mask) &&
               ValidateCoverPath(context, path, coverMode);
    }
    
    bool ValidateIsPath(Context *context)
    {
        if (!context->getExtensions().pathRendering)
        {
            context->handleError(InvalidOperation() << "GL_CHROMIUM_path_rendering is not available.");
            return false;
        }
        return true;
    }
    
    bool ValidateCoverFillPathInstanced(Context *context,
                                        GLsizei numPaths,
                                        GLenum pathNameType,
                                        const void *paths,
                                        GLuint pathBase,
                                        GLenum coverMode,
                                        GLenum transformType,
                                        const GLfloat *transformValues)
    {
        if (!ValidateInstancedPathParameters(context, numPaths, pathNameType, paths, pathBase,
                                             transformType, transformValues))
            return false;
    
        switch (coverMode)
        {
            case GL_CONVEX_HULL_CHROMIUM:
            case GL_BOUNDING_BOX_CHROMIUM:
            case GL_BOUNDING_BOX_OF_BOUNDING_BOXES_CHROMIUM:
                break;
            default:
                context->handleError(InvalidEnum() << "Invalid cover mode.");
                return false;
        }
    
        return true;
    }
    
    bool ValidateCoverStrokePathInstanced(Context *context,
                                          GLsizei numPaths,
                                          GLenum pathNameType,
                                          const void *paths,
                                          GLuint pathBase,
                                          GLenum coverMode,
                                          GLenum transformType,
                                          const GLfloat *transformValues)
    {
        if (!ValidateInstancedPathParameters(context, numPaths, pathNameType, paths, pathBase,
                                             transformType, transformValues))
            return false;
    
        switch (coverMode)
        {
            case GL_CONVEX_HULL_CHROMIUM:
            case GL_BOUNDING_BOX_CHROMIUM:
            case GL_BOUNDING_BOX_OF_BOUNDING_BOXES_CHROMIUM:
                break;
            default:
                context->handleError(InvalidEnum() << "Invalid cover mode.");
                return false;
        }
    
        return true;
    }
    
    bool ValidateStencilFillPathInstanced(Context *context,
                                          GLsizei numPaths,
                                          GLenum pathNameType,
                                          const void *paths,
                                          GLuint pathBase,
                                          GLenum fillMode,
                                          GLuint mask,
                                          GLenum transformType,
                                          const GLfloat *transformValues)
    {
    
        if (!ValidateInstancedPathParameters(context, numPaths, pathNameType, paths, pathBase,
                                             transformType, transformValues))
            return false;
    
        switch (fillMode)
        {
            case GL_COUNT_UP_CHROMIUM:
            case GL_COUNT_DOWN_CHROMIUM:
                break;
            default:
                context->handleError(InvalidEnum() << "Invalid fill mode.");
                return false;
        }
        if (!isPow2(mask + 1))
        {
            context->handleError(InvalidValue() << "Invalid stencil bit mask.");
            return false;
        }
        return true;
    }
    
    bool ValidateStencilStrokePathInstanced(Context *context,
                                            GLsizei numPaths,
                                            GLenum pathNameType,
                                            const void *paths,
                                            GLuint pathBase,
                                            GLint reference,
                                            GLuint mask,
                                            GLenum transformType,
                                            const GLfloat *transformValues)
    {
        if (!ValidateInstancedPathParameters(context, numPaths, pathNameType, paths, pathBase,
                                             transformType, transformValues))
            return false;
    
        // no more validation here.
    
        return true;
    }
    
    bool ValidateStencilThenCoverFillPathInstanced(Context *context,
                                                   GLsizei numPaths,
                                                   GLenum pathNameType,
                                                   const void *paths,
                                                   GLuint pathBase,
                                                   GLenum fillMode,
                                                   GLuint mask,
                                                   GLenum coverMode,
                                                   GLenum transformType,
                                                   const GLfloat *transformValues)
    {
        if (!ValidateInstancedPathParameters(context, numPaths, pathNameType, paths, pathBase,
                                             transformType, transformValues))
            return false;
    
        switch (coverMode)
        {
            case GL_CONVEX_HULL_CHROMIUM:
            case GL_BOUNDING_BOX_CHROMIUM:
            case GL_BOUNDING_BOX_OF_BOUNDING_BOXES_CHROMIUM:
                break;
            default:
                context->handleError(InvalidEnum() << "Invalid cover mode.");
                return false;
        }
    
        switch (fillMode)
        {
            case GL_COUNT_UP_CHROMIUM:
            case GL_COUNT_DOWN_CHROMIUM:
                break;
            default:
                context->handleError(InvalidEnum() << "Invalid fill mode.");
                return false;
        }
        if (!isPow2(mask + 1))
        {
            context->handleError(InvalidValue() << "Invalid stencil bit mask.");
            return false;
        }
    
        return true;
    }
    
    bool ValidateStencilThenCoverStrokePathInstanced(Context *context,
                                                     GLsizei numPaths,
                                                     GLenum pathNameType,
                                                     const void *paths,
                                                     GLuint pathBase,
                                                     GLint reference,
                                                     GLuint mask,
                                                     GLenum coverMode,
                                                     GLenum transformType,
                                                     const GLfloat *transformValues)
    {
        if (!ValidateInstancedPathParameters(context, numPaths, pathNameType, paths, pathBase,
                                             transformType, transformValues))
            return false;
    
        switch (coverMode)
        {
            case GL_CONVEX_HULL_CHROMIUM:
            case GL_BOUNDING_BOX_CHROMIUM:
            case GL_BOUNDING_BOX_OF_BOUNDING_BOXES_CHROMIUM:
                break;
            default:
                context->handleError(InvalidEnum() << "Invalid cover mode.");
                return false;
        }
    
        return true;
    }
    
    bool ValidateBindFragmentInputLocation(Context *context,
                                           GLuint program,
                                           GLint location,
                                           const GLchar *name)
    {
        if (!context->getExtensions().pathRendering)
        {
            context->handleError(InvalidOperation() << "GL_CHROMIUM_path_rendering is not available.");
            return false;
        }
    
        const GLint MaxLocation = context->getCaps().maxVaryingVectors * 4;
        if (location >= MaxLocation)
        {
            context->handleError(InvalidValue() << "Location exceeds max varying.");
            return false;
        }
    
        const auto *programObject = context->getProgram(program);
        if (!programObject)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ProgramNotBound);
            return false;
        }
    
        if (!name)
        {
            context->handleError(InvalidValue() << "No name given.");
            return false;
        }
    
        if (angle::BeginsWith(name, "gl_"))
        {
            context->handleError(InvalidOperation() << "Cannot bind a built-in variable.");
            return false;
        }
    
        return true;
    }
    
    bool ValidateProgramPathFragmentInputGen(Context *context,
                                             GLuint program,
                                             GLint location,
                                             GLenum genMode,
                                             GLint components,
                                             const GLfloat *coeffs)
    {
        if (!context->getExtensions().pathRendering)
        {
            context->handleError(InvalidOperation() << "GL_CHROMIUM_path_rendering is not available.");
            return false;
        }
    
        const auto *programObject = context->getProgram(program);
        if (!programObject || programObject->isFlaggedForDeletion())
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ProgramDoesNotExist);
            return false;
        }
    
        if (!programObject->isLinked())
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ProgramNotLinked);
            return false;
        }
    
        switch (genMode)
        {
            case GL_NONE:
                if (components != 0)
                {
                    context->handleError(InvalidValue() << "Invalid components.");
                    return false;
                }
                break;
    
            case GL_OBJECT_LINEAR_CHROMIUM:
            case GL_EYE_LINEAR_CHROMIUM:
            case GL_CONSTANT_CHROMIUM:
                if (components < 1 || components > 4)
                {
                    context->handleError(InvalidValue() << "Invalid components.");
                    return false;
                }
                if (!coeffs)
                {
                    context->handleError(InvalidValue() << "No coefficients array given.");
                    return false;
                }
                break;
    
            default:
                context->handleError(InvalidEnum() << "Invalid gen mode.");
                return false;
        }
    
        // If the location is -1 then the command is silently ignored
        // and no further validation is needed.
        if (location == -1)
            return true;
    
        const auto &binding = programObject->getFragmentInputBindingInfo(context, location);
    
        if (!binding.valid)
        {
            context->handleError(InvalidOperation() << "No such binding.");
            return false;
        }
    
        if (binding.type != GL_NONE)
        {
            GLint expectedComponents = 0;
            switch (binding.type)
            {
                case GL_FLOAT:
                    expectedComponents = 1;
                    break;
                case GL_FLOAT_VEC2:
                    expectedComponents = 2;
                    break;
                case GL_FLOAT_VEC3:
                    expectedComponents = 3;
                    break;
                case GL_FLOAT_VEC4:
                    expectedComponents = 4;
                    break;
                default:
                    context->handleError(
                        InvalidOperation()
                        << "Fragment input type is not a floating point scalar or vector.");
                    return false;
            }
            if (expectedComponents != components && genMode != GL_NONE)
            {
                context->handleError(InvalidOperation() << "Unexpected number of components");
                return false;
            }
        }
        return true;
    }
    
    bool ValidateCopyTextureCHROMIUM(Context *context,
                                     GLuint sourceId,
                                     GLint sourceLevel,
                                     GLenum destTarget,
                                     GLuint destId,
                                     GLint destLevel,
                                     GLint internalFormat,
                                     GLenum destType,
                                     GLboolean unpackFlipY,
                                     GLboolean unpackPremultiplyAlpha,
                                     GLboolean unpackUnmultiplyAlpha)
    {
        if (!context->getExtensions().copyTexture)
        {
            context->handleError(InvalidOperation()
                                 << "GL_CHROMIUM_copy_texture extension not available.");
            return false;
        }
    
        const Texture *source = context->getTexture(sourceId);
        if (source == nullptr)
        {
            context->handleError(InvalidValue() << "Source texture is not a valid texture object.");
            return false;
        }
    
        if (!IsValidCopyTextureSourceTarget(context, source->getTarget()))
        {
            context->handleError(InvalidValue() << "Source texture a valid texture type.");
            return false;
        }
    
        GLenum sourceTarget = source->getTarget();
        ASSERT(sourceTarget != GL_TEXTURE_CUBE_MAP);
    
        if (!IsValidCopyTextureSourceLevel(context, source->getTarget(), sourceLevel))
        {
            context->handleError(InvalidValue() << "Source texture level is not valid.");
            return false;
        }
    
        GLsizei sourceWidth  = static_cast<GLsizei>(source->getWidth(sourceTarget, sourceLevel));
        GLsizei sourceHeight = static_cast<GLsizei>(source->getHeight(sourceTarget, sourceLevel));
        if (sourceWidth == 0 || sourceHeight == 0)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidInternalFormat);
            return false;
        }
    
        const InternalFormat &sourceFormat = *source->getFormat(sourceTarget, sourceLevel).info;
        if (!IsValidCopyTextureSourceInternalFormatEnum(sourceFormat.internalFormat))
        {
            context->handleError(InvalidOperation() << "Source texture internal format is invalid.");
            return false;
        }
    
        const Texture *dest = context->getTexture(destId);
        if (dest == nullptr)
        {
            context->handleError(InvalidValue()
                                 << "Destination texture is not a valid texture object.");
            return false;
        }
    
        if (!IsValidCopyTextureDestinationTarget(context, dest->getTarget(), destTarget))
        {
            context->handleError(InvalidValue() << "Destination texture a valid texture type.");
            return false;
        }
    
        if (!IsValidCopyTextureDestinationLevel(context, destTarget, destLevel, sourceWidth,
                                                sourceHeight))
        {
            ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidMipLevel);
            return false;
        }
    
        if (!IsValidCopyTextureDestinationFormatType(context, internalFormat, destType))
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), MismatchedTypeAndFormat);
            return false;
        }
    
        if (IsCubeMapTextureTarget(destTarget) && sourceWidth != sourceHeight)
        {
            context->handleError(
                InvalidValue() << "Destination width and height must be equal for cube map textures.");
            return false;
        }
    
        if (dest->getImmutableFormat())
        {
            context->handleError(InvalidOperation() << "Destination texture is immutable.");
            return false;
        }
    
        return true;
    }
    
    bool ValidateCopySubTextureCHROMIUM(Context *context,
                                        GLuint sourceId,
                                        GLint sourceLevel,
                                        GLenum destTarget,
                                        GLuint destId,
                                        GLint destLevel,
                                        GLint xoffset,
                                        GLint yoffset,
                                        GLint x,
                                        GLint y,
                                        GLsizei width,
                                        GLsizei height,
                                        GLboolean unpackFlipY,
                                        GLboolean unpackPremultiplyAlpha,
                                        GLboolean unpackUnmultiplyAlpha)
    {
        if (!context->getExtensions().copyTexture)
        {
            context->handleError(InvalidOperation()
                                 << "GL_CHROMIUM_copy_texture extension not available.");
            return false;
        }
    
        const Texture *source = context->getTexture(sourceId);
        if (source == nullptr)
        {
            context->handleError(InvalidValue() << "Source texture is not a valid texture object.");
            return false;
        }
    
        if (!IsValidCopyTextureSourceTarget(context, source->getTarget()))
        {
            context->handleError(InvalidValue() << "Source texture a valid texture type.");
            return false;
        }
    
        GLenum sourceTarget = source->getTarget();
        ASSERT(sourceTarget != GL_TEXTURE_CUBE_MAP);
    
        if (!IsValidCopyTextureSourceLevel(context, source->getTarget(), sourceLevel))
        {
            ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidMipLevel);
            return false;
        }
    
        if (source->getWidth(sourceTarget, sourceLevel) == 0 ||
            source->getHeight(sourceTarget, sourceLevel) == 0)
        {
            context->handleError(InvalidValue()
                                 << "The source level of the source texture must be defined.");
            return false;
        }
    
        if (x < 0 || y < 0)
        {
            context->handleError(InvalidValue() << "x and y cannot be negative.");
            return false;
        }
    
        if (width < 0 || height < 0)
        {
            ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeSize);
            return false;
        }
    
        if (static_cast<size_t>(x + width) > source->getWidth(sourceTarget, sourceLevel) ||
            static_cast<size_t>(y + height) > source->getHeight(sourceTarget, sourceLevel))
        {
            ANGLE_VALIDATION_ERR(context, InvalidValue(), SourceTextureTooSmall);
            return false;
        }
    
        const Format &sourceFormat = source->getFormat(sourceTarget, sourceLevel);
        if (!IsValidCopySubTextureSourceInternalFormat(sourceFormat.info->internalFormat))
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidInternalFormat);
            return false;
        }
    
        const Texture *dest = context->getTexture(destId);
        if (dest == nullptr)
        {
            context->handleError(InvalidValue()
                                 << "Destination texture is not a valid texture object.");
            return false;
        }
    
        if (!IsValidCopyTextureDestinationTarget(context, dest->getTarget(), destTarget))
        {
            context->handleError(InvalidValue() << "Destination texture a valid texture type.");
            return false;
        }
    
        if (!IsValidCopyTextureDestinationLevel(context, destTarget, destLevel, width, height))
        {
            context->handleError(InvalidValue() << "Destination texture level is not valid.");
            return false;
        }
    
        if (dest->getWidth(destTarget, destLevel) == 0 || dest->getHeight(destTarget, destLevel) == 0)
        {
            context
                ->handleError(InvalidOperation()
                              << "The destination level of the destination texture must be defined.");
            return false;
        }
    
        const InternalFormat &destFormat = *dest->getFormat(destTarget, destLevel).info;
        if (!IsValidCopySubTextureDestionationInternalFormat(destFormat.internalFormat))
        {
            context->handleError(InvalidOperation()
                                 << "Destination internal format and type combination is not valid.");
            return false;
        }
    
        if (xoffset < 0 || yoffset < 0)
        {
            ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeOffset);
            return false;
        }
    
        if (static_cast<size_t>(xoffset + width) > dest->getWidth(destTarget, destLevel) ||
            static_cast<size_t>(yoffset + height) > dest->getHeight(destTarget, destLevel))
        {
            context->handleError(InvalidValue() << "Destination texture not large enough to copy to.");
            return false;
        }
    
        return true;
    }
    
    bool ValidateCompressedCopyTextureCHROMIUM(Context *context, GLuint sourceId, GLuint destId)
    {
        if (!context->getExtensions().copyCompressedTexture)
        {
            context->handleError(InvalidOperation()
                                 << "GL_CHROMIUM_copy_compressed_texture extension not available.");
            return false;
        }
    
        const gl::Texture *source = context->getTexture(sourceId);
        if (source == nullptr)
        {
            context->handleError(InvalidValue() << "Source texture is not a valid texture object.");
            return false;
        }
    
        if (source->getTarget() != GL_TEXTURE_2D)
        {
            context->handleError(InvalidValue() << "Source texture must be of type GL_TEXTURE_2D.");
            return false;
        }
    
        if (source->getWidth(GL_TEXTURE_2D, 0) == 0 || source->getHeight(GL_TEXTURE_2D, 0) == 0)
        {
            context->handleError(InvalidValue() << "Source texture must level 0 defined.");
            return false;
        }
    
        const gl::Format &sourceFormat = source->getFormat(GL_TEXTURE_2D, 0);
        if (!sourceFormat.info->compressed)
        {
            context->handleError(InvalidOperation()
                                 << "Source texture must have a compressed internal format.");
            return false;
        }
    
        const gl::Texture *dest = context->getTexture(destId);
        if (dest == nullptr)
        {
            context->handleError(InvalidValue()
                                 << "Destination texture is not a valid texture object.");
            return false;
        }
    
        if (dest->getTarget() != GL_TEXTURE_2D)
        {
            context->handleError(InvalidValue()
                                 << "Destination texture must be of type GL_TEXTURE_2D.");
            return false;
        }
    
        if (dest->getImmutableFormat())
        {
            context->handleError(InvalidOperation() << "Destination cannot be immutable.");
            return false;
        }
    
        return true;
    }
    
    bool ValidateCreateShader(Context *context, GLenum type)
    {
        switch (type)
        {
            case GL_VERTEX_SHADER:
            case GL_FRAGMENT_SHADER:
                break;
    
            case GL_COMPUTE_SHADER:
                if (context->getClientVersion() < Version(3, 1))
                {
                    context->handleError(InvalidEnum() << "GL_COMPUTE_SHADER requires OpenGL ES 3.1.");
                    return false;
                }
                break;
    
            default:
                ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidShaderType);
                return false;
        }
    
        return true;
    }
    
    bool ValidateBufferData(ValidationContext *context,
                            GLenum target,
                            GLsizeiptr size,
                            const void *data,
                            GLenum usage)
    {
        if (size < 0)
        {
            ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeSize);
            return false;
        }
    
        switch (usage)
        {
            case GL_STREAM_DRAW:
            case GL_STATIC_DRAW:
            case GL_DYNAMIC_DRAW:
                break;
    
            case GL_STREAM_READ:
            case GL_STREAM_COPY:
            case GL_STATIC_READ:
            case GL_STATIC_COPY:
            case GL_DYNAMIC_READ:
            case GL_DYNAMIC_COPY:
                if (context->getClientMajorVersion() < 3)
                {
                    ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidBufferUsage);
                    return false;
                }
                break;
    
            default:
                ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidBufferUsage);
                return false;
        }
    
        if (!ValidBufferTarget(context, target))
        {
            ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidBufferTypes);
            return false;
        }
    
        Buffer *buffer = context->getGLState().getTargetBuffer(target);
    
        if (!buffer)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), BufferNotBound);
            return false;
        }
    
        return true;
    }
    
    bool ValidateBufferSubData(ValidationContext *context,
                               GLenum target,
                               GLintptr offset,
                               GLsizeiptr size,
                               const void *data)
    {
        if (size < 0)
        {
            ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeSize);
            return false;
        }
    
        if (offset < 0)
        {
            ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeOffset);
            return false;
        }
    
        if (!ValidBufferTarget(context, target))
        {
            ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidBufferTypes);
            return false;
        }
    
        Buffer *buffer = context->getGLState().getTargetBuffer(target);
    
        if (!buffer)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), BufferNotBound);
            return false;
        }
    
        if (buffer->isMapped())
        {
            context->handleError(InvalidOperation());
            return false;
        }
    
        // Check for possible overflow of size + offset
        angle::CheckedNumeric<size_t> checkedSize(size);
        checkedSize += offset;
        if (!checkedSize.IsValid())
        {
            context->handleError(OutOfMemory());
            return false;
        }
    
        if (size + offset > buffer->getSize())
        {
            ANGLE_VALIDATION_ERR(context, InvalidValue(), InsufficientBufferSize);
            return false;
        }
    
        return true;
    }
    
    bool ValidateRequestExtensionANGLE(ValidationContext *context, const GLchar *name)
    {
        if (!context->getExtensions().requestExtension)
        {
            context->handleError(InvalidOperation() << "GL_ANGLE_request_extension is not available.");
            return false;
        }
    
        const ExtensionInfoMap &extensionInfos = GetExtensionInfoMap();
        auto extension                         = extensionInfos.find(name);
        if (extension == extensionInfos.end() || !extension->second.Requestable)
        {
            context->handleError(InvalidOperation() << "Extension " << name << " is not requestable.");
            return false;
        }
    
        return true;
    }
    
    bool ValidateActiveTexture(ValidationContext *context, GLenum texture)
    {
        if (texture < GL_TEXTURE0 ||
            texture > GL_TEXTURE0 + context->getCaps().maxCombinedTextureImageUnits - 1)
        {
            context->handleError(InvalidEnum());
            return false;
        }
    
        return true;
    }
    
    bool ValidateAttachShader(ValidationContext *context, GLuint program, GLuint shader)
    {
        Program *programObject = GetValidProgram(context, program);
        if (!programObject)
        {
            return false;
        }
    
        Shader *shaderObject = GetValidShader(context, shader);
        if (!shaderObject)
        {
            return false;
        }
    
        switch (shaderObject->getType())
        {
            case GL_VERTEX_SHADER:
            {
                if (programObject->getAttachedVertexShader())
                {
                    ANGLE_VALIDATION_ERR(context, InvalidOperation(), ShaderAttachmentHasShader);
                    return false;
                }
                break;
            }
            case GL_FRAGMENT_SHADER:
            {
                if (programObject->getAttachedFragmentShader())
                {
                    ANGLE_VALIDATION_ERR(context, InvalidOperation(), ShaderAttachmentHasShader);
                    return false;
                }
                break;
            }
            case GL_COMPUTE_SHADER:
            {
                if (programObject->getAttachedComputeShader())
                {
                    ANGLE_VALIDATION_ERR(context, InvalidOperation(), ShaderAttachmentHasShader);
                    return false;
                }
                break;
            }
            default:
                UNREACHABLE();
                break;
        }
    
        return true;
    }
    
    bool ValidateBindAttribLocation(ValidationContext *context,
                                    GLuint program,
                                    GLuint index,
                                    const GLchar *name)
    {
        if (index >= MAX_VERTEX_ATTRIBS)
        {
            ANGLE_VALIDATION_ERR(context, InvalidValue(), IndexExceedsMax);
            return false;
        }
    
        if (strncmp(name, "gl_", 3) == 0)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), NameBeginsWithGL);
            return false;
        }
    
        // The WebGL spec (section 6.20) disallows strings containing invalid ESSL characters for
        // shader-related entry points
        if (context->getExtensions().webglCompatibility && !IsValidESSLString(name, strlen(name)))
        {
            ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidNameCharacters);
            return false;
        }
    
        return GetValidProgram(context, program) != nullptr;
    }
    
    bool ValidateBindBuffer(ValidationContext *context, GLenum target, GLuint buffer)
    {
        if (!ValidBufferTarget(context, target))
        {
            ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidBufferTypes);
            return false;
        }
    
        if (!context->getGLState().isBindGeneratesResourceEnabled() &&
            !context->isBufferGenerated(buffer))
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ObjectNotGenerated);
            return false;
        }
    
        return true;
    }
    
    bool ValidateBindFramebuffer(ValidationContext *context, GLenum target, GLuint framebuffer)
    {
        if (!ValidFramebufferTarget(target))
        {
            ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidFramebufferTarget);
            return false;
        }
    
        if (!context->getGLState().isBindGeneratesResourceEnabled() &&
            !context->isFramebufferGenerated(framebuffer))
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ObjectNotGenerated);
            return false;
        }
    
        return true;
    }
    
    bool ValidateBindRenderbuffer(ValidationContext *context, GLenum target, GLuint renderbuffer)
    {
        if (target != GL_RENDERBUFFER)
        {
            ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidRenderbufferTarget);
            return false;
        }
    
        if (!context->getGLState().isBindGeneratesResourceEnabled() &&
            !context->isRenderbufferGenerated(renderbuffer))
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ObjectNotGenerated);
            return false;
        }
    
        return true;
    }
    
    static bool ValidBlendEquationMode(GLenum mode)
    {
        switch (mode)
        {
            case GL_FUNC_ADD:
            case GL_FUNC_SUBTRACT:
            case GL_FUNC_REVERSE_SUBTRACT:
            case GL_MIN:
            case GL_MAX:
                return true;
    
            default:
                return false;
        }
    }
    
    bool ValidateBlendColor(ValidationContext *context,
                            GLfloat red,
                            GLfloat green,
                            GLfloat blue,
                            GLfloat alpha)
    {
        return true;
    }
    
    bool ValidateBlendEquation(ValidationContext *context, GLenum mode)
    {
        if (!ValidBlendEquationMode(mode))
        {
            ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidBlendEquation);
            return false;
        }
    
        return true;
    }
    
    bool ValidateBlendEquationSeparate(ValidationContext *context, GLenum modeRGB, GLenum modeAlpha)
    {
        if (!ValidBlendEquationMode(modeRGB))
        {
            ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidBlendEquation);
            return false;
        }
    
        if (!ValidBlendEquationMode(modeAlpha))
        {
            ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidBlendEquation);
            return false;
        }
    
        return true;
    }
    
    bool ValidateBlendFunc(ValidationContext *context, GLenum sfactor, GLenum dfactor)
    {
        return ValidateBlendFuncSeparate(context, sfactor, dfactor, sfactor, dfactor);
    }
    
    static bool ValidSrcBlendFunc(GLenum srcBlend)
    {
        switch (srcBlend)
        {
            case GL_ZERO:
            case GL_ONE:
            case GL_SRC_COLOR:
            case GL_ONE_MINUS_SRC_COLOR:
            case GL_DST_COLOR:
            case GL_ONE_MINUS_DST_COLOR:
            case GL_SRC_ALPHA:
            case GL_ONE_MINUS_SRC_ALPHA:
            case GL_DST_ALPHA:
            case GL_ONE_MINUS_DST_ALPHA:
            case GL_CONSTANT_COLOR:
            case GL_ONE_MINUS_CONSTANT_COLOR:
            case GL_CONSTANT_ALPHA:
            case GL_ONE_MINUS_CONSTANT_ALPHA:
            case GL_SRC_ALPHA_SATURATE:
                return true;
    
            default:
                return false;
        }
    }
    
    static bool ValidDstBlendFunc(GLenum dstBlend, GLint contextMajorVersion)
    {
        switch (dstBlend)
        {
            case GL_ZERO:
            case GL_ONE:
            case GL_SRC_COLOR:
            case GL_ONE_MINUS_SRC_COLOR:
            case GL_DST_COLOR:
            case GL_ONE_MINUS_DST_COLOR:
            case GL_SRC_ALPHA:
            case GL_ONE_MINUS_SRC_ALPHA:
            case GL_DST_ALPHA:
            case GL_ONE_MINUS_DST_ALPHA:
            case GL_CONSTANT_COLOR:
            case GL_ONE_MINUS_CONSTANT_COLOR:
            case GL_CONSTANT_ALPHA:
            case GL_ONE_MINUS_CONSTANT_ALPHA:
                return true;
    
            case GL_SRC_ALPHA_SATURATE:
                return (contextMajorVersion >= 3);
    
            default:
                return false;
        }
    }
    
    bool ValidateBlendFuncSeparate(ValidationContext *context,
                                   GLenum srcRGB,
                                   GLenum dstRGB,
                                   GLenum srcAlpha,
                                   GLenum dstAlpha)
    {
        if (!ValidSrcBlendFunc(srcRGB))
        {
            ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidBlendFunction);
            return false;
        }
    
        if (!ValidDstBlendFunc(dstRGB, context->getClientMajorVersion()))
        {
            ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidBlendFunction);
            return false;
        }
    
        if (!ValidSrcBlendFunc(srcAlpha))
        {
            ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidBlendFunction);
            return false;
        }
    
        if (!ValidDstBlendFunc(dstAlpha, context->getClientMajorVersion()))
        {
            ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidBlendFunction);
            return false;
        }
    
        if (context->getLimitations().noSimultaneousConstantColorAndAlphaBlendFunc ||
            context->getExtensions().webglCompatibility)
        {
            bool constantColorUsed =
                (srcRGB == GL_CONSTANT_COLOR || srcRGB == GL_ONE_MINUS_CONSTANT_COLOR ||
                 dstRGB == GL_CONSTANT_COLOR || dstRGB == GL_ONE_MINUS_CONSTANT_COLOR);
    
            bool constantAlphaUsed =
                (srcRGB == GL_CONSTANT_ALPHA || srcRGB == GL_ONE_MINUS_CONSTANT_ALPHA ||
                 dstRGB == GL_CONSTANT_ALPHA || dstRGB == GL_ONE_MINUS_CONSTANT_ALPHA);
    
            if (constantColorUsed && constantAlphaUsed)
            {
                const char *msg;
                if (context->getExtensions().webglCompatibility)
                {
                    msg = kErrorInvalidConstantColor;
                }
                else
                {
                    msg =
                        "Simultaneous use of GL_CONSTANT_ALPHA/GL_ONE_MINUS_CONSTANT_ALPHA and "
                        "GL_CONSTANT_COLOR/GL_ONE_MINUS_CONSTANT_COLOR not supported by this "
                        "implementation.";
                    ERR() << msg;
                }
                context->handleError(InvalidOperation() << msg);
                return false;
            }
        }
    
        return true;
    }
    
    bool ValidateGetString(Context *context, GLenum name)
    {
        switch (name)
        {
            case GL_VENDOR:
            case GL_RENDERER:
            case GL_VERSION:
            case GL_SHADING_LANGUAGE_VERSION:
            case GL_EXTENSIONS:
                break;
    
            case GL_REQUESTABLE_EXTENSIONS_ANGLE:
                if (!context->getExtensions().requestExtension)
                {
                    context->handleError(InvalidEnum() << "Invalid name.");
                    return false;
                }
                break;
    
            default:
                context->handleError(InvalidEnum() << "Invalid name.");
                return false;
        }
    
        return true;
    }
    
    bool ValidateLineWidth(ValidationContext *context, GLfloat width)
    {
        if (width <= 0.0f || isNaN(width))
        {
            context->handleError(InvalidValue() << "Invalid width value.");
            return false;
        }
    
        return true;
    }
    
    bool ValidateVertexAttribPointer(ValidationContext *context,
                                     GLuint index,
                                     GLint size,
                                     GLenum type,
                                     GLboolean normalized,
                                     GLsizei stride,
                                     const void *ptr)
    {
        if (!ValidateVertexFormatBase(context, index, size, type, false))
        {
            return false;
        }
    
        if (stride < 0)
        {
            ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeStride);
            return false;
        }
    
        const Caps &caps = context->getCaps();
        if (context->getClientVersion() >= ES_3_1)
        {
            if (stride > caps.maxVertexAttribStride)
            {
                context->handleError(InvalidValue()
                                     << "stride cannot be greater than MAX_VERTEX_ATTRIB_STRIDE.");
                return false;
            }
    
            if (index >= caps.maxVertexAttribBindings)
            {
                context->handleError(InvalidValue()
                                     << "index must be smaller than MAX_VERTEX_ATTRIB_BINDINGS.");
                return false;
            }
        }
    
        // [OpenGL ES 3.0.2] Section 2.8 page 24:
        // An INVALID_OPERATION error is generated when a non-zero vertex array object
        // is bound, zero is bound to the ARRAY_BUFFER buffer object binding point,
        // and the pointer argument is not NULL.
        bool nullBufferAllowed = context->getGLState().areClientArraysEnabled() &&
                                 context->getGLState().getVertexArray()->id() == 0;
        if (!nullBufferAllowed && context->getGLState().getArrayBufferId() == 0 && ptr != nullptr)
        {
            context
                ->handleError(InvalidOperation()
                              << "Client data cannot be used with a non-default vertex array object.");
            return false;
        }
    
        if (context->getExtensions().webglCompatibility)
        {
            // WebGL 1.0 [Section 6.14] Fixed point support
            // The WebGL API does not support the GL_FIXED data type.
            if (type == GL_FIXED)
            {
                context->handleError(InvalidEnum() << "GL_FIXED is not supported in WebGL.");
                return false;
            }
    
            if (!ValidateWebGLVertexAttribPointer(context, type, normalized, stride, ptr, false))
            {
                return false;
            }
        }
    
        return true;
    }
    
    bool ValidateDepthRangef(ValidationContext *context, GLfloat zNear, GLfloat zFar)
    {
        if (context->getExtensions().webglCompatibility && zNear > zFar)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidDepthRange);
            return false;
        }
    
        return true;
    }
    
    bool ValidateRenderbufferStorage(ValidationContext *context,
                                     GLenum target,
                                     GLenum internalformat,
                                     GLsizei width,
                                     GLsizei height)
    {
        return ValidateRenderbufferStorageParametersBase(context, target, 0, internalformat, width,
                                                         height);
    }
    
    bool ValidateRenderbufferStorageMultisampleANGLE(ValidationContext *context,
                                                     GLenum target,
                                                     GLsizei samples,
                                                     GLenum internalformat,
                                                     GLsizei width,
                                                     GLsizei height)
    {
        if (!context->getExtensions().framebufferMultisample)
        {
            context->handleError(InvalidOperation()
                                 << "GL_ANGLE_framebuffer_multisample not available");
            return false;
        }
    
        // ANGLE_framebuffer_multisample states that the value of samples must be less than or equal
        // to MAX_SAMPLES_ANGLE (Context::getCaps().maxSamples) otherwise GL_INVALID_OPERATION is
        // generated.
        if (static_cast<GLuint>(samples) > context->getCaps().maxSamples)
        {
            context->handleError(InvalidValue());
            return false;
        }
    
        // ANGLE_framebuffer_multisample states GL_OUT_OF_MEMORY is generated on a failure to create
        // the specified storage. This is different than ES 3.0 in which a sample number higher
        // than the maximum sample number supported by this format generates a GL_INVALID_VALUE.
        // The TextureCaps::getMaxSamples method is only guarenteed to be valid when the context is ES3.
        if (context->getClientMajorVersion() >= 3)
        {
            const TextureCaps &formatCaps = context->getTextureCaps().get(internalformat);
            if (static_cast<GLuint>(samples) > formatCaps.getMaxSamples())
            {
                context->handleError(OutOfMemory());
                return false;
            }
        }
    
        return ValidateRenderbufferStorageParametersBase(context, target, samples, internalformat,
                                                         width, height);
    }
    
    bool ValidateCheckFramebufferStatus(ValidationContext *context, GLenum target)
    {
        if (!ValidFramebufferTarget(target))
        {
            ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidFramebufferTarget);
            return false;
        }
    
        return true;
    }
    
    bool ValidateClearColor(ValidationContext *context,
                            GLfloat red,
                            GLfloat green,
                            GLfloat blue,
                            GLfloat alpha)
    {
        return true;
    }
    
    bool ValidateClearDepthf(ValidationContext *context, GLfloat depth)
    {
        return true;
    }
    
    bool ValidateClearStencil(ValidationContext *context, GLint s)
    {
        return true;
    }
    
    bool ValidateColorMask(ValidationContext *context,
                           GLboolean red,
                           GLboolean green,
                           GLboolean blue,
                           GLboolean alpha)
    {
        return true;
    }
    
    bool ValidateCompileShader(ValidationContext *context, GLuint shader)
    {
        return true;
    }
    
    bool ValidateCreateProgram(ValidationContext *context)
    {
        return true;
    }
    
    bool ValidateCullFace(ValidationContext *context, GLenum mode)
    {
        switch (mode)
        {
            case GL_FRONT:
            case GL_BACK:
            case GL_FRONT_AND_BACK:
                break;
    
            default:
                ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidCullMode);
                return false;
        }
    
        return true;
    }
    
    bool ValidateDeleteProgram(ValidationContext *context, GLuint program)
    {
        if (program == 0)
        {
            return false;
        }
    
        if (!context->getProgram(program))
        {
            if (context->getShader(program))
            {
                ANGLE_VALIDATION_ERR(context, InvalidOperation(), ExpectedProgramName);
                return false;
            }
            else
            {
                ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidProgramName);
                return false;
            }
        }
    
        return true;
    }
    
    bool ValidateDeleteShader(ValidationContext *context, GLuint shader)
    {
        if (shader == 0)
        {
            return false;
        }
    
        if (!context->getShader(shader))
        {
            if (context->getProgram(shader))
            {
                ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidShaderName);
                return false;
            }
            else
            {
                ANGLE_VALIDATION_ERR(context, InvalidValue(), ExpectedShaderName);
                return false;
            }
        }
    
        return true;
    }
    
    bool ValidateDepthFunc(ValidationContext *context, GLenum func)
    {
        switch (func)
        {
            case GL_NEVER:
            case GL_ALWAYS:
            case GL_LESS:
            case GL_LEQUAL:
            case GL_EQUAL:
            case GL_GREATER:
            case GL_GEQUAL:
            case GL_NOTEQUAL:
                break;
    
            default:
                ANGLE_VALIDATION_ERR(context, InvalidEnum(), EnumNotSupported);
                return false;
        }
    
        return true;
    }
    
    bool ValidateDepthMask(ValidationContext *context, GLboolean flag)
    {
        return true;
    }
    
    bool ValidateDetachShader(ValidationContext *context, GLuint program, GLuint shader)
    {
        Program *programObject = GetValidProgram(context, program);
        if (!programObject)
        {
            return false;
        }
    
        Shader *shaderObject = GetValidShader(context, shader);
        if (!shaderObject)
        {
            return false;
        }
    
        const Shader *attachedShader = nullptr;
    
        switch (shaderObject->getType())
        {
            case GL_VERTEX_SHADER:
            {
                attachedShader = programObject->getAttachedVertexShader();
                break;
            }
            case GL_FRAGMENT_SHADER:
            {
                attachedShader = programObject->getAttachedFragmentShader();
                break;
            }
            case GL_COMPUTE_SHADER:
            {
                attachedShader = programObject->getAttachedComputeShader();
                break;
            }
            default:
                UNREACHABLE();
                return false;
        }
    
        if (attachedShader != shaderObject)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ShaderToDetachMustBeAttached);
            return false;
        }
    
        return true;
    }
    
    bool ValidateDisableVertexAttribArray(ValidationContext *context, GLuint index)
    {
        if (index >= MAX_VERTEX_ATTRIBS)
        {
            ANGLE_VALIDATION_ERR(context, InvalidValue(), IndexExceedsMax);
            return false;
        }
    
        return true;
    }
    
    bool ValidateEnableVertexAttribArray(ValidationContext *context, GLuint index)
    {
        if (index >= MAX_VERTEX_ATTRIBS)
        {
            ANGLE_VALIDATION_ERR(context, InvalidValue(), IndexExceedsMax);
            return false;
        }
    
        return true;
    }
    
    bool ValidateFinish(ValidationContext *context)
    {
        return true;
    }
    
    bool ValidateFlush(ValidationContext *context)
    {
        return true;
    }
    
    bool ValidateFrontFace(ValidationContext *context, GLenum mode)
    {
        switch (mode)
        {
            case GL_CW:
            case GL_CCW:
                break;
            default:
                ANGLE_VALIDATION_ERR(context, InvalidEnum(), EnumNotSupported);
                return false;
        }
    
        return true;
    }
    
    bool ValidateGetActiveAttrib(ValidationContext *context,
                                 GLuint program,
                                 GLuint index,
                                 GLsizei bufsize,
                                 GLsizei *length,
                                 GLint *size,
                                 GLenum *type,
                                 GLchar *name)
    {
        if (bufsize < 0)
        {
            ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeBufferSize);
            return false;
        }
    
        Program *programObject = GetValidProgram(context, program);
    
        if (!programObject)
        {
            return false;
        }
    
        if (index >= static_cast<GLuint>(programObject->getActiveAttributeCount()))
        {
            context->handleError(InvalidValue() << "index exeeds program active attribute count.");
            return false;
        }
    
        return true;
    }
    
    bool ValidateGetActiveUniform(ValidationContext *context,
                                  GLuint program,
                                  GLuint index,
                                  GLsizei bufsize,
                                  GLsizei *length,
                                  GLint *size,
                                  GLenum *type,
                                  GLchar *name)
    {
        if (bufsize < 0)
        {
            ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeBufferSize);
            return false;
        }
    
        Program *programObject = GetValidProgram(context, program);
    
        if (!programObject)
        {
            return false;
        }
    
        if (index >= static_cast<GLuint>(programObject->getActiveUniformCount()))
        {
            context->handleError(InvalidValue() << "index exceeds program active uniform count.");
            return false;
        }
    
        return true;
    }
    
    bool ValidateGetAttachedShaders(ValidationContext *context,
                                    GLuint program,
                                    GLsizei maxcount,
                                    GLsizei *count,
                                    GLuint *shaders)
    {
        if (maxcount < 0)
        {
            context->handleError(InvalidValue() << "max count must be non-negative.");
            return false;
        }
    
        Program *programObject = GetValidProgram(context, program);
    
        if (!programObject)
        {
            return false;
        }
    
        return true;
    }
    
    bool ValidateGetAttribLocation(ValidationContext *context, GLuint program, const GLchar *name)
    {
        // The WebGL spec (section 6.20) disallows strings containing invalid ESSL characters for
        // shader-related entry points
        if (context->getExtensions().webglCompatibility && !IsValidESSLString(name, strlen(name)))
        {
            ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidNameCharacters);
            return false;
        }
    
        Program *programObject = GetValidProgram(context, program);
    
        if (!programObject)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ProgramNotBound);
            return false;
        }
    
        if (!programObject->isLinked())
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ProgramNotLinked);
            return false;
        }
    
        return true;
    }
    
    bool ValidateGetBooleanv(ValidationContext *context, GLenum pname, GLboolean *params)
    {
        GLenum nativeType;
        unsigned int numParams = 0;
        return ValidateStateQuery(context, pname, &nativeType, &numParams);
    }
    
    bool ValidateGetError(ValidationContext *context)
    {
        return true;
    }
    
    bool ValidateGetFloatv(ValidationContext *context, GLenum pname, GLfloat *params)
    {
        GLenum nativeType;
        unsigned int numParams = 0;
        return ValidateStateQuery(context, pname, &nativeType, &numParams);
    }
    
    bool ValidateGetIntegerv(ValidationContext *context, GLenum pname, GLint *params)
    {
        GLenum nativeType;
        unsigned int numParams = 0;
        return ValidateStateQuery(context, pname, &nativeType, &numParams);
    }
    
    bool ValidateGetProgramInfoLog(ValidationContext *context,
                                   GLuint program,
                                   GLsizei bufsize,
                                   GLsizei *length,
                                   GLchar *infolog)
    {
        if (bufsize < 0)
        {
            ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeBufferSize);
            return false;
        }
    
        Program *programObject = GetValidProgram(context, program);
        if (!programObject)
        {
            return false;
        }
    
        return true;
    }
    
    bool ValidateGetShaderInfoLog(ValidationContext *context,
                                  GLuint shader,
                                  GLsizei bufsize,
                                  GLsizei *length,
                                  GLchar *infolog)
    {
        if (bufsize < 0)
        {
            ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeBufferSize);
            return false;
        }
    
        Shader *shaderObject = GetValidShader(context, shader);
        if (!shaderObject)
        {
            return false;
        }
    
        return true;
    }
    
    bool ValidateGetShaderPrecisionFormat(ValidationContext *context,
                                          GLenum shadertype,
                                          GLenum precisiontype,
                                          GLint *range,
                                          GLint *precision)
    {
        switch (shadertype)
        {
            case GL_VERTEX_SHADER:
            case GL_FRAGMENT_SHADER:
                break;
            case GL_COMPUTE_SHADER:
                context->handleError(InvalidOperation()
                                     << "compute shader precision not yet implemented.");
                return false;
            default:
                ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidShaderType);
                return false;
        }
    
        switch (precisiontype)
        {
            case GL_LOW_FLOAT:
            case GL_MEDIUM_FLOAT:
            case GL_HIGH_FLOAT:
            case GL_LOW_INT:
            case GL_MEDIUM_INT:
            case GL_HIGH_INT:
                break;
    
            default:
                ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidPrecision);
                return false;
        }
    
        return true;
    }
    
    bool ValidateGetShaderSource(ValidationContext *context,
                                 GLuint shader,
                                 GLsizei bufsize,
                                 GLsizei *length,
                                 GLchar *source)
    {
        if (bufsize < 0)
        {
            ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeBufferSize);
            return false;
        }
    
        Shader *shaderObject = GetValidShader(context, shader);
        if (!shaderObject)
        {
            return false;
        }
    
        return true;
    }
    
    bool ValidateGetUniformLocation(ValidationContext *context, GLuint program, const GLchar *name)
    {
        if (strstr(name, "gl_") == name)
        {
            return false;
        }
    
        // The WebGL spec (section 6.20) disallows strings containing invalid ESSL characters for
        // shader-related entry points
        if (context->getExtensions().webglCompatibility && !IsValidESSLString(name, strlen(name)))
        {
            ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidNameCharacters);
            return false;
        }
    
        Program *programObject = GetValidProgram(context, program);
    
        if (!programObject)
        {
            return false;
        }
    
        if (!programObject->isLinked())
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ProgramNotLinked);
            return false;
        }
    
        return true;
    }
    
    bool ValidateHint(ValidationContext *context, GLenum target, GLenum mode)
    {
        switch (mode)
        {
            case GL_FASTEST:
            case GL_NICEST:
            case GL_DONT_CARE:
                break;
    
            default:
                ANGLE_VALIDATION_ERR(context, InvalidEnum(), EnumNotSupported);
                return false;
        }
    
        switch (target)
        {
            case GL_GENERATE_MIPMAP_HINT:
                break;
    
            case GL_FRAGMENT_SHADER_DERIVATIVE_HINT:
                if (context->getClientVersion() < ES_3_0 &&
                    !context->getExtensions().standardDerivatives)
                {
                    context->handleError(InvalidOperation()
                                         << "hint requires OES_standard_derivatives.");
                    return false;
                }
                break;
    
            default:
                ANGLE_VALIDATION_ERR(context, InvalidEnum(), EnumNotSupported);
                return false;
        }
    
        return true;
    }
    
    bool ValidateIsBuffer(ValidationContext *context, GLuint buffer)
    {
        return true;
    }
    
    bool ValidateIsFramebuffer(ValidationContext *context, GLuint framebuffer)
    {
        return true;
    }
    
    bool ValidateIsProgram(ValidationContext *context, GLuint program)
    {
        return true;
    }
    
    bool ValidateIsRenderbuffer(ValidationContext *context, GLuint renderbuffer)
    {
        return true;
    }
    
    bool ValidateIsShader(ValidationContext *context, GLuint shader)
    {
        return true;
    }
    
    bool ValidateIsTexture(ValidationContext *context, GLuint texture)
    {
        return true;
    }
    
    bool ValidatePixelStorei(ValidationContext *context, GLenum pname, GLint param)
    {
        if (context->getClientMajorVersion() < 3)
        {
            switch (pname)
            {
                case GL_UNPACK_IMAGE_HEIGHT:
                case GL_UNPACK_SKIP_IMAGES:
                    context->handleError(InvalidEnum());
                    return false;
    
                case GL_UNPACK_ROW_LENGTH:
                case GL_UNPACK_SKIP_ROWS:
                case GL_UNPACK_SKIP_PIXELS:
                    if (!context->getExtensions().unpackSubimage)
                    {
                        context->handleError(InvalidEnum());
                        return false;
                    }
                    break;
    
                case GL_PACK_ROW_LENGTH:
                case GL_PACK_SKIP_ROWS:
                case GL_PACK_SKIP_PIXELS:
                    if (!context->getExtensions().packSubimage)
                    {
                        context->handleError(InvalidEnum());
                        return false;
                    }
                    break;
            }
        }
    
        if (param < 0)
        {
            context->handleError(InvalidValue() << "Cannot use negative values in PixelStorei");
            return false;
        }
    
        switch (pname)
        {
            case GL_UNPACK_ALIGNMENT:
                if (param != 1 && param != 2 && param != 4 && param != 8)
                {
                    ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidUnpackAlignment);
                    return false;
                }
                break;
    
            case GL_PACK_ALIGNMENT:
                if (param != 1 && param != 2 && param != 4 && param != 8)
                {
                    ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidUnpackAlignment);
                    return false;
                }
                break;
    
            case GL_PACK_REVERSE_ROW_ORDER_ANGLE:
            case GL_UNPACK_ROW_LENGTH:
            case GL_UNPACK_IMAGE_HEIGHT:
            case GL_UNPACK_SKIP_IMAGES:
            case GL_UNPACK_SKIP_ROWS:
            case GL_UNPACK_SKIP_PIXELS:
            case GL_PACK_ROW_LENGTH:
            case GL_PACK_SKIP_ROWS:
            case GL_PACK_SKIP_PIXELS:
                break;
    
            default:
                ANGLE_VALIDATION_ERR(context, InvalidEnum(), EnumNotSupported);
                return false;
        }
    
        return true;
    }
    
    bool ValidatePolygonOffset(ValidationContext *context, GLfloat factor, GLfloat units)
    {
        return true;
    }
    
    bool ValidateReleaseShaderCompiler(ValidationContext *context)
    {
        return true;
    }
    
    bool ValidateSampleCoverage(ValidationContext *context, GLfloat value, GLboolean invert)
    {
        return true;
    }
    
    bool ValidateScissor(ValidationContext *context, GLint x, GLint y, GLsizei width, GLsizei height)
    {
        if (width < 0 || height < 0)
        {
            ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeSize);
            return false;
        }
    
        return true;
    }
    
    bool ValidateShaderBinary(ValidationContext *context,
                              GLsizei n,
                              const GLuint *shaders,
                              GLenum binaryformat,
                              const void *binary,
                              GLsizei length)
    {
        const std::vector<GLenum> &shaderBinaryFormats = context->getCaps().shaderBinaryFormats;
        if (std::find(shaderBinaryFormats.begin(), shaderBinaryFormats.end(), binaryformat) ==
            shaderBinaryFormats.end())
        {
            context->handleError(InvalidEnum() << "Invalid shader binary format.");
            return false;
        }
    
        return true;
    }
    
    bool ValidateShaderSource(ValidationContext *context,
                              GLuint shader,
                              GLsizei count,
                              const GLchar *const *string,
                              const GLint *length)
    {
        if (count < 0)
        {
            ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeCount);
            return false;
        }
    
        // The WebGL spec (section 6.20) disallows strings containing invalid ESSL characters for
        // shader-related entry points
        if (context->getExtensions().webglCompatibility)
        {
            for (GLsizei i = 0; i < count; i++)
            {
                size_t len =
                    (length && length[i] >= 0) ? static_cast<size_t>(length[i]) : strlen(string[i]);
    
                // Backslash as line-continuation is allowed in WebGL 2.0.
                if (!IsValidESSLShaderSourceString(string[i], len,
                                                   context->getClientVersion() >= ES_3_0))
                {
                    ANGLE_VALIDATION_ERR(context, InvalidValue(), ShaderSourceInvalidCharacters);
                    return false;
                }
            }
        }
    
        Shader *shaderObject = GetValidShader(context, shader);
        if (!shaderObject)
        {
            return false;
        }
    
        return true;
    }
    
    bool ValidateStencilFunc(ValidationContext *context, GLenum func, GLint ref, GLuint mask)
    {
        if (!IsValidStencilFunc(func))
        {
            ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidStencil);
            return false;
        }
    
        return true;
    }
    
    bool ValidateStencilFuncSeparate(ValidationContext *context,
                                     GLenum face,
                                     GLenum func,
                                     GLint ref,
                                     GLuint mask)
    {
        if (!IsValidStencilFace(face))
        {
            ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidStencil);
            return false;
        }
    
        if (!IsValidStencilFunc(func))
        {
            ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidStencil);
            return false;
        }
    
        return true;
    }
    
    bool ValidateStencilMask(ValidationContext *context, GLuint mask)
    {
        return true;
    }
    
    bool ValidateStencilMaskSeparate(ValidationContext *context, GLenum face, GLuint mask)
    {
        if (!IsValidStencilFace(face))
        {
            ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidStencil);
            return false;
        }
    
        return true;
    }
    
    bool ValidateStencilOp(ValidationContext *context, GLenum fail, GLenum zfail, GLenum zpass)
    {
        if (!IsValidStencilOp(fail))
        {
            ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidStencil);
            return false;
        }
    
        if (!IsValidStencilOp(zfail))
        {
            ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidStencil);
            return false;
        }
    
        if (!IsValidStencilOp(zpass))
        {
            ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidStencil);
            return false;
        }
    
        return true;
    }
    
    bool ValidateStencilOpSeparate(ValidationContext *context,
                                   GLenum face,
                                   GLenum fail,
                                   GLenum zfail,
                                   GLenum zpass)
    {
        if (!IsValidStencilFace(face))
        {
            ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidStencil);
            return false;
        }
    
        return ValidateStencilOp(context, fail, zfail, zpass);
    }
    
    bool ValidateUniform1f(ValidationContext *context, GLint location, GLfloat x)
    {
        return ValidateUniform(context, GL_FLOAT, location, 1);
    }
    
    bool ValidateUniform1fv(ValidationContext *context, GLint location, GLsizei count, const GLfloat *v)
    {
        return ValidateUniform(context, GL_FLOAT, location, count);
    }
    
    bool ValidateUniform1i(ValidationContext *context, GLint location, GLint x)
    {
        return ValidateUniform1iv(context, location, 1, &x);
    }
    
    bool ValidateUniform2f(ValidationContext *context, GLint location, GLfloat x, GLfloat y)
    {
        return ValidateUniform(context, GL_FLOAT_VEC2, location, 1);
    }
    
    bool ValidateUniform2fv(ValidationContext *context, GLint location, GLsizei count, const GLfloat *v)
    {
        return ValidateUniform(context, GL_FLOAT_VEC2, location, count);
    }
    
    bool ValidateUniform2i(ValidationContext *context, GLint location, GLint x, GLint y)
    {
        return ValidateUniform(context, GL_INT_VEC2, location, 1);
    }
    
    bool ValidateUniform2iv(ValidationContext *context, GLint location, GLsizei count, const GLint *v)
    {
        return ValidateUniform(context, GL_INT_VEC2, location, count);
    }
    
    bool ValidateUniform3f(ValidationContext *context, GLint location, GLfloat x, GLfloat y, GLfloat z)
    {
        return ValidateUniform(context, GL_FLOAT_VEC3, location, 1);
    }
    
    bool ValidateUniform3fv(ValidationContext *context, GLint location, GLsizei count, const GLfloat *v)
    {
        return ValidateUniform(context, GL_FLOAT_VEC3, location, count);
    }
    
    bool ValidateUniform3i(ValidationContext *context, GLint location, GLint x, GLint y, GLint z)
    {
        return ValidateUniform(context, GL_INT_VEC3, location, 1);
    }
    
    bool ValidateUniform3iv(ValidationContext *context, GLint location, GLsizei count, const GLint *v)
    {
        return ValidateUniform(context, GL_INT_VEC3, location, count);
    }
    
    bool ValidateUniform4f(ValidationContext *context,
                           GLint location,
                           GLfloat x,
                           GLfloat y,
                           GLfloat z,
                           GLfloat w)
    {
        return ValidateUniform(context, GL_FLOAT_VEC4, location, 1);
    }
    
    bool ValidateUniform4fv(ValidationContext *context, GLint location, GLsizei count, const GLfloat *v)
    {
        return ValidateUniform(context, GL_FLOAT_VEC4, location, count);
    }
    
    bool ValidateUniform4i(ValidationContext *context,
                           GLint location,
                           GLint x,
                           GLint y,
                           GLint z,
                           GLint w)
    {
        return ValidateUniform(context, GL_INT_VEC4, location, 1);
    }
    
    bool ValidateUniform4iv(ValidationContext *context, GLint location, GLsizei count, const GLint *v)
    {
        return ValidateUniform(context, GL_INT_VEC4, location, count);
    }
    
    bool ValidateUniformMatrix2fv(ValidationContext *context,
                                  GLint location,
                                  GLsizei count,
                                  GLboolean transpose,
                                  const GLfloat *value)
    {
        return ValidateUniformMatrix(context, GL_FLOAT_MAT2, location, count, transpose);
    }
    
    bool ValidateUniformMatrix3fv(ValidationContext *context,
                                  GLint location,
                                  GLsizei count,
                                  GLboolean transpose,
                                  const GLfloat *value)
    {
        return ValidateUniformMatrix(context, GL_FLOAT_MAT3, location, count, transpose);
    }
    
    bool ValidateUniformMatrix4fv(ValidationContext *context,
                                  GLint location,
                                  GLsizei count,
                                  GLboolean transpose,
                                  const GLfloat *value)
    {
        return ValidateUniformMatrix(context, GL_FLOAT_MAT4, location, count, transpose);
    }
    
    bool ValidateValidateProgram(ValidationContext *context, GLuint program)
    {
        Program *programObject = GetValidProgram(context, program);
    
        if (!programObject)
        {
            return false;
        }
    
        return true;
    }
    
    bool ValidateVertexAttribIndex(ValidationContext *context, GLuint index)
    {
        if (index >= MAX_VERTEX_ATTRIBS)
        {
            ANGLE_VALIDATION_ERR(context, InvalidValue(), IndexExceedsMax);
            return false;
        }
    
        return true;
    }
    
    bool ValidateVertexAttrib1f(ValidationContext *context, GLuint index, GLfloat x)
    {
        return ValidateVertexAttribIndex(context, index);
    }
    
    bool ValidateVertexAttrib1fv(ValidationContext *context, GLuint index, const GLfloat *values)
    {
        return ValidateVertexAttribIndex(context, index);
    }
    
    bool ValidateVertexAttrib2f(ValidationContext *context, GLuint index, GLfloat x, GLfloat y)
    {
        return ValidateVertexAttribIndex(context, index);
    }
    
    bool ValidateVertexAttrib2fv(ValidationContext *context, GLuint index, const GLfloat *values)
    {
        return ValidateVertexAttribIndex(context, index);
    }
    
    bool ValidateVertexAttrib3f(ValidationContext *context,
                                GLuint index,
                                GLfloat x,
                                GLfloat y,
                                GLfloat z)
    {
        return ValidateVertexAttribIndex(context, index);
    }
    
    bool ValidateVertexAttrib3fv(ValidationContext *context, GLuint index, const GLfloat *values)
    {
        return ValidateVertexAttribIndex(context, index);
    }
    
    bool ValidateVertexAttrib4f(ValidationContext *context,
                                GLuint index,
                                GLfloat x,
                                GLfloat y,
                                GLfloat z,
                                GLfloat w)
    {
        return ValidateVertexAttribIndex(context, index);
    }
    
    bool ValidateVertexAttrib4fv(ValidationContext *context, GLuint index, const GLfloat *values)
    {
        return ValidateVertexAttribIndex(context, index);
    }
    
    bool ValidateViewport(ValidationContext *context, GLint x, GLint y, GLsizei width, GLsizei height)
    {
        if (width < 0 || height < 0)
        {
            ANGLE_VALIDATION_ERR(context, InvalidValue(), ViewportNegativeSize);
            return false;
        }
    
        return true;
    }
    
    bool ValidateDrawArrays(ValidationContext *context, GLenum mode, GLint first, GLsizei count)
    {
        return ValidateDrawArraysCommon(context, mode, first, count, 1);
    }
    
    bool ValidateDrawElements(ValidationContext *context,
                              GLenum mode,
                              GLsizei count,
                              GLenum type,
                              const void *indices)
    {
        return ValidateDrawElementsCommon(context, mode, count, type, indices, 1);
    }
    
    bool ValidateGetFramebufferAttachmentParameteriv(ValidationContext *context,
                                                     GLenum target,
                                                     GLenum attachment,
                                                     GLenum pname,
                                                     GLint *params)
    {
        return ValidateGetFramebufferAttachmentParameterivBase(context, target, attachment, pname,
                                                               nullptr);
    }
    
    bool ValidateGetProgramiv(ValidationContext *context, GLuint program, GLenum pname, GLint *params)
    {
        return ValidateGetProgramivBase(context, program, pname, nullptr);
    }
    
    bool ValidateCopyTexImage2D(ValidationContext *context,
                                GLenum target,
                                GLint level,
                                GLenum internalformat,
                                GLint x,
                                GLint y,
                                GLsizei width,
                                GLsizei height,
                                GLint border)
    {
        if (context->getClientMajorVersion() < 3)
        {
            return ValidateES2CopyTexImageParameters(context, target, level, internalformat, false, 0,
                                                     0, x, y, width, height, border);
        }
    
        ASSERT(context->getClientMajorVersion() == 3);
        return ValidateES3CopyTexImage2DParameters(context, target, level, internalformat, false, 0, 0,
                                                   0, x, y, width, height, border);
    }
    
    bool ValidateCopyTexSubImage2D(Context *context,
                                   GLenum target,
                                   GLint level,
                                   GLint xoffset,
                                   GLint yoffset,
                                   GLint x,
                                   GLint y,
                                   GLsizei width,
                                   GLsizei height)
    {
        if (context->getClientMajorVersion() < 3)
        {
            return ValidateES2CopyTexImageParameters(context, target, level, GL_NONE, true, xoffset,
                                                     yoffset, x, y, width, height, 0);
        }
    
        return ValidateES3CopyTexImage2DParameters(context, target, level, GL_NONE, true, xoffset,
                                                   yoffset, 0, x, y, width, height, 0);
    }
    
    bool ValidateDeleteBuffers(Context *context, GLint n, const GLuint *)
    {
        return ValidateGenOrDelete(context, n);
    }
    
    bool ValidateDeleteFramebuffers(Context *context, GLint n, const GLuint *)
    {
        return ValidateGenOrDelete(context, n);
    }
    
    bool ValidateDeleteRenderbuffers(Context *context, GLint n, const GLuint *)
    {
        return ValidateGenOrDelete(context, n);
    }
    
    bool ValidateDeleteTextures(Context *context, GLint n, const GLuint *)
    {
        return ValidateGenOrDelete(context, n);
    }
    
    bool ValidateDisable(Context *context, GLenum cap)
    {
        if (!ValidCap(context, cap, false))
        {
            ANGLE_VALIDATION_ERR(context, InvalidEnum(), EnumNotSupported);
            return false;
        }
    
        return true;
    }
    
    bool ValidateEnable(Context *context, GLenum cap)
    {
        if (!ValidCap(context, cap, false))
        {
            ANGLE_VALIDATION_ERR(context, InvalidEnum(), EnumNotSupported);
            return false;
        }
    
        if (context->getLimitations().noSampleAlphaToCoverageSupport &&
            cap == GL_SAMPLE_ALPHA_TO_COVERAGE)
        {
            const char *errorMessage = "Current renderer doesn't support alpha-to-coverage";
            context->handleError(InvalidOperation() << errorMessage);
    
            // We also output an error message to the debugger window if tracing is active, so that
            // developers can see the error message.
            ERR() << errorMessage;
            return false;
        }
    
        return true;
    }
    
    bool ValidateFramebufferRenderbuffer(Context *context,
                                         GLenum target,
                                         GLenum attachment,
                                         GLenum renderbuffertarget,
                                         GLuint renderbuffer)
    {
        if (!ValidFramebufferTarget(target))
        {
            ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidFramebufferTarget);
            return false;
        }
    
        if (renderbuffertarget != GL_RENDERBUFFER && renderbuffer != 0)
        {
            ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidRenderbufferTarget);
            return false;
        }
    
        return ValidateFramebufferRenderbufferParameters(context, target, attachment,
                                                         renderbuffertarget, renderbuffer);
    }
    
    bool ValidateFramebufferTexture2D(Context *context,
                                      GLenum target,
                                      GLenum attachment,
                                      GLenum textarget,
                                      GLuint texture,
                                      GLint level)
    {
        // Attachments are required to be bound to level 0 without ES3 or the GL_OES_fbo_render_mipmap
        // extension
        if (context->getClientMajorVersion() < 3 && !context->getExtensions().fboRenderMipmap &&
            level != 0)
        {
            ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidFramebufferTextureLevel);
            return false;
        }
    
        if (!ValidateFramebufferTextureBase(context, target, attachment, texture, level))
        {
            return false;
        }
    
        if (texture != 0)
        {
            gl::Texture *tex = context->getTexture(texture);
            ASSERT(tex);
    
            const gl::Caps &caps = context->getCaps();
    
            switch (textarget)
            {
                case GL_TEXTURE_2D:
                {
                    if (level > gl::log2(caps.max2DTextureSize))
                    {
                        context->handleError(InvalidValue());
                        return false;
                    }
                    if (tex->getTarget() != GL_TEXTURE_2D)
                    {
                        ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidTextureTarget);
                        return 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(caps.maxCubeMapTextureSize))
                    {
                        context->handleError(InvalidValue());
                        return false;
                    }
                    if (tex->getTarget() != GL_TEXTURE_CUBE_MAP)
                    {
                        context->handleError(InvalidOperation()
                                             << "Textarget must match the texture target type.");
                        return false;
                    }
                }
                break;
    
                case GL_TEXTURE_2D_MULTISAMPLE:
                {
                    if (context->getClientVersion() < ES_3_1)
                    {
                        context->handleError(InvalidOperation()
                                             << "Texture target requires at least OpenGL ES 3.1.");
                        return false;
                    }
    
                    if (level != 0)
                    {
                        context->handleError(InvalidValue()
                                             << "Level must be 0 for TEXTURE_2D_MULTISAMPLE.");
                        return false;
                    }
                    if (tex->getTarget() != GL_TEXTURE_2D_MULTISAMPLE)
                    {
                        context->handleError(InvalidOperation()
                                             << "Textarget must match the texture target type.");
                        return false;
                    }
                }
                break;
    
                default:
                    ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidTextureTarget);
                    return false;
            }
    
            const Format &format = tex->getFormat(textarget, level);
            if (format.info->compressed)
            {
                context->handleError(InvalidOperation());
                return false;
            }
        }
    
        return true;
    }
    
    bool ValidateGenBuffers(Context *context, GLint n, GLuint *)
    {
        return ValidateGenOrDelete(context, n);
    }
    
    bool ValidateGenFramebuffers(Context *context, GLint n, GLuint *)
    {
        return ValidateGenOrDelete(context, n);
    }
    
    bool ValidateGenRenderbuffers(Context *context, GLint n, GLuint *)
    {
        return ValidateGenOrDelete(context, n);
    }
    
    bool ValidateGenTextures(Context *context, GLint n, GLuint *)
    {
        return ValidateGenOrDelete(context, n);
    }
    
    bool ValidateGenerateMipmap(Context *context, GLenum target)
    {
        if (!ValidTextureTarget(context, target))
        {
            ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidTextureTarget);
            return false;
        }
    
        Texture *texture = context->getTargetTexture(target);
    
        if (texture == nullptr)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), TextureNotBound);
            return false;
        }
    
        const GLuint effectiveBaseLevel = texture->getTextureState().getEffectiveBaseLevel();
    
        // This error isn't spelled out in the spec in a very explicit way, but we interpret the spec so
        // that out-of-range base level has a non-color-renderable / non-texture-filterable format.
        if (effectiveBaseLevel >= gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS)
        {
            context->handleError(InvalidOperation());
            return false;
        }
    
        GLenum baseTarget  = (target == GL_TEXTURE_CUBE_MAP) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : target;
        const auto &format = texture->getFormat(baseTarget, effectiveBaseLevel);
        const TextureCaps &formatCaps = context->getTextureCaps().get(format.info->sizedInternalFormat);
    
        if (format.info->compressed)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), GenerateMipmapNotAllowed);
            return false;
        }
    
        // GenerateMipmap should not generate an INVALID_OPERATION for textures created with
        // unsized formats or that are color renderable and filterable. Since we do not track if
        // the texture was created with sized or unsized format (only sized formats are stored),
        // it is not possible to make sure the the LUMA formats can generate mipmaps (they should
        // be able to) because they aren't color renderable.  Simply do a special case for LUMA
        // textures since they're the only texture format that can be created with unsized formats
        // that is not color renderable.  New unsized formats are unlikely to be added, since ES2
        // was the last version to use add them.
        if (format.info->depthBits > 0 || format.info->stencilBits > 0 || !formatCaps.filterable ||
            (!formatCaps.renderable && !format.info->isLUMA()))
        {
            context->handleError(InvalidOperation());
            return false;
        }
    
        // ES3 and WebGL grant mipmap generation for sRGB textures but GL_EXT_sRGB does not.
        bool supportsSRGBMipmapGeneration =
            context->getClientVersion() >= ES_3_0 || context->getExtensions().webglCompatibility;
        if (!supportsSRGBMipmapGeneration && format.info->colorEncoding == GL_SRGB)
        {
            context->handleError(InvalidOperation()
                                 << "Mipmap generation of sRGB textures is not allowed.");
            return false;
        }
    
        // Non-power of 2 ES2 check
        if (context->getClientVersion() < Version(3, 0) && !context->getExtensions().textureNPOT &&
            (!isPow2(static_cast<int>(texture->getWidth(baseTarget, 0))) ||
             !isPow2(static_cast<int>(texture->getHeight(baseTarget, 0)))))
        {
            ASSERT(target == GL_TEXTURE_2D || target == GL_TEXTURE_CUBE_MAP);
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), TextureNotPow2);
            return false;
        }
    
        // Cube completeness check
        if (target == GL_TEXTURE_CUBE_MAP && !texture->getTextureState().isCubeComplete())
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), CubemapIncomplete);
            return false;
        }
    
        return true;
    }
    
    bool ValidateGetBufferParameteriv(ValidationContext *context,
                                      GLenum target,
                                      GLenum pname,
                                      GLint *params)
    {
        return ValidateGetBufferParameterBase(context, target, pname, false, nullptr);
    }
    
    bool ValidateGetRenderbufferParameteriv(Context *context,
                                            GLenum target,
                                            GLenum pname,
                                            GLint *params)
    {
        return ValidateGetRenderbufferParameterivBase(context, target, pname, nullptr);
    }
    
    bool ValidateGetShaderiv(Context *context, GLuint shader, GLenum pname, GLint *params)
    {
        return ValidateGetShaderivBase(context, shader, pname, nullptr);
    }
    
    bool ValidateGetTexParameterfv(Context *context, GLenum target, GLenum pname, GLfloat *params)
    {
        return ValidateGetTexParameterBase(context, target, pname, nullptr);
    }
    
    bool ValidateGetTexParameteriv(Context *context, GLenum target, GLenum pname, GLint *params)
    {
        return ValidateGetTexParameterBase(context, target, pname, nullptr);
    }
    
    bool ValidateGetUniformfv(Context *context, GLuint program, GLint location, GLfloat *params)
    {
        return ValidateGetUniformBase(context, program, location);
    }
    
    bool ValidateGetUniformiv(Context *context, GLuint program, GLint location, GLint *params)
    {
        return ValidateGetUniformBase(context, program, location);
    }
    
    bool ValidateGetVertexAttribfv(Context *context, GLuint index, GLenum pname, GLfloat *params)
    {
        return ValidateGetVertexAttribBase(context, index, pname, nullptr, false, false);
    }
    
    bool ValidateGetVertexAttribiv(Context *context, GLuint index, GLenum pname, GLint *params)
    {
        return ValidateGetVertexAttribBase(context, index, pname, nullptr, false, false);
    }
    
    bool ValidateGetVertexAttribPointerv(Context *context, GLuint index, GLenum pname, void **pointer)
    {
        return ValidateGetVertexAttribBase(context, index, pname, nullptr, true, false);
    }
    
    bool ValidateIsEnabled(Context *context, GLenum cap)
    {
        if (!ValidCap(context, cap, true))
        {
            ANGLE_VALIDATION_ERR(context, InvalidEnum(), EnumNotSupported);
            return false;
        }
    
        return true;
    }
    
    bool ValidateLinkProgram(Context *context, GLuint program)
    {
        if (context->hasActiveTransformFeedback(program))
        {
            // ES 3.0.4 section 2.15 page 91
            context->handleError(InvalidOperation() << "Cannot link program while program is "
                                                       "associated with an active transform "
                                                       "feedback object.");
            return false;
        }
    
        Program *programObject = GetValidProgram(context, program);
        if (!programObject)
        {
            return false;
        }
    
        return true;
    }
    
    bool ValidateReadPixels(Context *context,
                            GLint x,
                            GLint y,
                            GLsizei width,
                            GLsizei height,
                            GLenum format,
                            GLenum type,
                            void *pixels)
    {
        return ValidateReadPixelsBase(context, x, y, width, height, format, type, -1, nullptr, nullptr,
                                      nullptr, pixels);
    }
    
    bool ValidateTexParameterf(Context *context, GLenum target, GLenum pname, GLfloat param)
    {
        return ValidateTexParameterBase(context, target, pname, -1, &param);
    }
    
    bool ValidateTexParameterfv(Context *context, GLenum target, GLenum pname, const GLfloat *params)
    {
        return ValidateTexParameterBase(context, target, pname, -1, params);
    }
    
    bool ValidateTexParameteri(Context *context, GLenum target, GLenum pname, GLint param)
    {
        return ValidateTexParameterBase(context, target, pname, -1, &param);
    }
    
    bool ValidateTexParameteriv(Context *context, GLenum target, GLenum pname, const GLint *params)
    {
        return ValidateTexParameterBase(context, target, pname, -1, params);
    }
    
    bool ValidateUseProgram(Context *context, GLuint program)
    {
        if (program != 0)
        {
            Program *programObject = context->getProgram(program);
            if (!programObject)
            {
                // ES 3.1.0 section 7.3 page 72
                if (context->getShader(program))
                {
                    ANGLE_VALIDATION_ERR(context, InvalidOperation(), ExpectedProgramName);
                    return false;
                }
                else
                {
                    ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidProgramName);
                    return false;
                }
            }
            if (!programObject->isLinked())
            {
                ANGLE_VALIDATION_ERR(context, InvalidOperation(), ProgramNotLinked);
                return false;
            }
        }
        if (context->getGLState().isTransformFeedbackActiveUnpaused())
        {
            // ES 3.0.4 section 2.15 page 91
            context
                ->handleError(InvalidOperation()
                              << "Cannot change active program while transform feedback is unpaused.");
            return false;
        }
    
        return true;
    }
    
    }  // namespace gl