//
// Copyright 2019 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.
//
// validationESEXT.cpp: Validation functions for OpenGL ES extension entry points.

#include "libANGLE/validationESEXT_autogen.h"

#include "libANGLE/Context.h"
#include "libANGLE/ErrorStrings.h"
#include "libANGLE/MemoryObject.h"
#include "libANGLE/validationES.h"
#include "libANGLE/validationES2.h"
#include "libANGLE/validationES3.h"
#include "libANGLE/validationES31.h"
#include "libANGLE/validationES32.h"

namespace gl
{
using namespace err;

namespace
{
template <typename ObjectT>
bool ValidateGetImageFormatAndType(const Context *context, ObjectT *obj, GLenum format, GLenum type)
{
    GLenum implFormat = obj->getImplementationColorReadFormat(context);
    if (!ValidES3Format(format) && (format != implFormat || format == GL_NONE))
    {
        context->validationError(GL_INVALID_ENUM, kInvalidFormat);
        return false;
    }

    GLenum implType = obj->getImplementationColorReadType(context);
    if (!ValidES3Type(type) && (type != implType || type == GL_NONE))
    {
        context->validationError(GL_INVALID_ENUM, kInvalidType);
        return false;
    }

    // Format/type combinations are not yet validated.

    return true;
}

bool IsValidImageLayout(ImageLayout layout)
{
    switch (layout)
    {
        case ImageLayout::Undefined:
        case ImageLayout::General:
        case ImageLayout::ColorAttachment:
        case ImageLayout::DepthStencilAttachment:
        case ImageLayout::DepthStencilReadOnlyAttachment:
        case ImageLayout::ShaderReadOnly:
        case ImageLayout::TransferSrc:
        case ImageLayout::TransferDst:
        case ImageLayout::DepthReadOnlyStencilAttachment:
        case ImageLayout::DepthAttachmentStencilReadOnly:
            return true;

        default:
            return false;
    }
}

bool IsValidMemoryObjectParamater(const Context *context, GLenum pname)
{
    switch (pname)
    {
        case GL_DEDICATED_MEMORY_OBJECT_EXT:
            return true;

        default:
            return false;
    }
}

bool ValidateObjectIdentifierAndName(const Context *context, GLenum identifier, GLuint name)
{
    bool isGLES11 = context->getClientVersion() == Version(1, 1);
    bool isGLES3  = context->getClientMajorVersion() >= 3;
    bool isGLES31 = context->getClientVersion() >= Version(3, 1);
    switch (identifier)
    {
        case GL_BUFFER_OBJECT_EXT:
            if (context->getBuffer({name}) == nullptr)
            {
                context->validationError(GL_INVALID_OPERATION, kInvalidBufferName);
                return false;
            }
            return true;

        case GL_SHADER_OBJECT_EXT:
            if (isGLES11)
            {
                context->validationError(GL_INVALID_ENUM, kInvalidType);
                return false;
            }
            if (context->getShader({name}) == nullptr)
            {
                context->validationError(GL_INVALID_OPERATION, kInvalidShaderName);
                return false;
            }
            return true;

        case GL_PROGRAM_OBJECT_EXT:
            if (isGLES11)
            {
                context->validationError(GL_INVALID_ENUM, kInvalidType);
                return false;
            }
            if (context->getProgramNoResolveLink({name}) == nullptr)
            {
                context->validationError(GL_INVALID_OPERATION, kInvalidProgramName);
                return false;
            }
            return true;

        case GL_VERTEX_ARRAY_OBJECT_EXT:
            if (!isGLES3 && !context->getExtensions().vertexArrayObjectOES)
            {
                context->validationError(GL_INVALID_ENUM, kInvalidType);
                return false;
            }
            if (context->getVertexArray({name}) == nullptr)
            {
                context->validationError(GL_INVALID_OPERATION, kInvalidVertexArrayName);
                return false;
            }
            return true;

        case GL_QUERY_OBJECT_EXT:
            if (!isGLES3 && !context->getExtensions().occlusionQueryBoolean)
            {
                context->validationError(GL_INVALID_ENUM, kInvalidType);
                return false;
            }
            if (context->getQuery({name}) == nullptr)
            {
                context->validationError(GL_INVALID_OPERATION, kInvalidQueryName);
                return false;
            }
            return true;

        case GL_TRANSFORM_FEEDBACK:
            if (!isGLES3)
            {
                context->validationError(GL_INVALID_ENUM, kInvalidType);
                return false;
            }
            if (context->getTransformFeedback({name}) == nullptr)
            {
                context->validationError(GL_INVALID_OPERATION, kInvalidTransformFeedbackName);
                return false;
            }
            return true;

        case GL_SAMPLER:
            if (!isGLES3)
            {
                context->validationError(GL_INVALID_ENUM, kInvalidType);
                return false;
            }
            if (context->getSampler({name}) == nullptr)
            {
                context->validationError(GL_INVALID_OPERATION, kInvalidSamplerName);
                return false;
            }
            return true;

        case GL_TEXTURE:
            if (context->getTexture({name}) == nullptr)
            {
                context->validationError(GL_INVALID_OPERATION, kInvalidTextureName);
                return false;
            }
            return true;

        case GL_RENDERBUFFER:
            if (!context->isRenderbuffer({name}))
            {
                context->validationError(GL_INVALID_OPERATION, kInvalidRenderbufferName);
                return false;
            }
            return true;

        case GL_FRAMEBUFFER:
            if (context->getFramebuffer({name}) == nullptr)
            {
                context->validationError(GL_INVALID_OPERATION, kInvalidFramebufferName);
                return false;
            }
            return true;

        case GL_PROGRAM_PIPELINE_OBJECT_EXT:
            if (!isGLES31 && !context->getExtensions().separateShaderObjects)
            {
                context->validationError(GL_INVALID_ENUM, kInvalidType);
                return false;
            }
            if (context->getProgramPipeline({name}) == nullptr)
            {
                context->validationError(GL_INVALID_OPERATION, kInvalidProgramPipelineName);
                return false;
            }
            return true;

        default:
            context->validationError(GL_INVALID_ENUM, kInvalidIndentifier);
            return false;
    }
}
}  // namespace

bool ValidateGetTexImageANGLE(const Context *context,
                              TextureTarget target,
                              GLint level,
                              GLenum format,
                              GLenum type,
                              const void *pixels)
{
    if (!context->getExtensions().getImageANGLE)
    {
        context->validationError(GL_INVALID_OPERATION, kGetImageExtensionNotEnabled);
        return false;
    }

    if (!ValidTexture2DDestinationTarget(context, target) &&
        !ValidTexture3DDestinationTarget(context, target))
    {
        context->validationError(GL_INVALID_ENUM, kInvalidTextureTarget);
        return false;
    }

    if (level < 0)
    {
        context->validationError(GL_INVALID_VALUE, kNegativeLevel);
        return false;
    }

    TextureType textureType = TextureTargetToType(target);
    if (!ValidMipLevel(context, textureType, level))
    {
        context->validationError(GL_INVALID_VALUE, kInvalidMipLevel);
        return false;
    }

    Texture *texture = context->getTextureByTarget(target);

    if (!ValidateGetImageFormatAndType(context, texture, format, type))
    {
        return false;
    }

    GLsizei width  = static_cast<GLsizei>(texture->getWidth(target, level));
    GLsizei height = static_cast<GLsizei>(texture->getHeight(target, level));
    if (!ValidatePixelPack(context, format, type, 0, 0, width, height, -1, nullptr, pixels))
    {
        return false;
    }

    return true;
}

bool ValidateGetRenderbufferImageANGLE(const Context *context,
                                       GLenum target,
                                       GLenum format,
                                       GLenum type,
                                       const void *pixels)
{
    if (!context->getExtensions().getImageANGLE)
    {
        context->validationError(GL_INVALID_OPERATION, kGetImageExtensionNotEnabled);
        return false;
    }

    if (target != GL_RENDERBUFFER)
    {
        context->validationError(GL_INVALID_ENUM, kInvalidRenderbufferTarget);
        return false;
    }

    Renderbuffer *renderbuffer = context->getState().getCurrentRenderbuffer();

    if (!ValidateGetImageFormatAndType(context, renderbuffer, format, type))
    {
        return false;
    }

    GLsizei width  = renderbuffer->getWidth();
    GLsizei height = renderbuffer->getHeight();
    if (!ValidatePixelPack(context, format, type, 0, 0, width, height, -1, nullptr, pixels))
    {
        return false;
    }

    return true;
}

bool ValidateDrawElementsBaseVertexEXT(const Context *context,
                                       PrimitiveMode mode,
                                       GLsizei count,
                                       DrawElementsType type,
                                       const void *indices,
                                       GLint basevertex)
{
    if (!context->getExtensions().drawElementsBaseVertexAny())
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateDrawElementsCommon(context, mode, count, type, indices, 1);
}

bool ValidateDrawElementsInstancedBaseVertexEXT(const Context *context,
                                                PrimitiveMode mode,
                                                GLsizei count,
                                                DrawElementsType type,
                                                const void *indices,
                                                GLsizei instancecount,
                                                GLint basevertex)
{
    if (!context->getExtensions().drawElementsBaseVertexAny())
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateDrawElementsInstancedBase(context, mode, count, type, indices, instancecount);
}

bool ValidateDrawRangeElementsBaseVertexEXT(const Context *context,
                                            PrimitiveMode mode,
                                            GLuint start,
                                            GLuint end,
                                            GLsizei count,
                                            DrawElementsType type,
                                            const void *indices,
                                            GLint basevertex)
{
    if (!context->getExtensions().drawElementsBaseVertexAny())
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    if (end < start)
    {
        context->validationError(GL_INVALID_VALUE, kInvalidElementRange);
        return false;
    }

    if (!ValidateDrawElementsCommon(context, mode, count, type, indices, 0))
    {
        return false;
    }

    // Skip range checks for no-op calls.
    if (count <= 0)
    {
        return true;
    }

    // Note that resolving the index range is a bit slow. We should probably optimize this.
    IndexRange indexRange;
    ANGLE_VALIDATION_TRY(context->getState().getVertexArray()->getIndexRange(context, type, count,
                                                                             indices, &indexRange));

    if (indexRange.end > end || indexRange.start < start)
    {
        // GL spec says that behavior in this case is undefined - generating an error is fine.
        context->validationError(GL_INVALID_OPERATION, kExceedsElementRange);
        return false;
    }
    return true;
}

bool ValidateMultiDrawElementsBaseVertexEXT(const Context *context,
                                            PrimitiveMode mode,
                                            const GLsizei *count,
                                            DrawElementsType type,
                                            const void *const *indices,
                                            GLsizei drawcount,
                                            const GLint *basevertex)
{
    return true;
}

bool ValidateDrawElementsBaseVertexOES(const Context *context,
                                       PrimitiveMode mode,
                                       GLsizei count,
                                       DrawElementsType type,
                                       const void *indices,
                                       GLint basevertex)
{
    if (!context->getExtensions().drawElementsBaseVertexAny())
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateDrawElementsCommon(context, mode, count, type, indices, 1);
}

bool ValidateDrawElementsInstancedBaseVertexOES(const Context *context,
                                                PrimitiveMode mode,
                                                GLsizei count,
                                                DrawElementsType type,
                                                const void *indices,
                                                GLsizei instancecount,
                                                GLint basevertex)
{
    if (!context->getExtensions().drawElementsBaseVertexAny())
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateDrawElementsInstancedBase(context, mode, count, type, indices, instancecount);
}

bool ValidateDrawRangeElementsBaseVertexOES(const Context *context,
                                            PrimitiveMode mode,
                                            GLuint start,
                                            GLuint end,
                                            GLsizei count,
                                            DrawElementsType type,
                                            const void *indices,
                                            GLint basevertex)
{
    if (!context->getExtensions().drawElementsBaseVertexAny())
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    if (end < start)
    {
        context->validationError(GL_INVALID_VALUE, kInvalidElementRange);
        return false;
    }

    if (!ValidateDrawElementsCommon(context, mode, count, type, indices, 0))
    {
        return false;
    }

    // Skip range checks for no-op calls.
    if (count <= 0)
    {
        return true;
    }

    // Note that resolving the index range is a bit slow. We should probably optimize this.
    IndexRange indexRange;
    ANGLE_VALIDATION_TRY(context->getState().getVertexArray()->getIndexRange(context, type, count,
                                                                             indices, &indexRange));

    if (indexRange.end > end || indexRange.start < start)
    {
        // GL spec says that behavior in this case is undefined - generating an error is fine.
        context->validationError(GL_INVALID_OPERATION, kExceedsElementRange);
        return false;
    }
    return true;
}

bool ValidateBlendEquationSeparateiEXT(const Context *context,
                                       GLuint buf,
                                       GLenum modeRGB,
                                       GLenum modeAlpha)
{
    if (!context->getExtensions().drawBuffersIndexedEXT)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateBlendEquationSeparatei(context, buf, modeRGB, modeAlpha);
}

bool ValidateBlendEquationiEXT(const Context *context, GLuint buf, GLenum mode)
{
    if (!context->getExtensions().drawBuffersIndexedEXT)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateBlendEquationi(context, buf, mode);
}

bool ValidateBlendFuncSeparateiEXT(const Context *context,
                                   GLuint buf,
                                   GLenum srcRGB,
                                   GLenum dstRGB,
                                   GLenum srcAlpha,
                                   GLenum dstAlpha)
{
    if (!context->getExtensions().drawBuffersIndexedEXT)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateBlendFuncSeparatei(context, buf, srcRGB, dstRGB, srcAlpha, dstAlpha);
}

bool ValidateBlendFunciEXT(const Context *context, GLuint buf, GLenum src, GLenum dst)
{
    if (!context->getExtensions().drawBuffersIndexedEXT)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateBlendFunci(context, buf, src, dst);
}

bool ValidateColorMaskiEXT(const Context *context,
                           GLuint index,
                           GLboolean r,
                           GLboolean g,
                           GLboolean b,
                           GLboolean a)
{
    if (!context->getExtensions().drawBuffersIndexedEXT)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateColorMaski(context, index, r, g, b, a);
}

bool ValidateDisableiEXT(const Context *context, GLenum target, GLuint index)
{
    if (!context->getExtensions().drawBuffersIndexedEXT)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateDisablei(context, target, index);
}

bool ValidateEnableiEXT(const Context *context, GLenum target, GLuint index)
{
    if (!context->getExtensions().drawBuffersIndexedEXT)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateEnablei(context, target, index);
}

bool ValidateIsEnablediEXT(const Context *context, GLenum target, GLuint index)
{
    if (!context->getExtensions().drawBuffersIndexedEXT)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateIsEnabledi(context, target, index);
}

bool ValidateBlendEquationSeparateiOES(const Context *context,
                                       GLuint buf,
                                       GLenum modeRGB,
                                       GLenum modeAlpha)
{
    if (!context->getExtensions().drawBuffersIndexedOES)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateBlendEquationSeparatei(context, buf, modeRGB, modeAlpha);
}

bool ValidateBlendEquationiOES(const Context *context, GLuint buf, GLenum mode)
{
    if (!context->getExtensions().drawBuffersIndexedOES)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateBlendEquationi(context, buf, mode);
}

bool ValidateBlendFuncSeparateiOES(const Context *context,
                                   GLuint buf,
                                   GLenum srcRGB,
                                   GLenum dstRGB,
                                   GLenum srcAlpha,
                                   GLenum dstAlpha)
{
    if (!context->getExtensions().drawBuffersIndexedOES)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateBlendFuncSeparatei(context, buf, srcRGB, dstRGB, srcAlpha, dstAlpha);
}

bool ValidateBlendFunciOES(const Context *context, GLuint buf, GLenum src, GLenum dst)
{
    if (!context->getExtensions().drawBuffersIndexedOES)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateBlendFunci(context, buf, src, dst);
}

bool ValidateColorMaskiOES(const Context *context,
                           GLuint index,
                           GLboolean r,
                           GLboolean g,
                           GLboolean b,
                           GLboolean a)
{
    if (!context->getExtensions().drawBuffersIndexedOES)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateColorMaski(context, index, r, g, b, a);
}

bool ValidateDisableiOES(const Context *context, GLenum target, GLuint index)
{
    if (!context->getExtensions().drawBuffersIndexedOES)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateDisablei(context, target, index);
}

bool ValidateEnableiOES(const Context *context, GLenum target, GLuint index)
{
    if (!context->getExtensions().drawBuffersIndexedOES)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateEnablei(context, target, index);
}

bool ValidateIsEnablediOES(const Context *context, GLenum target, GLuint index)
{
    if (!context->getExtensions().drawBuffersIndexedOES)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateIsEnabledi(context, target, index);
}

bool ValidateGetInteger64vEXT(const Context *context, GLenum pname, const GLint64 *data)
{
    if (!context->getExtensions().disjointTimerQuery)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    GLenum nativeType      = GL_NONE;
    unsigned int numParams = 0;
    if (!ValidateStateQuery(context, pname, &nativeType, &numParams))
    {
        return false;
    }

    return true;
}

bool ValidateCopyImageSubDataEXT(const Context *context,
                                 GLuint srcName,
                                 GLenum srcTarget,
                                 GLint srcLevel,
                                 GLint srcX,
                                 GLint srcY,
                                 GLint srcZ,
                                 GLuint dstName,
                                 GLenum dstTarget,
                                 GLint dstLevel,
                                 GLint dstX,
                                 GLint dstY,
                                 GLint dstZ,
                                 GLsizei srcWidth,
                                 GLsizei srcHeight,
                                 GLsizei srcDepth)
{
    if (!context->getExtensions().copyImageEXT)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateCopyImageSubDataBase(context, srcName, srcTarget, srcLevel, srcX, srcY, srcZ,
                                        dstName, dstTarget, dstLevel, dstX, dstY, dstZ, srcWidth,
                                        srcHeight, srcDepth);
}

bool ValidateCopyImageSubDataOES(const Context *context,
                                 GLuint srcName,
                                 GLenum srcTarget,
                                 GLint srcLevel,
                                 GLint srcX,
                                 GLint srcY,
                                 GLint srcZ,
                                 GLuint dstName,
                                 GLenum dstTarget,
                                 GLint dstLevel,
                                 GLint dstX,
                                 GLint dstY,
                                 GLint dstZ,
                                 GLsizei srcWidth,
                                 GLsizei srcHeight,
                                 GLsizei srcDepth)
{
    if (!context->getExtensions().copyImageEXT)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateCopyImageSubDataBase(context, srcName, srcTarget, srcLevel, srcX, srcY, srcZ,
                                        dstName, dstTarget, dstLevel, dstX, dstY, dstZ, srcWidth,
                                        srcHeight, srcDepth);
}

bool ValidateBufferStorageMemEXT(const Context *context,
                                 TextureType target,
                                 GLsizeiptr size,
                                 MemoryObjectID memory,
                                 GLuint64 offset)
{
    if (!context->getExtensions().memoryObject)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    UNIMPLEMENTED();
    return false;
}

bool ValidateCreateMemoryObjectsEXT(const Context *context,
                                    GLsizei n,
                                    const MemoryObjectID *memoryObjects)
{
    if (!context->getExtensions().memoryObject)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateGenOrDelete(context, n);
}

bool ValidateDeleteMemoryObjectsEXT(const Context *context,
                                    GLsizei n,
                                    const MemoryObjectID *memoryObjects)
{
    if (!context->getExtensions().memoryObject)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateGenOrDelete(context, n);
}

bool ValidateGetMemoryObjectParameterivEXT(const Context *context,
                                           MemoryObjectID memoryObject,
                                           GLenum pname,
                                           const GLint *params)
{
    if (!context->getExtensions().memoryObject)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    const MemoryObject *memory = context->getMemoryObject(memoryObject);
    if (memory == nullptr)
    {
        context->validationError(GL_INVALID_VALUE, kInvalidMemoryObject);
    }

    if (!IsValidMemoryObjectParamater(context, pname))
    {
        context->validationError(GL_INVALID_ENUM, kInvalidMemoryObjectParameter);
        return false;
    }

    return true;
}

bool ValidateGetUnsignedBytevEXT(const Context *context, GLenum pname, const GLubyte *data)
{
    if (!context->getExtensions().memoryObject && !context->getExtensions().semaphore)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    UNIMPLEMENTED();
    return false;
}

bool ValidateGetUnsignedBytei_vEXT(const Context *context,
                                   GLenum target,
                                   GLuint index,
                                   const GLubyte *data)
{
    if (!context->getExtensions().memoryObject && !context->getExtensions().semaphore)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    UNIMPLEMENTED();
    return false;
}

bool ValidateIsMemoryObjectEXT(const Context *context, MemoryObjectID memoryObject)
{
    if (!context->getExtensions().memoryObject)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return true;
}

bool ValidateMemoryObjectParameterivEXT(const Context *context,
                                        MemoryObjectID memoryObject,
                                        GLenum pname,
                                        const GLint *params)
{
    if (!context->getExtensions().memoryObject)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    const MemoryObject *memory = context->getMemoryObject(memoryObject);
    if (memory == nullptr)
    {
        context->validationError(GL_INVALID_VALUE, kInvalidMemoryObject);
        return false;
    }

    if (memory->isImmutable())
    {
        context->validationError(GL_INVALID_OPERATION, kImmutableMemoryObject);
        return false;
    }

    if (!IsValidMemoryObjectParamater(context, pname))
    {
        context->validationError(GL_INVALID_ENUM, kInvalidMemoryObjectParameter);
        return false;
    }

    return true;
}

bool ValidateTexStorageMem2DEXT(const Context *context,
                                TextureType target,
                                GLsizei levels,
                                GLenum internalFormat,
                                GLsizei width,
                                GLsizei height,
                                MemoryObjectID memory,
                                GLuint64 offset)
{
    if (!context->getExtensions().memoryObject)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    if (context->getClientMajorVersion() < 3)
    {
        return ValidateES2TexStorageParametersBase(context, target, levels, internalFormat, width,
                                                   height);
    }

    ASSERT(context->getClientMajorVersion() >= 3);
    return ValidateES3TexStorage2DParameters(context, target, levels, internalFormat, width, height,
                                             1);
}

bool ValidateTexStorageMem3DEXT(const Context *context,
                                TextureType target,
                                GLsizei levels,
                                GLenum internalFormat,
                                GLsizei width,
                                GLsizei height,
                                GLsizei depth,
                                MemoryObjectID memory,
                                GLuint64 offset)
{
    if (!context->getExtensions().memoryObject)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    UNIMPLEMENTED();
    return false;
}

bool ValidateImportMemoryFdEXT(const Context *context,
                               MemoryObjectID memory,
                               GLuint64 size,
                               HandleType handleType,
                               GLint fd)
{
    if (!context->getExtensions().memoryObjectFd)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    switch (handleType)
    {
        case HandleType::OpaqueFd:
            break;
        default:
            context->validationError(GL_INVALID_ENUM, kInvalidHandleType);
            return false;
    }

    return true;
}

bool ValidateImportMemoryZirconHandleANGLE(const Context *context,
                                           MemoryObjectID memory,
                                           GLuint64 size,
                                           HandleType handleType,
                                           GLuint handle)
{
    if (!context->getExtensions().memoryObjectFuchsiaANGLE)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    switch (handleType)
    {
        case HandleType::ZirconVmo:
            break;
        default:
            context->validationError(GL_INVALID_ENUM, kInvalidHandleType);
            return false;
    }

    return true;
}

bool ValidateDeleteSemaphoresEXT(const Context *context, GLsizei n, const SemaphoreID *semaphores)
{
    if (!context->getExtensions().semaphore)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateGenOrDelete(context, n);
}

bool ValidateGenSemaphoresEXT(const Context *context, GLsizei n, const SemaphoreID *semaphores)
{
    if (!context->getExtensions().semaphore)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateGenOrDelete(context, n);
}

bool ValidateGetSemaphoreParameterui64vEXT(const Context *context,
                                           SemaphoreID semaphore,
                                           GLenum pname,
                                           const GLuint64 *params)
{
    if (!context->getExtensions().semaphore)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    UNIMPLEMENTED();
    return false;
}

bool ValidateIsSemaphoreEXT(const Context *context, SemaphoreID semaphore)
{
    if (!context->getExtensions().semaphore)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return true;
}

bool ValidateSemaphoreParameterui64vEXT(const Context *context,
                                        SemaphoreID semaphore,
                                        GLenum pname,
                                        const GLuint64 *params)
{
    if (!context->getExtensions().semaphore)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    UNIMPLEMENTED();
    return false;
}

bool ValidateSignalSemaphoreEXT(const Context *context,
                                SemaphoreID semaphore,
                                GLuint numBufferBarriers,
                                const BufferID *buffers,
                                GLuint numTextureBarriers,
                                const TextureID *textures,
                                const GLenum *dstLayouts)
{
    if (!context->getExtensions().semaphore)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    for (GLuint i = 0; i < numTextureBarriers; ++i)
    {
        if (!IsValidImageLayout(FromGLenum<ImageLayout>(dstLayouts[i])))
        {
            context->validationError(GL_INVALID_ENUM, kInvalidImageLayout);
            return false;
        }
    }

    return true;
}

bool ValidateWaitSemaphoreEXT(const Context *context,
                              SemaphoreID semaphore,
                              GLuint numBufferBarriers,
                              const BufferID *buffers,
                              GLuint numTextureBarriers,
                              const TextureID *textures,
                              const GLenum *srcLayouts)
{
    if (!context->getExtensions().semaphore)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    for (GLuint i = 0; i < numTextureBarriers; ++i)
    {
        if (!IsValidImageLayout(FromGLenum<ImageLayout>(srcLayouts[i])))
        {
            context->validationError(GL_INVALID_ENUM, kInvalidImageLayout);
            return false;
        }
    }

    return true;
}

bool ValidateImportSemaphoreFdEXT(const Context *context,
                                  SemaphoreID semaphore,
                                  HandleType handleType,
                                  GLint fd)
{
    if (!context->getExtensions().semaphoreFd)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    switch (handleType)
    {
        case HandleType::OpaqueFd:
            break;
        default:
            context->validationError(GL_INVALID_ENUM, kInvalidHandleType);
            return false;
    }

    return true;
}

bool ValidateImportSemaphoreZirconHandleANGLE(const Context *context,
                                              SemaphoreID semaphore,
                                              HandleType handleType,
                                              GLuint handle)
{
    if (!context->getExtensions().semaphoreFuchsiaANGLE)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    switch (handleType)
    {
        case HandleType::ZirconEvent:
            break;
        default:
            context->validationError(GL_INVALID_ENUM, kInvalidHandleType);
            return false;
    }

    return true;
}

bool ValidateFramebufferFetchBarrierEXT(const Context *context)
{
    return true;
}

bool ValidatePatchParameteriEXT(const Context *context, GLenum pname, GLint value)
{
    return true;
}

bool ValidateTexStorageMemFlags2DANGLE(const Context *context,
                                       TextureType targetPacked,
                                       GLsizei levels,
                                       GLenum internalFormat,
                                       GLsizei width,
                                       GLsizei height,
                                       MemoryObjectID memoryPacked,
                                       GLuint64 offset,
                                       GLbitfield createFlags,
                                       GLbitfield usageFlags)
{
    if (!context->getExtensions().memoryObjectFlagsANGLE)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    if (!ValidateTexStorageMem2DEXT(context, targetPacked, levels, internalFormat, width, height,
                                    memoryPacked, offset))
    {
        return false;
    }

    // |createFlags| and |usageFlags| must only have bits specified by the extension.
    constexpr GLbitfield kAllCreateFlags =
        GL_CREATE_SPARSE_BINDING_BIT_ANGLE | GL_CREATE_SPARSE_RESIDENCY_BIT_ANGLE |
        GL_CREATE_SPARSE_ALIASED_BIT_ANGLE | GL_CREATE_MUTABLE_FORMAT_BIT_ANGLE |
        GL_CREATE_CUBE_COMPATIBLE_BIT_ANGLE | GL_CREATE_ALIAS_BIT_ANGLE |
        GL_CREATE_SPLIT_INSTANCE_BIND_REGIONS_BIT_ANGLE | GL_CREATE_2D_ARRAY_COMPATIBLE_BIT_ANGLE |
        GL_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT_ANGLE | GL_CREATE_EXTENDED_USAGE_BIT_ANGLE |
        GL_CREATE_PROTECTED_BIT_ANGLE | GL_CREATE_DISJOINT_BIT_ANGLE |
        GL_CREATE_CORNER_SAMPLED_BIT_ANGLE | GL_CREATE_SAMPLE_LOCATIONS_COMPATIBLE_DEPTH_BIT_ANGLE |
        GL_CREATE_SUBSAMPLED_BIT_ANGLE;

    if ((createFlags & ~kAllCreateFlags) != 0)
    {
        context->validationError(GL_INVALID_VALUE, kInvalidExternalCreateFlags);
        return false;
    }

    constexpr GLbitfield kAllUsageFlags =
        GL_USAGE_TRANSFER_SRC_BIT_ANGLE | GL_USAGE_TRANSFER_DST_BIT_ANGLE |
        GL_USAGE_SAMPLED_BIT_ANGLE | GL_USAGE_STORAGE_BIT_ANGLE |
        GL_USAGE_COLOR_ATTACHMENT_BIT_ANGLE | GL_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT_ANGLE |
        GL_USAGE_TRANSIENT_ATTACHMENT_BIT_ANGLE | GL_USAGE_INPUT_ATTACHMENT_BIT_ANGLE |
        GL_USAGE_SHADING_RATE_IMAGE_BIT_ANGLE | GL_USAGE_FRAGMENT_DENSITY_MAP_BIT_ANGLE;

    if ((usageFlags & ~kAllUsageFlags) != 0)
    {
        context->validationError(GL_INVALID_VALUE, kInvalidExternalUsageFlags);
        return false;
    }

    return true;
}

bool ValidateTexStorageMemFlags2DMultisampleANGLE(const Context *context,
                                                  TextureType targetPacked,
                                                  GLsizei samples,
                                                  GLenum internalFormat,
                                                  GLsizei width,
                                                  GLsizei height,
                                                  GLboolean fixedSampleLocations,
                                                  MemoryObjectID memoryPacked,
                                                  GLuint64 offset,
                                                  GLbitfield createFlags,
                                                  GLbitfield usageFlags)
{
    UNIMPLEMENTED();
    return false;
}

bool ValidateTexStorageMemFlags3DANGLE(const Context *context,
                                       TextureType targetPacked,
                                       GLsizei levels,
                                       GLenum internalFormat,
                                       GLsizei width,
                                       GLsizei height,
                                       GLsizei depth,
                                       MemoryObjectID memoryPacked,
                                       GLuint64 offset,
                                       GLbitfield createFlags,
                                       GLbitfield usageFlags)
{
    UNIMPLEMENTED();
    return false;
}

bool ValidateTexStorageMemFlags3DMultisampleANGLE(const Context *context,
                                                  TextureType targetPacked,
                                                  GLsizei samples,
                                                  GLenum internalFormat,
                                                  GLsizei width,
                                                  GLsizei height,
                                                  GLsizei depth,
                                                  GLboolean fixedSampleLocations,
                                                  MemoryObjectID memoryPacked,
                                                  GLuint64 offset,
                                                  GLbitfield createFlags,
                                                  GLbitfield usageFlags)
{
    UNIMPLEMENTED();
    return false;
}

// GL_EXT_buffer_storage
bool ValidateBufferStorageEXT(const Context *context,
                              BufferBinding targetPacked,
                              GLsizeiptr size,
                              const void *data,
                              GLbitfield flags)
{
    if (!context->isValidBufferBinding(targetPacked))
    {
        context->validationError(GL_INVALID_ENUM, kInvalidBufferTypes);
        return false;
    }

    if (size <= 0)
    {
        context->validationError(GL_INVALID_VALUE, kNonPositiveSize);
        return false;
    }

    constexpr GLbitfield kAllUsageFlags =
        (GL_DYNAMIC_STORAGE_BIT_EXT | GL_MAP_READ_BIT | GL_MAP_WRITE_BIT |
         GL_MAP_PERSISTENT_BIT_EXT | GL_MAP_COHERENT_BIT_EXT | GL_CLIENT_STORAGE_BIT_EXT);
    if ((flags & ~kAllUsageFlags) != 0)
    {
        context->validationError(GL_INVALID_VALUE, kInvalidBufferUsageFlags);
        return false;
    }

    if (((flags & GL_MAP_PERSISTENT_BIT_EXT) != 0) &&
        ((flags & (GL_MAP_READ_BIT | GL_MAP_WRITE_BIT)) == 0))
    {
        context->validationError(GL_INVALID_VALUE, kInvalidBufferUsageFlags);
        return false;
    }

    if (((flags & GL_MAP_COHERENT_BIT_EXT) != 0) && ((flags & GL_MAP_PERSISTENT_BIT_EXT) == 0))
    {
        context->validationError(GL_INVALID_VALUE, kInvalidBufferUsageFlags);
        return false;
    }

    Buffer *buffer = context->getState().getTargetBuffer(targetPacked);

    if (buffer == nullptr)
    {
        context->validationError(GL_INVALID_OPERATION, kBufferNotBound);
        return false;
    }

    if (buffer->isImmutable())
    {
        context->validationError(GL_INVALID_OPERATION, kBufferImmutable);
        return false;
    }

    return true;
}

// GL_EXT_clip_control
bool ValidateClipControlEXT(const Context *context, GLenum origin, GLenum depth)
{
    return false;
}

// GL_EXT_external_buffer
bool ValidateBufferStorageExternalEXT(const Context *context,
                                      BufferBinding targetPacked,
                                      GLintptr offset,
                                      GLsizeiptr size,
                                      GLeglClientBufferEXT clientBuffer,
                                      GLbitfield flags)
{
    if (!ValidateBufferStorageEXT(context, targetPacked, size, nullptr, flags))
    {
        return false;
    }

    if (offset != 0)
    {
        context->validationError(GL_INVALID_VALUE, kExternalBufferInvalidOffset);
        return false;
    }

    if (clientBuffer == nullptr && size > 0)
    {
        context->validationError(GL_INVALID_VALUE, kClientBufferInvalid);
        return false;
    }

    return true;
}

bool ValidateNamedBufferStorageExternalEXT(const Context *context,
                                           GLuint buffer,
                                           GLintptr offset,
                                           GLsizeiptr size,
                                           GLeglClientBufferEXT clientBuffer,
                                           GLbitfield flags)
{
    UNIMPLEMENTED();
    return false;
}

// GL_EXT_separate_shader_objects
bool ValidateActiveShaderProgramEXT(const Context *context,
                                    ProgramPipelineID pipelinePacked,
                                    ShaderProgramID programPacked)
{
    if (!context->getExtensions().separateShaderObjects)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateActiveShaderProgramBase(context, pipelinePacked, programPacked);
}

bool ValidateBindProgramPipelineEXT(const Context *context, ProgramPipelineID pipelinePacked)
{
    if (!context->getExtensions().separateShaderObjects)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateBindProgramPipelineBase(context, pipelinePacked);
}

bool ValidateCreateShaderProgramvEXT(const Context *context,
                                     ShaderType typePacked,
                                     GLsizei count,
                                     const GLchar **strings)
{
    if (!context->getExtensions().separateShaderObjects)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateCreateShaderProgramvBase(context, typePacked, count, strings);
}

bool ValidateDeleteProgramPipelinesEXT(const Context *context,
                                       GLsizei n,
                                       const ProgramPipelineID *pipelinesPacked)
{
    if (!context->getExtensions().separateShaderObjects)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateDeleteProgramPipelinesBase(context, n, pipelinesPacked);
}

bool ValidateGenProgramPipelinesEXT(const Context *context,
                                    GLsizei n,
                                    const ProgramPipelineID *pipelinesPacked)
{
    if (!context->getExtensions().separateShaderObjects)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateGenProgramPipelinesBase(context, n, pipelinesPacked);
}

bool ValidateGetProgramPipelineInfoLogEXT(const Context *context,
                                          ProgramPipelineID pipelinePacked,
                                          GLsizei bufSize,
                                          const GLsizei *length,
                                          const GLchar *infoLog)
{
    if (!context->getExtensions().separateShaderObjects)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateGetProgramPipelineInfoLogBase(context, pipelinePacked, bufSize, length, infoLog);
}

bool ValidateGetProgramPipelineivEXT(const Context *context,
                                     ProgramPipelineID pipelinePacked,
                                     GLenum pname,
                                     const GLint *params)
{
    if (!context->getExtensions().separateShaderObjects)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateGetProgramPipelineivBase(context, pipelinePacked, pname, params);
}

bool ValidateIsProgramPipelineEXT(const Context *context, ProgramPipelineID pipelinePacked)
{
    if (!context->getExtensions().separateShaderObjects)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateIsProgramPipelineBase(context, pipelinePacked);
}

bool ValidateProgramParameteriEXT(const Context *context,
                                  ShaderProgramID programPacked,
                                  GLenum pname,
                                  GLint value)
{
    if (!context->getExtensions().separateShaderObjects)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateProgramParameteriBase(context, programPacked, pname, value);
}

bool ValidateProgramUniform1fEXT(const Context *context,
                                 ShaderProgramID programPacked,
                                 UniformLocation locationPacked,
                                 GLfloat v0)
{
    if (!context->getExtensions().separateShaderObjects)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateProgramUniform1fBase(context, programPacked, locationPacked, v0);
}

bool ValidateProgramUniform1fvEXT(const Context *context,
                                  ShaderProgramID programPacked,
                                  UniformLocation locationPacked,
                                  GLsizei count,
                                  const GLfloat *value)
{
    if (!context->getExtensions().separateShaderObjects)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateProgramUniform1fvBase(context, programPacked, locationPacked, count, value);
}

bool ValidateProgramUniform1iEXT(const Context *context,
                                 ShaderProgramID programPacked,
                                 UniformLocation locationPacked,
                                 GLint v0)
{
    if (!context->getExtensions().separateShaderObjects)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateProgramUniform1iBase(context, programPacked, locationPacked, v0);
}

bool ValidateProgramUniform1ivEXT(const Context *context,
                                  ShaderProgramID programPacked,
                                  UniformLocation locationPacked,
                                  GLsizei count,
                                  const GLint *value)
{
    if (!context->getExtensions().separateShaderObjects)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateProgramUniform1ivBase(context, programPacked, locationPacked, count, value);
}

bool ValidateProgramUniform1uiEXT(const Context *context,
                                  ShaderProgramID programPacked,
                                  UniformLocation locationPacked,
                                  GLuint v0)
{
    if (!context->getExtensions().separateShaderObjects)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateProgramUniform1uiBase(context, programPacked, locationPacked, v0);
}

bool ValidateProgramUniform1uivEXT(const Context *context,
                                   ShaderProgramID programPacked,
                                   UniformLocation locationPacked,
                                   GLsizei count,
                                   const GLuint *value)
{
    if (!context->getExtensions().separateShaderObjects)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateProgramUniform1uivBase(context, programPacked, locationPacked, count, value);
}

bool ValidateProgramUniform2fEXT(const Context *context,
                                 ShaderProgramID programPacked,
                                 UniformLocation locationPacked,
                                 GLfloat v0,
                                 GLfloat v1)
{
    if (!context->getExtensions().separateShaderObjects)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateProgramUniform2fBase(context, programPacked, locationPacked, v0, v1);
}

bool ValidateProgramUniform2fvEXT(const Context *context,
                                  ShaderProgramID programPacked,
                                  UniformLocation locationPacked,
                                  GLsizei count,
                                  const GLfloat *value)
{
    if (!context->getExtensions().separateShaderObjects)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateProgramUniform2fvBase(context, programPacked, locationPacked, count, value);
}

bool ValidateProgramUniform2iEXT(const Context *context,
                                 ShaderProgramID programPacked,
                                 UniformLocation locationPacked,
                                 GLint v0,
                                 GLint v1)
{
    if (!context->getExtensions().separateShaderObjects)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateProgramUniform2iBase(context, programPacked, locationPacked, v0, v1);
}

bool ValidateProgramUniform2ivEXT(const Context *context,
                                  ShaderProgramID programPacked,
                                  UniformLocation locationPacked,
                                  GLsizei count,
                                  const GLint *value)
{
    if (!context->getExtensions().separateShaderObjects)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateProgramUniform2ivBase(context, programPacked, locationPacked, count, value);
}

bool ValidateProgramUniform2uiEXT(const Context *context,
                                  ShaderProgramID programPacked,
                                  UniformLocation locationPacked,
                                  GLuint v0,
                                  GLuint v1)
{
    if (!context->getExtensions().separateShaderObjects)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateProgramUniform2uiBase(context, programPacked, locationPacked, v0, v1);
}

bool ValidateProgramUniform2uivEXT(const Context *context,
                                   ShaderProgramID programPacked,
                                   UniformLocation locationPacked,
                                   GLsizei count,
                                   const GLuint *value)
{
    if (!context->getExtensions().separateShaderObjects)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateProgramUniform2uivBase(context, programPacked, locationPacked, count, value);
}

bool ValidateProgramUniform3fEXT(const Context *context,
                                 ShaderProgramID programPacked,
                                 UniformLocation locationPacked,
                                 GLfloat v0,
                                 GLfloat v1,
                                 GLfloat v2)
{
    if (!context->getExtensions().separateShaderObjects)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateProgramUniform3fBase(context, programPacked, locationPacked, v0, v1, v2);
}

bool ValidateProgramUniform3fvEXT(const Context *context,
                                  ShaderProgramID programPacked,
                                  UniformLocation locationPacked,
                                  GLsizei count,
                                  const GLfloat *value)
{
    if (!context->getExtensions().separateShaderObjects)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateProgramUniform3fvBase(context, programPacked, locationPacked, count, value);
}

bool ValidateProgramUniform3iEXT(const Context *context,
                                 ShaderProgramID programPacked,
                                 UniformLocation locationPacked,
                                 GLint v0,
                                 GLint v1,
                                 GLint v2)
{
    if (!context->getExtensions().separateShaderObjects)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateProgramUniform3iBase(context, programPacked, locationPacked, v0, v1, v2);
}

bool ValidateProgramUniform3ivEXT(const Context *context,
                                  ShaderProgramID programPacked,
                                  UniformLocation locationPacked,
                                  GLsizei count,
                                  const GLint *value)
{
    if (!context->getExtensions().separateShaderObjects)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateProgramUniform3ivBase(context, programPacked, locationPacked, count, value);
}

bool ValidateProgramUniform3uiEXT(const Context *context,
                                  ShaderProgramID programPacked,
                                  UniformLocation locationPacked,
                                  GLuint v0,
                                  GLuint v1,
                                  GLuint v2)
{
    if (!context->getExtensions().separateShaderObjects)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateProgramUniform3uiBase(context, programPacked, locationPacked, v0, v1, v2);
}

bool ValidateProgramUniform3uivEXT(const Context *context,
                                   ShaderProgramID programPacked,
                                   UniformLocation locationPacked,
                                   GLsizei count,
                                   const GLuint *value)
{
    if (!context->getExtensions().separateShaderObjects)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateProgramUniform3uivBase(context, programPacked, locationPacked, count, value);
}

bool ValidateProgramUniform4fEXT(const Context *context,
                                 ShaderProgramID programPacked,
                                 UniformLocation locationPacked,
                                 GLfloat v0,
                                 GLfloat v1,
                                 GLfloat v2,
                                 GLfloat v3)
{
    if (!context->getExtensions().separateShaderObjects)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateProgramUniform4fBase(context, programPacked, locationPacked, v0, v1, v2, v3);
}

bool ValidateProgramUniform4fvEXT(const Context *context,
                                  ShaderProgramID programPacked,
                                  UniformLocation locationPacked,
                                  GLsizei count,
                                  const GLfloat *value)
{
    if (!context->getExtensions().separateShaderObjects)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateProgramUniform4fvBase(context, programPacked, locationPacked, count, value);
}

bool ValidateProgramUniform4iEXT(const Context *context,
                                 ShaderProgramID programPacked,
                                 UniformLocation locationPacked,
                                 GLint v0,
                                 GLint v1,
                                 GLint v2,
                                 GLint v3)
{
    if (!context->getExtensions().separateShaderObjects)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateProgramUniform4iBase(context, programPacked, locationPacked, v0, v1, v2, v3);
}

bool ValidateProgramUniform4ivEXT(const Context *context,
                                  ShaderProgramID programPacked,
                                  UniformLocation locationPacked,
                                  GLsizei count,
                                  const GLint *value)
{
    return ValidateProgramUniform4ivBase(context, programPacked, locationPacked, count, value);
}

bool ValidateProgramUniform4uiEXT(const Context *context,
                                  ShaderProgramID programPacked,
                                  UniformLocation locationPacked,
                                  GLuint v0,
                                  GLuint v1,
                                  GLuint v2,
                                  GLuint v3)
{
    if (!context->getExtensions().separateShaderObjects)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateProgramUniform4uiBase(context, programPacked, locationPacked, v0, v1, v2, v3);
}

bool ValidateProgramUniform4uivEXT(const Context *context,
                                   ShaderProgramID programPacked,
                                   UniformLocation locationPacked,
                                   GLsizei count,
                                   const GLuint *value)
{
    return ValidateProgramUniform4uivBase(context, programPacked, locationPacked, count, value);
}

bool ValidateProgramUniformMatrix2fvEXT(const Context *context,
                                        ShaderProgramID programPacked,
                                        UniformLocation locationPacked,
                                        GLsizei count,
                                        GLboolean transpose,
                                        const GLfloat *value)
{
    if (!context->getExtensions().separateShaderObjects)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateProgramUniformMatrix2fvBase(context, programPacked, locationPacked, count,
                                               transpose, value);
}

bool ValidateProgramUniformMatrix2x3fvEXT(const Context *context,
                                          ShaderProgramID programPacked,
                                          UniformLocation locationPacked,
                                          GLsizei count,
                                          GLboolean transpose,
                                          const GLfloat *value)
{
    if (!context->getExtensions().separateShaderObjects)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateProgramUniformMatrix2x3fvBase(context, programPacked, locationPacked, count,
                                                 transpose, value);
}

bool ValidateProgramUniformMatrix2x4fvEXT(const Context *context,
                                          ShaderProgramID programPacked,
                                          UniformLocation locationPacked,
                                          GLsizei count,
                                          GLboolean transpose,
                                          const GLfloat *value)
{
    if (!context->getExtensions().separateShaderObjects)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateProgramUniformMatrix2x4fvBase(context, programPacked, locationPacked, count,
                                                 transpose, value);
}

bool ValidateProgramUniformMatrix3fvEXT(const Context *context,
                                        ShaderProgramID programPacked,
                                        UniformLocation locationPacked,
                                        GLsizei count,
                                        GLboolean transpose,
                                        const GLfloat *value)
{
    if (!context->getExtensions().separateShaderObjects)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateProgramUniformMatrix3fvBase(context, programPacked, locationPacked, count,
                                               transpose, value);
}

bool ValidateProgramUniformMatrix3x2fvEXT(const Context *context,
                                          ShaderProgramID programPacked,
                                          UniformLocation locationPacked,
                                          GLsizei count,
                                          GLboolean transpose,
                                          const GLfloat *value)
{
    if (!context->getExtensions().separateShaderObjects)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateProgramUniformMatrix3x2fvBase(context, programPacked, locationPacked, count,
                                                 transpose, value);
}

bool ValidateProgramUniformMatrix3x4fvEXT(const Context *context,
                                          ShaderProgramID programPacked,
                                          UniformLocation locationPacked,
                                          GLsizei count,
                                          GLboolean transpose,
                                          const GLfloat *value)
{
    if (!context->getExtensions().separateShaderObjects)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateProgramUniformMatrix3x4fvBase(context, programPacked, locationPacked, count,
                                                 transpose, value);
}

bool ValidateProgramUniformMatrix4fvEXT(const Context *context,
                                        ShaderProgramID programPacked,
                                        UniformLocation locationPacked,
                                        GLsizei count,
                                        GLboolean transpose,
                                        const GLfloat *value)
{
    if (!context->getExtensions().separateShaderObjects)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateProgramUniformMatrix4fvBase(context, programPacked, locationPacked, count,
                                               transpose, value);
}

bool ValidateProgramUniformMatrix4x2fvEXT(const Context *context,
                                          ShaderProgramID programPacked,
                                          UniformLocation locationPacked,
                                          GLsizei count,
                                          GLboolean transpose,
                                          const GLfloat *value)
{
    if (!context->getExtensions().separateShaderObjects)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateProgramUniformMatrix4x2fvBase(context, programPacked, locationPacked, count,
                                                 transpose, value);
}

bool ValidateProgramUniformMatrix4x3fvEXT(const Context *context,
                                          ShaderProgramID programPacked,
                                          UniformLocation locationPacked,
                                          GLsizei count,
                                          GLboolean transpose,
                                          const GLfloat *value)
{
    if (!context->getExtensions().separateShaderObjects)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateProgramUniformMatrix4x3fvBase(context, programPacked, locationPacked, count,
                                                 transpose, value);
}

bool ValidateUseProgramStagesEXT(const Context *context,
                                 ProgramPipelineID pipelinePacked,
                                 GLbitfield stages,
                                 ShaderProgramID programPacked)
{
    if (!context->getExtensions().separateShaderObjects)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateUseProgramStagesBase(context, pipelinePacked, stages, programPacked);
}

bool ValidateValidateProgramPipelineEXT(const Context *context, ProgramPipelineID pipelinePacked)
{
    if (!context->getExtensions().separateShaderObjects)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    return ValidateValidateProgramPipelineBase(context, pipelinePacked);
}

// GL_EXT_debug_label
bool ValidateGetObjectLabelEXT(const Context *context,
                               GLenum type,
                               GLuint object,
                               GLsizei bufSize,
                               const GLsizei *length,
                               const GLchar *label)
{
    if (!context->getExtensions().debugLabel)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    if (bufSize < 0)
    {
        context->validationError(GL_INVALID_VALUE, kNegativeBufferSize);
        return false;
    }

    return ValidateObjectIdentifierAndName(context, type, object);
}

bool ValidateLabelObjectEXT(const Context *context,
                            GLenum type,
                            GLuint object,
                            GLsizei length,
                            const GLchar *label)
{
    if (!context->getExtensions().debugLabel)
    {
        context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
        return false;
    }

    if (length < 0)
    {
        context->validationError(GL_INVALID_VALUE, kNegativeLength);
        return false;
    }

    return ValidateObjectIdentifierAndName(context, type, object);
}
}  // namespace gl
