Edit

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

Branch :

  • Show log

    Commit

  • Author : Yuly Novikov
    Date : 2018-06-04 18:59:41
    Hash : f15f886c
    Message : Differentiate texture and renderbuffer framebuffer attachment capabilities ANGLE used to describe the abitily to attach textures and renderbuffers of a specific format to a framebuffer using a single notion of "renderable". However, for some formats, only one can be supported, but not the other. Split TextureCaps::renderable into textureAttachment and renderbuffer. Also, split InternalFormat::renderSupport into textureAttachmentSupport and renderbufferSupport. The only functional change is in a few places which now explicitly check for texture or renderbuffer attachement support. Information in format support tables was duplicated for the two capabilities, so behavior should remain the same. It should be corrected in future CLs. Note: additional information in those tables may need to be added in order to properly support GenerateMipmap and TexStorage2DMultisample, this is beyond the scope of this CL. Bug: angleproject:2567 Change-Id: I18bce4100525be35709d8bbf4de08ec812aab502 Reviewed-on: https://chromium-review.googlesource.com/1086491 Commit-Queue: Yuly Novikov <ynovikov@chromium.org> Reviewed-by: Geoff Lang <geofflang@chromium.org>

  • src/libANGLE/validationES31.cpp
  • //
    // Copyright (c) 2016 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.
    //
    
    // validationES31.cpp: Validation functions for OpenGL ES 3.1 entry point parameters
    
    #include "libANGLE/validationES31.h"
    
    #include "libANGLE/Context.h"
    #include "libANGLE/ErrorStrings.h"
    #include "libANGLE/Framebuffer.h"
    #include "libANGLE/VertexArray.h"
    #include "libANGLE/validationES.h"
    #include "libANGLE/validationES2.h"
    #include "libANGLE/validationES3.h"
    
    #include "common/utilities.h"
    
    using namespace angle;
    
    namespace gl
    {
    
    namespace
    {
    
    bool ValidateNamedProgramInterface(GLenum programInterface)
    {
        switch (programInterface)
        {
            case GL_UNIFORM:
            case GL_UNIFORM_BLOCK:
            case GL_PROGRAM_INPUT:
            case GL_PROGRAM_OUTPUT:
            case GL_TRANSFORM_FEEDBACK_VARYING:
            case GL_BUFFER_VARIABLE:
            case GL_SHADER_STORAGE_BLOCK:
                return true;
            default:
                return false;
        }
    }
    
    bool ValidateLocationProgramInterface(GLenum programInterface)
    {
        switch (programInterface)
        {
            case GL_UNIFORM:
            case GL_PROGRAM_INPUT:
            case GL_PROGRAM_OUTPUT:
                return true;
            default:
                return false;
        }
    }
    
    bool ValidateProgramInterface(GLenum programInterface)
    {
        return (programInterface == GL_ATOMIC_COUNTER_BUFFER ||
                ValidateNamedProgramInterface(programInterface));
    }
    
    bool ValidateProgramResourceProperty(const Context *context, GLenum prop)
    {
        ASSERT(context);
        switch (prop)
        {
            case GL_ACTIVE_VARIABLES:
            case GL_BUFFER_BINDING:
            case GL_NUM_ACTIVE_VARIABLES:
    
            case GL_ARRAY_SIZE:
    
            case GL_ARRAY_STRIDE:
            case GL_BLOCK_INDEX:
            case GL_IS_ROW_MAJOR:
            case GL_MATRIX_STRIDE:
    
            case GL_ATOMIC_COUNTER_BUFFER_INDEX:
    
            case GL_BUFFER_DATA_SIZE:
    
            case GL_LOCATION:
    
            case GL_NAME_LENGTH:
    
            case GL_OFFSET:
    
            case GL_REFERENCED_BY_VERTEX_SHADER:
            case GL_REFERENCED_BY_FRAGMENT_SHADER:
            case GL_REFERENCED_BY_COMPUTE_SHADER:
    
            case GL_TOP_LEVEL_ARRAY_SIZE:
            case GL_TOP_LEVEL_ARRAY_STRIDE:
    
            case GL_TYPE:
                return true;
    
            case GL_REFERENCED_BY_GEOMETRY_SHADER_EXT:
                return context->getExtensions().geometryShader;
    
            default:
                return false;
        }
    }
    
    // GLES 3.10 spec: Page 82 -- Table 7.2
    bool ValidateProgramResourcePropertyByInterface(GLenum prop, GLenum programInterface)
    {
        switch (prop)
        {
            case GL_ACTIVE_VARIABLES:
            case GL_BUFFER_BINDING:
            case GL_NUM_ACTIVE_VARIABLES:
            {
                switch (programInterface)
                {
                    case GL_ATOMIC_COUNTER_BUFFER:
                    case GL_SHADER_STORAGE_BLOCK:
                    case GL_UNIFORM_BLOCK:
                        return true;
                    default:
                        return false;
                }
            }
    
            case GL_ARRAY_SIZE:
            {
                switch (programInterface)
                {
                    case GL_BUFFER_VARIABLE:
                    case GL_PROGRAM_INPUT:
                    case GL_PROGRAM_OUTPUT:
                    case GL_TRANSFORM_FEEDBACK_VARYING:
                    case GL_UNIFORM:
                        return true;
                    default:
                        return false;
                }
            }
    
            case GL_ARRAY_STRIDE:
            case GL_BLOCK_INDEX:
            case GL_IS_ROW_MAJOR:
            case GL_MATRIX_STRIDE:
            {
                switch (programInterface)
                {
                    case GL_BUFFER_VARIABLE:
                    case GL_UNIFORM:
                        return true;
                    default:
                        return false;
                }
            }
    
            case GL_ATOMIC_COUNTER_BUFFER_INDEX:
            {
                if (programInterface == GL_UNIFORM)
                {
                    return true;
                }
                return false;
            }
    
            case GL_BUFFER_DATA_SIZE:
            {
                switch (programInterface)
                {
                    case GL_ATOMIC_COUNTER_BUFFER:
                    case GL_SHADER_STORAGE_BLOCK:
                    case GL_UNIFORM_BLOCK:
                        return true;
                    default:
                        return false;
                }
            }
    
            case GL_LOCATION:
            {
                return ValidateLocationProgramInterface(programInterface);
            }
    
            case GL_NAME_LENGTH:
            {
                return ValidateNamedProgramInterface(programInterface);
            }
    
            case GL_OFFSET:
            {
                switch (programInterface)
                {
                    case GL_BUFFER_VARIABLE:
                    case GL_UNIFORM:
                        return true;
                    default:
                        return false;
                }
            }
    
            case GL_REFERENCED_BY_VERTEX_SHADER:
            case GL_REFERENCED_BY_FRAGMENT_SHADER:
            case GL_REFERENCED_BY_COMPUTE_SHADER:
            case GL_REFERENCED_BY_GEOMETRY_SHADER_EXT:
            {
                switch (programInterface)
                {
                    case GL_ATOMIC_COUNTER_BUFFER:
                    case GL_BUFFER_VARIABLE:
                    case GL_PROGRAM_INPUT:
                    case GL_PROGRAM_OUTPUT:
                    case GL_SHADER_STORAGE_BLOCK:
                    case GL_UNIFORM:
                    case GL_UNIFORM_BLOCK:
                        return true;
                    default:
                        return false;
                }
            }
    
            case GL_TOP_LEVEL_ARRAY_SIZE:
            case GL_TOP_LEVEL_ARRAY_STRIDE:
            {
                if (programInterface == GL_BUFFER_VARIABLE)
                {
                    return true;
                }
                return false;
            }
    
            case GL_TYPE:
            {
                switch (programInterface)
                {
                    case GL_BUFFER_VARIABLE:
                    case GL_PROGRAM_INPUT:
                    case GL_PROGRAM_OUTPUT:
                    case GL_TRANSFORM_FEEDBACK_VARYING:
                    case GL_UNIFORM:
                        return true;
                    default:
                        return false;
                }
            }
    
            default:
                return false;
        }
    }
    
    bool ValidateProgramResourceIndex(const Program *programObject,
                                      GLenum programInterface,
                                      GLuint index)
    {
        switch (programInterface)
        {
            case GL_PROGRAM_INPUT:
                return (index < static_cast<GLuint>(programObject->getActiveAttributeCount()));
    
            case GL_PROGRAM_OUTPUT:
                return (index < static_cast<GLuint>(programObject->getOutputResourceCount()));
    
            case GL_UNIFORM:
                return (index < static_cast<GLuint>(programObject->getActiveUniformCount()));
    
            case GL_BUFFER_VARIABLE:
                return (index < static_cast<GLuint>(programObject->getActiveBufferVariableCount()));
    
            case GL_SHADER_STORAGE_BLOCK:
                return (index < static_cast<GLuint>(programObject->getActiveShaderStorageBlockCount()));
    
            case GL_UNIFORM_BLOCK:
                return (index < programObject->getActiveUniformBlockCount());
    
            case GL_ATOMIC_COUNTER_BUFFER:
                return (index < programObject->getActiveAtomicCounterBufferCount());
    
            case GL_TRANSFORM_FEEDBACK_VARYING:
                return (index < static_cast<GLuint>(programObject->getTransformFeedbackVaryingCount()));
    
            default:
                UNREACHABLE();
                return false;
        }
    }
    
    bool ValidateProgramUniform(Context *context,
                                GLenum valueType,
                                GLuint program,
                                GLint location,
                                GLsizei count)
    {
        // Check for ES31 program uniform entry points
        if (context->getClientVersion() < Version(3, 1))
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES31Required);
            return false;
        }
    
        const LinkedUniform *uniform = nullptr;
        Program *programObject       = GetValidProgram(context, program);
        return ValidateUniformCommonBase(context, programObject, location, count, &uniform) &&
               ValidateUniformValue(context, valueType, uniform->type);
    }
    
    bool ValidateProgramUniformMatrix(Context *context,
                                      GLenum valueType,
                                      GLuint program,
                                      GLint location,
                                      GLsizei count,
                                      GLboolean transpose)
    {
        // Check for ES31 program uniform entry points
        if (context->getClientVersion() < Version(3, 1))
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES31Required);
            return false;
        }
    
        const LinkedUniform *uniform = nullptr;
        Program *programObject       = GetValidProgram(context, program);
        return ValidateUniformCommonBase(context, programObject, location, count, &uniform) &&
               ValidateUniformMatrixValue(context, valueType, uniform->type);
    }
    
    bool ValidateVertexAttribFormatCommon(Context *context,
                                          GLuint attribIndex,
                                          GLint size,
                                          GLenum type,
                                          GLuint relativeOffset,
                                          GLboolean pureInteger)
    {
        if (context->getClientVersion() < ES_3_1)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES31Required);
            return false;
        }
    
        const Caps &caps = context->getCaps();
        if (relativeOffset > static_cast<GLuint>(caps.maxVertexAttribRelativeOffset))
        {
            context->handleError(
                InvalidValue()
                << "relativeOffset cannot be greater than MAX_VERTEX_ATTRIB_RELATIVE_OFFSET.");
            return false;
        }
    
        // [OpenGL ES 3.1] Section 10.3.1 page 243:
        // An INVALID_OPERATION error is generated if the default vertex array object is bound.
        if (context->getGLState().getVertexArrayId() == 0)
        {
            context->handleError(InvalidOperation() << "Default vertex array object is bound.");
            return false;
        }
    
        return ValidateVertexFormatBase(context, attribIndex, size, type, pureInteger);
    }
    
    }  // anonymous namespace
    
    bool ValidateGetBooleani_v(Context *context, GLenum target, GLuint index, GLboolean *data)
    {
        if (context->getClientVersion() < ES_3_1)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES31Required);
            return false;
        }
    
        if (!ValidateIndexedStateQuery(context, target, index, nullptr))
        {
            return false;
        }
    
        return true;
    }
    
    bool ValidateGetBooleani_vRobustANGLE(Context *context,
                                          GLenum target,
                                          GLuint index,
                                          GLsizei bufSize,
                                          GLsizei *length,
                                          GLboolean *data)
    {
        if (context->getClientVersion() < ES_3_1)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES31Required);
            return false;
        }
    
        if (!ValidateRobustEntryPoint(context, bufSize))
        {
            return false;
        }
    
        GLsizei numParams = 0;
    
        if (!ValidateIndexedStateQuery(context, target, index, &numParams))
        {
            return false;
        }
    
        if (!ValidateRobustBufferSize(context, bufSize, numParams))
        {
            return false;
        }
    
        SetRobustLengthParam(length, numParams);
        return true;
    }
    
    bool ValidateDrawIndirectBase(Context *context, PrimitiveMode mode, const void *indirect)
    {
        if (context->getClientVersion() < ES_3_1)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES31Required);
            return false;
        }
    
        // Here the third parameter 1 is only to pass the count validation.
        if (!ValidateDrawBase(context, mode, 1))
        {
            return false;
        }
    
        const State &state = context->getGLState();
    
        // An INVALID_OPERATION error is generated if zero is bound to VERTEX_ARRAY_BINDING,
        // DRAW_INDIRECT_BUFFER or to any enabled vertex array.
        if (!state.getVertexArrayId())
        {
            context->handleError(InvalidOperation() << "zero is bound to VERTEX_ARRAY_BINDING");
            return false;
        }
    
        Buffer *drawIndirectBuffer = state.getTargetBuffer(BufferBinding::DrawIndirect);
        if (!drawIndirectBuffer)
        {
            context->handleError(InvalidOperation() << "zero is bound to DRAW_INDIRECT_BUFFER");
            return false;
        }
    
        // An INVALID_VALUE error is generated if indirect is not a multiple of the size, in basic
        // machine units, of uint.
        GLint64 offset = reinterpret_cast<GLint64>(indirect);
        if ((static_cast<GLuint>(offset) % sizeof(GLuint)) != 0)
        {
            context->handleError(
                InvalidValue()
                << "indirect is not a multiple of the size, in basic machine units, of uint");
            return false;
        }
    
        // ANGLE_multiview spec, revision 1:
        // An INVALID_OPERATION is generated by DrawArraysIndirect and DrawElementsIndirect if the
        // number of views in the draw framebuffer is greater than 1.
        const Framebuffer *drawFramebuffer = context->getGLState().getDrawFramebuffer();
        ASSERT(drawFramebuffer != nullptr);
        if (drawFramebuffer->getNumViews() > 1)
        {
            context->handleError(
                InvalidOperation()
                << "The number of views in the active draw framebuffer is greater than 1.");
            return false;
        }
    
        return true;
    }
    
    bool ValidateDrawArraysIndirect(Context *context, PrimitiveMode mode, const void *indirect)
    {
        const State &state                      = context->getGLState();
        TransformFeedback *curTransformFeedback = state.getCurrentTransformFeedback();
        if (curTransformFeedback && curTransformFeedback->isActive() &&
            !curTransformFeedback->isPaused())
        {
            // EXT_geometry_shader allows transform feedback to work with all draw commands.
            // [EXT_geometry_shader] Section 12.1, "Transform Feedback"
            if (context->getExtensions().geometryShader)
            {
                if (!ValidateTransformFeedbackPrimitiveMode(
                        context, curTransformFeedback->getPrimitiveMode(), mode))
                {
                    ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidDrawModeTransformFeedback);
                    return false;
                }
            }
            else
            {
                // An INVALID_OPERATION error is generated if transform feedback is active and not
                // paused.
                ANGLE_VALIDATION_ERR(context, InvalidOperation(),
                                     UnsupportedDrawModeForTransformFeedback);
                return false;
            }
        }
    
        if (!ValidateDrawIndirectBase(context, mode, indirect))
            return false;
    
        Buffer *drawIndirectBuffer = state.getTargetBuffer(BufferBinding::DrawIndirect);
        CheckedNumeric<size_t> checkedOffset(reinterpret_cast<size_t>(indirect));
        // In OpenGL ES3.1 spec, session 10.5, it defines the struct of DrawArraysIndirectCommand
        // which's size is 4 * sizeof(uint).
        auto checkedSum = checkedOffset + 4 * sizeof(GLuint);
        if (!checkedSum.IsValid() ||
            checkedSum.ValueOrDie() > static_cast<size_t>(drawIndirectBuffer->getSize()))
        {
            context->handleError(
                InvalidOperation()
                << "the  command  would source data beyond the end of the buffer object.");
            return false;
        }
    
        return true;
    }
    
    bool ValidateDrawElementsIndirect(Context *context,
                                      PrimitiveMode mode,
                                      GLenum type,
                                      const void *indirect)
    {
        if (!ValidateDrawElementsBase(context, mode, type))
        {
            return false;
        }
    
        const State &state         = context->getGLState();
        const VertexArray *vao     = state.getVertexArray();
        Buffer *elementArrayBuffer = vao->getElementArrayBuffer().get();
        if (!elementArrayBuffer)
        {
            context->handleError(InvalidOperation() << "zero is bound to ELEMENT_ARRAY_BUFFER");
            return false;
        }
    
        if (!ValidateDrawIndirectBase(context, mode, indirect))
            return false;
    
        Buffer *drawIndirectBuffer = state.getTargetBuffer(BufferBinding::DrawIndirect);
        CheckedNumeric<size_t> checkedOffset(reinterpret_cast<size_t>(indirect));
        // In OpenGL ES3.1 spec, session 10.5, it defines the struct of DrawElementsIndirectCommand
        // which's size is 5 * sizeof(uint).
        auto checkedSum = checkedOffset + 5 * sizeof(GLuint);
        if (!checkedSum.IsValid() ||
            checkedSum.ValueOrDie() > static_cast<size_t>(drawIndirectBuffer->getSize()))
        {
            context->handleError(
                InvalidOperation()
                << "the  command  would source data beyond the end of the buffer object.");
            return false;
        }
    
        return true;
    }
    
    bool ValidateProgramUniform1i(Context *context, GLuint program, GLint location, GLint v0)
    {
        return ValidateProgramUniform1iv(context, program, location, 1, &v0);
    }
    
    bool ValidateProgramUniform2i(Context *context, GLuint program, GLint location, GLint v0, GLint v1)
    {
        GLint xy[2] = {v0, v1};
        return ValidateProgramUniform2iv(context, program, location, 1, xy);
    }
    
    bool ValidateProgramUniform3i(Context *context,
                                  GLuint program,
                                  GLint location,
                                  GLint v0,
                                  GLint v1,
                                  GLint v2)
    {
        GLint xyz[3] = {v0, v1, v2};
        return ValidateProgramUniform3iv(context, program, location, 1, xyz);
    }
    
    bool ValidateProgramUniform4i(Context *context,
                                  GLuint program,
                                  GLint location,
                                  GLint v0,
                                  GLint v1,
                                  GLint v2,
                                  GLint v3)
    {
        GLint xyzw[4] = {v0, v1, v2, v3};
        return ValidateProgramUniform4iv(context, program, location, 1, xyzw);
    }
    
    bool ValidateProgramUniform1ui(Context *context, GLuint program, GLint location, GLuint v0)
    {
        return ValidateProgramUniform1uiv(context, program, location, 1, &v0);
    }
    
    bool ValidateProgramUniform2ui(Context *context,
                                   GLuint program,
                                   GLint location,
                                   GLuint v0,
                                   GLuint v1)
    {
        GLuint xy[2] = {v0, v1};
        return ValidateProgramUniform2uiv(context, program, location, 1, xy);
    }
    
    bool ValidateProgramUniform3ui(Context *context,
                                   GLuint program,
                                   GLint location,
                                   GLuint v0,
                                   GLuint v1,
                                   GLuint v2)
    {
        GLuint xyz[3] = {v0, v1, v2};
        return ValidateProgramUniform3uiv(context, program, location, 1, xyz);
    }
    
    bool ValidateProgramUniform4ui(Context *context,
                                   GLuint program,
                                   GLint location,
                                   GLuint v0,
                                   GLuint v1,
                                   GLuint v2,
                                   GLuint v3)
    {
        GLuint xyzw[4] = {v0, v1, v2, v3};
        return ValidateProgramUniform4uiv(context, program, location, 1, xyzw);
    }
    
    bool ValidateProgramUniform1f(Context *context, GLuint program, GLint location, GLfloat v0)
    {
        return ValidateProgramUniform1fv(context, program, location, 1, &v0);
    }
    
    bool ValidateProgramUniform2f(Context *context,
                                  GLuint program,
                                  GLint location,
                                  GLfloat v0,
                                  GLfloat v1)
    {
        GLfloat xy[2] = {v0, v1};
        return ValidateProgramUniform2fv(context, program, location, 1, xy);
    }
    
    bool ValidateProgramUniform3f(Context *context,
                                  GLuint program,
                                  GLint location,
                                  GLfloat v0,
                                  GLfloat v1,
                                  GLfloat v2)
    {
        GLfloat xyz[3] = {v0, v1, v2};
        return ValidateProgramUniform3fv(context, program, location, 1, xyz);
    }
    
    bool ValidateProgramUniform4f(Context *context,
                                  GLuint program,
                                  GLint location,
                                  GLfloat v0,
                                  GLfloat v1,
                                  GLfloat v2,
                                  GLfloat v3)
    {
        GLfloat xyzw[4] = {v0, v1, v2, v3};
        return ValidateProgramUniform4fv(context, program, location, 1, xyzw);
    }
    
    bool ValidateProgramUniform1iv(Context *context,
                                   GLuint program,
                                   GLint location,
                                   GLsizei count,
                                   const GLint *value)
    {
        // Check for ES31 program uniform entry points
        if (context->getClientVersion() < Version(3, 1))
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES31Required);
            return false;
        }
    
        const LinkedUniform *uniform = nullptr;
        Program *programObject       = GetValidProgram(context, program);
        return ValidateUniformCommonBase(context, programObject, location, count, &uniform) &&
               ValidateUniform1ivValue(context, uniform->type, count, value);
    }
    
    bool ValidateProgramUniform2iv(Context *context,
                                   GLuint program,
                                   GLint location,
                                   GLsizei count,
                                   const GLint *value)
    {
        return ValidateProgramUniform(context, GL_INT_VEC2, program, location, count);
    }
    
    bool ValidateProgramUniform3iv(Context *context,
                                   GLuint program,
                                   GLint location,
                                   GLsizei count,
                                   const GLint *value)
    {
        return ValidateProgramUniform(context, GL_INT_VEC3, program, location, count);
    }
    
    bool ValidateProgramUniform4iv(Context *context,
                                   GLuint program,
                                   GLint location,
                                   GLsizei count,
                                   const GLint *value)
    {
        return ValidateProgramUniform(context, GL_INT_VEC4, program, location, count);
    }
    
    bool ValidateProgramUniform1uiv(Context *context,
                                    GLuint program,
                                    GLint location,
                                    GLsizei count,
                                    const GLuint *value)
    {
        return ValidateProgramUniform(context, GL_UNSIGNED_INT, program, location, count);
    }
    
    bool ValidateProgramUniform2uiv(Context *context,
                                    GLuint program,
                                    GLint location,
                                    GLsizei count,
                                    const GLuint *value)
    {
        return ValidateProgramUniform(context, GL_UNSIGNED_INT_VEC2, program, location, count);
    }
    
    bool ValidateProgramUniform3uiv(Context *context,
                                    GLuint program,
                                    GLint location,
                                    GLsizei count,
                                    const GLuint *value)
    {
        return ValidateProgramUniform(context, GL_UNSIGNED_INT_VEC3, program, location, count);
    }
    
    bool ValidateProgramUniform4uiv(Context *context,
                                    GLuint program,
                                    GLint location,
                                    GLsizei count,
                                    const GLuint *value)
    {
        return ValidateProgramUniform(context, GL_UNSIGNED_INT_VEC4, program, location, count);
    }
    
    bool ValidateProgramUniform1fv(Context *context,
                                   GLuint program,
                                   GLint location,
                                   GLsizei count,
                                   const GLfloat *value)
    {
        return ValidateProgramUniform(context, GL_FLOAT, program, location, count);
    }
    
    bool ValidateProgramUniform2fv(Context *context,
                                   GLuint program,
                                   GLint location,
                                   GLsizei count,
                                   const GLfloat *value)
    {
        return ValidateProgramUniform(context, GL_FLOAT_VEC2, program, location, count);
    }
    
    bool ValidateProgramUniform3fv(Context *context,
                                   GLuint program,
                                   GLint location,
                                   GLsizei count,
                                   const GLfloat *value)
    {
        return ValidateProgramUniform(context, GL_FLOAT_VEC3, program, location, count);
    }
    
    bool ValidateProgramUniform4fv(Context *context,
                                   GLuint program,
                                   GLint location,
                                   GLsizei count,
                                   const GLfloat *value)
    {
        return ValidateProgramUniform(context, GL_FLOAT_VEC4, program, location, count);
    }
    
    bool ValidateProgramUniformMatrix2fv(Context *context,
                                         GLuint program,
                                         GLint location,
                                         GLsizei count,
                                         GLboolean transpose,
                                         const GLfloat *value)
    {
        return ValidateProgramUniformMatrix(context, GL_FLOAT_MAT2, program, location, count,
                                            transpose);
    }
    
    bool ValidateProgramUniformMatrix3fv(Context *context,
                                         GLuint program,
                                         GLint location,
                                         GLsizei count,
                                         GLboolean transpose,
                                         const GLfloat *value)
    {
        return ValidateProgramUniformMatrix(context, GL_FLOAT_MAT3, program, location, count,
                                            transpose);
    }
    
    bool ValidateProgramUniformMatrix4fv(Context *context,
                                         GLuint program,
                                         GLint location,
                                         GLsizei count,
                                         GLboolean transpose,
                                         const GLfloat *value)
    {
        return ValidateProgramUniformMatrix(context, GL_FLOAT_MAT4, program, location, count,
                                            transpose);
    }
    
    bool ValidateProgramUniformMatrix2x3fv(Context *context,
                                           GLuint program,
                                           GLint location,
                                           GLsizei count,
                                           GLboolean transpose,
                                           const GLfloat *value)
    {
        return ValidateProgramUniformMatrix(context, GL_FLOAT_MAT2x3, program, location, count,
                                            transpose);
    }
    
    bool ValidateProgramUniformMatrix3x2fv(Context *context,
                                           GLuint program,
                                           GLint location,
                                           GLsizei count,
                                           GLboolean transpose,
                                           const GLfloat *value)
    {
        return ValidateProgramUniformMatrix(context, GL_FLOAT_MAT3x2, program, location, count,
                                            transpose);
    }
    
    bool ValidateProgramUniformMatrix2x4fv(Context *context,
                                           GLuint program,
                                           GLint location,
                                           GLsizei count,
                                           GLboolean transpose,
                                           const GLfloat *value)
    {
        return ValidateProgramUniformMatrix(context, GL_FLOAT_MAT2x4, program, location, count,
                                            transpose);
    }
    
    bool ValidateProgramUniformMatrix4x2fv(Context *context,
                                           GLuint program,
                                           GLint location,
                                           GLsizei count,
                                           GLboolean transpose,
                                           const GLfloat *value)
    {
        return ValidateProgramUniformMatrix(context, GL_FLOAT_MAT4x2, program, location, count,
                                            transpose);
    }
    
    bool ValidateProgramUniformMatrix3x4fv(Context *context,
                                           GLuint program,
                                           GLint location,
                                           GLsizei count,
                                           GLboolean transpose,
                                           const GLfloat *value)
    {
        return ValidateProgramUniformMatrix(context, GL_FLOAT_MAT3x4, program, location, count,
                                            transpose);
    }
    
    bool ValidateProgramUniformMatrix4x3fv(Context *context,
                                           GLuint program,
                                           GLint location,
                                           GLsizei count,
                                           GLboolean transpose,
                                           const GLfloat *value)
    {
        return ValidateProgramUniformMatrix(context, GL_FLOAT_MAT4x3, program, location, count,
                                            transpose);
    }
    
    bool ValidateGetTexLevelParameterBase(Context *context,
                                          TextureTarget target,
                                          GLint level,
                                          GLenum pname,
                                          GLsizei *length)
    {
        if (context->getClientVersion() < ES_3_1)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES31Required);
            return false;
        }
    
        if (length)
        {
            *length = 0;
        }
    
        TextureType type = TextureTargetToType(target);
    
        if (!ValidTexLevelDestinationTarget(context, type))
        {
            ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidTextureTarget);
            return false;
        }
    
        if (context->getTargetTexture(type) == nullptr)
        {
            context->handleError(InvalidEnum() << "No texture bound.");
            return false;
        }
    
        if (!ValidMipLevel(context, type, level))
        {
            context->handleError(InvalidValue());
            return false;
        }
    
        switch (pname)
        {
            case GL_TEXTURE_RED_TYPE:
            case GL_TEXTURE_GREEN_TYPE:
            case GL_TEXTURE_BLUE_TYPE:
            case GL_TEXTURE_ALPHA_TYPE:
            case GL_TEXTURE_DEPTH_TYPE:
                break;
            case GL_TEXTURE_RED_SIZE:
            case GL_TEXTURE_GREEN_SIZE:
            case GL_TEXTURE_BLUE_SIZE:
            case GL_TEXTURE_ALPHA_SIZE:
            case GL_TEXTURE_DEPTH_SIZE:
            case GL_TEXTURE_STENCIL_SIZE:
            case GL_TEXTURE_SHARED_SIZE:
                break;
            case GL_TEXTURE_INTERNAL_FORMAT:
            case GL_TEXTURE_WIDTH:
            case GL_TEXTURE_HEIGHT:
            case GL_TEXTURE_DEPTH:
                break;
            case GL_TEXTURE_SAMPLES:
            case GL_TEXTURE_FIXED_SAMPLE_LOCATIONS:
                break;
            case GL_TEXTURE_COMPRESSED:
                break;
            default:
                ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidPname);
                return false;
        }
    
        if (length)
        {
            *length = 1;
        }
        return true;
    }
    
    bool ValidateGetTexLevelParameterfv(Context *context,
                                        TextureTarget target,
                                        GLint level,
                                        GLenum pname,
                                        GLfloat *params)
    {
        return ValidateGetTexLevelParameterBase(context, target, level, pname, nullptr);
    }
    
    bool ValidateGetTexLevelParameterfvRobustANGLE(Context *context,
                                                   TextureTarget target,
                                                   GLint level,
                                                   GLenum pname,
                                                   GLsizei bufSize,
                                                   GLsizei *length,
                                                   GLfloat *params)
    {
        UNIMPLEMENTED();
        return false;
    }
    
    bool ValidateGetTexLevelParameteriv(Context *context,
                                        TextureTarget target,
                                        GLint level,
                                        GLenum pname,
                                        GLint *params)
    {
        return ValidateGetTexLevelParameterBase(context, target, level, pname, nullptr);
    }
    
    bool ValidateGetTexLevelParameterivRobustANGLE(Context *context,
                                                   TextureTarget target,
                                                   GLint level,
                                                   GLenum pname,
                                                   GLsizei bufSize,
                                                   GLsizei *length,
                                                   GLint *params)
    {
        UNIMPLEMENTED();
        return false;
    }
    
    bool ValidateTexStorage2DMultisample(Context *context,
                                         TextureType target,
                                         GLsizei samples,
                                         GLint internalFormat,
                                         GLsizei width,
                                         GLsizei height,
                                         GLboolean fixedSampleLocations)
    {
        if (context->getClientVersion() < ES_3_1)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES31Required);
            return false;
        }
    
        if (target != TextureType::_2DMultisample)
        {
            context->handleError(InvalidEnum() << "Target must be TEXTURE_2D_MULTISAMPLE.");
            return false;
        }
    
        if (width < 1 || height < 1)
        {
            ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeSize);
            return false;
        }
    
        const Caps &caps = context->getCaps();
        if (static_cast<GLuint>(width) > caps.max2DTextureSize ||
            static_cast<GLuint>(height) > caps.max2DTextureSize)
        {
            context
                ->handleError(InvalidValue()
                              << "Width and height must be less than or equal to GL_MAX_TEXTURE_SIZE.");
            return false;
        }
    
        if (samples == 0)
        {
            context->handleError(InvalidValue() << "Samples may not be zero.");
            return false;
        }
    
        const TextureCaps &formatCaps = context->getTextureCaps().get(internalFormat);
        if (!formatCaps.textureAttachment)
        {
            context->handleError(InvalidEnum() << "SizedInternalformat must be color-renderable, "
                                                  "depth-renderable, or stencil-renderable.");
            return false;
        }
    
        // The ES3.1 spec(section 8.8) states that an INVALID_ENUM error is generated if internalformat
        // is one of the unsized base internalformats listed in table 8.11.
        const InternalFormat &formatInfo = GetSizedInternalFormatInfo(internalFormat);
        if (formatInfo.internalFormat == GL_NONE)
        {
            context->handleError(
                InvalidEnum()
                << "Internalformat is one of the unsupported unsized base internalformats.");
            return false;
        }
    
        if (static_cast<GLuint>(samples) > formatCaps.getMaxSamples())
        {
            context->handleError(
                InvalidOperation()
                << "Samples must not be greater than maximum supported value for the format.");
            return false;
        }
    
        Texture *texture = context->getTargetTexture(target);
        if (!texture || texture->id() == 0)
        {
            context->handleError(InvalidOperation() << "Zero is bound to target.");
            return false;
        }
    
        if (texture->getImmutableFormat())
        {
            context->handleError(InvalidOperation() << "The value of TEXTURE_IMMUTABLE_FORMAT for "
                                                       "the texture currently bound to target on "
                                                       "the active texture unit is true.");
            return false;
        }
    
        return true;
    }
    
    bool ValidateGetMultisamplefv(Context *context, GLenum pname, GLuint index, GLfloat *val)
    {
        if (context->getClientVersion() < ES_3_1)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES31Required);
            return false;
        }
    
        if (pname != GL_SAMPLE_POSITION)
        {
            context->handleError(InvalidEnum() << "Pname must be SAMPLE_POSITION.");
            return false;
        }
    
        Framebuffer *framebuffer = context->getGLState().getDrawFramebuffer();
        GLint samples            = framebuffer->getSamples(context);
    
        if (index >= static_cast<GLuint>(samples))
        {
            context->handleError(InvalidValue() << "Index must be less than the value of SAMPLES.");
            return false;
        }
    
        return true;
    }
    
    bool ValidateGetMultisamplefvRobustANGLE(Context *context,
                                             GLenum pname,
                                             GLuint index,
                                             GLsizei bufSize,
                                             GLsizei *length,
                                             GLfloat *val)
    {
        UNIMPLEMENTED();
        return false;
    }
    
    bool ValidateFramebufferParameteri(Context *context, GLenum target, GLenum pname, GLint param)
    {
        if (context->getClientVersion() < ES_3_1)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES31Required);
            return false;
        }
    
        if (!ValidFramebufferTarget(context, target))
        {
            context->handleError(InvalidEnum() << "Invalid framebuffer target.");
            return false;
        }
    
        switch (pname)
        {
            case GL_FRAMEBUFFER_DEFAULT_WIDTH:
            {
                GLint maxWidth = context->getCaps().maxFramebufferWidth;
                if (param < 0 || param > maxWidth)
                {
                    context->handleError(
                        InvalidValue()
                        << "Params less than 0 or greater than GL_MAX_FRAMEBUFFER_WIDTH.");
                    return false;
                }
                break;
            }
            case GL_FRAMEBUFFER_DEFAULT_HEIGHT:
            {
                GLint maxHeight = context->getCaps().maxFramebufferHeight;
                if (param < 0 || param > maxHeight)
                {
                    context->handleError(
                        InvalidValue()
                        << "Params less than 0 or greater than GL_MAX_FRAMEBUFFER_HEIGHT.");
                    return false;
                }
                break;
            }
            case GL_FRAMEBUFFER_DEFAULT_SAMPLES:
            {
                GLint maxSamples = context->getCaps().maxFramebufferSamples;
                if (param < 0 || param > maxSamples)
                {
                    context->handleError(
                        InvalidValue()
                        << "Params less than 0 or greater than GL_MAX_FRAMEBUFFER_SAMPLES.");
                    return false;
                }
                break;
            }
            case GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS:
            {
                break;
            }
            case GL_FRAMEBUFFER_DEFAULT_LAYERS_EXT:
            {
                if (!context->getExtensions().geometryShader)
                {
                    ANGLE_VALIDATION_ERR(context, InvalidEnum(), GeometryShaderExtensionNotEnabled);
                    return false;
                }
                GLint maxLayers = context->getCaps().maxFramebufferLayers;
                if (param < 0 || param > maxLayers)
                {
                    ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidFramebufferLayer);
                    return false;
                }
                break;
            }
            default:
            {
                ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidPname);
                return false;
            }
        }
    
        const Framebuffer *framebuffer = context->getGLState().getTargetFramebuffer(target);
        ASSERT(framebuffer);
        if (framebuffer->id() == 0)
        {
            context->handleError(InvalidOperation() << "Default framebuffer is bound to target.");
            return false;
        }
        return true;
    }
    
    bool ValidateGetFramebufferParameteriv(Context *context, GLenum target, GLenum pname, GLint *params)
    {
        if (context->getClientVersion() < ES_3_1)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES31Required);
            return false;
        }
    
        if (!ValidFramebufferTarget(context, target))
        {
            ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidFramebufferTarget);
            return false;
        }
    
        switch (pname)
        {
            case GL_FRAMEBUFFER_DEFAULT_WIDTH:
            case GL_FRAMEBUFFER_DEFAULT_HEIGHT:
            case GL_FRAMEBUFFER_DEFAULT_SAMPLES:
            case GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS:
                break;
            case GL_FRAMEBUFFER_DEFAULT_LAYERS_EXT:
                if (!context->getExtensions().geometryShader)
                {
                    ANGLE_VALIDATION_ERR(context, InvalidEnum(), GeometryShaderExtensionNotEnabled);
                    return false;
                }
                break;
            default:
                ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidPname);
                return false;
        }
    
        const Framebuffer *framebuffer = context->getGLState().getTargetFramebuffer(target);
        ASSERT(framebuffer);
    
        if (framebuffer->id() == 0)
        {
            context->handleError(InvalidOperation() << "Default framebuffer is bound to target.");
            return false;
        }
        return true;
    }
    
    bool ValidateGetFramebufferParameterivRobustANGLE(Context *context,
                                                      GLenum target,
                                                      GLenum pname,
                                                      GLsizei bufSize,
                                                      GLsizei *length,
                                                      GLint *params)
    {
        UNIMPLEMENTED();
        return false;
    }
    
    bool ValidateGetProgramResourceIndex(Context *context,
                                         GLuint program,
                                         GLenum programInterface,
                                         const GLchar *name)
    {
        if (context->getClientVersion() < ES_3_1)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES31Required);
            return false;
        }
    
        Program *programObject = GetValidProgram(context, program);
        if (programObject == nullptr)
        {
            return false;
        }
    
        if (!ValidateNamedProgramInterface(programInterface))
        {
            context->handleError(InvalidEnum() << "Invalid program interface: 0x" << std::hex
                                               << std::uppercase << programInterface);
            return false;
        }
    
        return true;
    }
    
    bool ValidateBindVertexBuffer(Context *context,
                                  GLuint bindingIndex,
                                  GLuint buffer,
                                  GLintptr offset,
                                  GLsizei stride)
    {
        if (context->getClientVersion() < ES_3_1)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES31Required);
            return false;
        }
    
        if (!context->isBufferGenerated(buffer))
        {
            context->handleError(InvalidOperation() << "Buffer is not generated.");
            return false;
        }
    
        const Caps &caps = context->getCaps();
        if (bindingIndex >= caps.maxVertexAttribBindings)
        {
            context->handleError(InvalidValue()
                                 << "bindingindex must be smaller than MAX_VERTEX_ATTRIB_BINDINGS.");
            return false;
        }
    
        if (offset < 0)
        {
            ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeOffset);
            return false;
        }
    
        if (stride < 0 || stride > caps.maxVertexAttribStride)
        {
            context->handleError(InvalidValue()
                                 << "stride must be between 0 and MAX_VERTEX_ATTRIB_STRIDE.");
            return false;
        }
    
        // [OpenGL ES 3.1] Section 10.3.1 page 244:
        // An INVALID_OPERATION error is generated if the default vertex array object is bound.
        if (context->getGLState().getVertexArrayId() == 0)
        {
            context->handleError(InvalidOperation() << "Default vertex array buffer is bound.");
            return false;
        }
    
        return true;
    }
    
    bool ValidateVertexBindingDivisor(Context *context, GLuint bindingIndex, GLuint divisor)
    {
        if (context->getClientVersion() < ES_3_1)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES31Required);
            return false;
        }
    
        const Caps &caps = context->getCaps();
        if (bindingIndex >= caps.maxVertexAttribBindings)
        {
            context->handleError(InvalidValue()
                                 << "bindingindex must be smaller than MAX_VERTEX_ATTRIB_BINDINGS.");
            return false;
        }
    
        // [OpenGL ES 3.1] Section 10.3.1 page 243:
        // An INVALID_OPERATION error is generated if the default vertex array object is bound.
        if (context->getGLState().getVertexArrayId() == 0)
        {
            context->handleError(InvalidOperation() << "Default vertex array object is bound.");
            return false;
        }
    
        return true;
    }
    
    bool ValidateVertexAttribFormat(Context *context,
                                    GLuint attribindex,
                                    GLint size,
                                    GLenum type,
                                    GLboolean normalized,
                                    GLuint relativeoffset)
    {
        return ValidateVertexAttribFormatCommon(context, attribindex, size, type, relativeoffset,
                                                false);
    }
    
    bool ValidateVertexAttribIFormat(Context *context,
                                     GLuint attribindex,
                                     GLint size,
                                     GLenum type,
                                     GLuint relativeoffset)
    {
        return ValidateVertexAttribFormatCommon(context, attribindex, size, type, relativeoffset, true);
    }
    
    bool ValidateVertexAttribBinding(Context *context, GLuint attribIndex, GLuint bindingIndex)
    {
        if (context->getClientVersion() < ES_3_1)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES31Required);
            return false;
        }
    
        // [OpenGL ES 3.1] Section 10.3.1 page 243:
        // An INVALID_OPERATION error is generated if the default vertex array object is bound.
        if (context->getGLState().getVertexArrayId() == 0)
        {
            context->handleError(InvalidOperation() << "Default vertex array object is bound.");
            return false;
        }
    
        const Caps &caps = context->getCaps();
        if (attribIndex >= caps.maxVertexAttributes)
        {
            ANGLE_VALIDATION_ERR(context, InvalidValue(), IndexExceedsMaxVertexAttribute);
            return false;
        }
    
        if (bindingIndex >= caps.maxVertexAttribBindings)
        {
            context->handleError(InvalidValue()
                                 << "bindingindex must be smaller than MAX_VERTEX_ATTRIB_BINDINGS");
            return false;
        }
    
        return true;
    }
    
    bool ValidateGetProgramResourceName(Context *context,
                                        GLuint program,
                                        GLenum programInterface,
                                        GLuint index,
                                        GLsizei bufSize,
                                        GLsizei *length,
                                        GLchar *name)
    {
        if (context->getClientVersion() < ES_3_1)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES31Required);
            return false;
        }
    
        Program *programObject = GetValidProgram(context, program);
        if (programObject == nullptr)
        {
            return false;
        }
    
        if (!ValidateNamedProgramInterface(programInterface))
        {
            context->handleError(InvalidEnum() << "Invalid program interface: 0x" << std::hex
                                               << std::uppercase << programInterface);
            return false;
        }
    
        if (!ValidateProgramResourceIndex(programObject, programInterface, index))
        {
            context->handleError(InvalidValue() << "Invalid index: " << index);
            return false;
        }
    
        if (bufSize < 0)
        {
            ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeBufferSize);
            return false;
        }
    
        return true;
    }
    
    bool ValidateDispatchCompute(Context *context,
                                 GLuint numGroupsX,
                                 GLuint numGroupsY,
                                 GLuint numGroupsZ)
    {
        if (context->getClientVersion() < ES_3_1)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES31Required);
            return false;
        }
    
        const State &state = context->getGLState();
        Program *program   = state.getProgram();
    
        if (program == nullptr || !program->hasLinkedShaderStage(ShaderType::Compute))
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), NoActiveProgramWithComputeShader);
            return false;
        }
    
        const Caps &caps = context->getCaps();
        if (numGroupsX > caps.maxComputeWorkGroupCount[0])
        {
            context->handleError(
                InvalidValue() << "num_groups_x cannot be greater than MAX_COMPUTE_WORK_GROUP_COUNT[0]="
                               << caps.maxComputeWorkGroupCount[0]);
            return false;
        }
        if (numGroupsY > caps.maxComputeWorkGroupCount[1])
        {
            context->handleError(
                InvalidValue() << "num_groups_y cannot be greater than MAX_COMPUTE_WORK_GROUP_COUNT[1]="
                               << caps.maxComputeWorkGroupCount[1]);
            return false;
        }
        if (numGroupsZ > caps.maxComputeWorkGroupCount[2])
        {
            context->handleError(
                InvalidValue() << "num_groups_z cannot be greater than MAX_COMPUTE_WORK_GROUP_COUNT[2]="
                               << caps.maxComputeWorkGroupCount[2]);
            return false;
        }
    
        return true;
    }
    
    bool ValidateDispatchComputeIndirect(Context *context, GLintptr indirect)
    {
        if (context->getClientVersion() < ES_3_1)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES31Required);
            return false;
        }
    
        const State &state = context->getGLState();
        Program *program   = state.getProgram();
    
        if (program == nullptr || !program->hasLinkedShaderStage(ShaderType::Compute))
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), NoActiveProgramWithComputeShader);
            return false;
        }
    
        if (indirect < 0)
        {
            ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeOffset);
            return false;
        }
    
        if ((indirect & (sizeof(GLuint) - 1)) != 0)
        {
            ANGLE_VALIDATION_ERR(context, InvalidValue(), OffsetMustBeMultipleOfUint);
            return false;
        }
    
        Buffer *dispatchIndirectBuffer = state.getTargetBuffer(BufferBinding::DispatchIndirect);
        if (!dispatchIndirectBuffer)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), DispatchIndirectBufferNotBound);
            return false;
        }
    
        CheckedNumeric<GLuint64> checkedOffset(static_cast<GLuint64>(indirect));
        auto checkedSum = checkedOffset + static_cast<GLuint64>(3 * sizeof(GLuint));
        if (!checkedSum.IsValid() ||
            checkedSum.ValueOrDie() > static_cast<GLuint64>(dispatchIndirectBuffer->getSize()))
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), InsufficientBufferSize);
            return false;
        }
    
        return true;
    }
    
    bool ValidateBindImageTexture(Context *context,
                                  GLuint unit,
                                  GLuint texture,
                                  GLint level,
                                  GLboolean layered,
                                  GLint layer,
                                  GLenum access,
                                  GLenum format)
    {
        GLuint maxImageUnits = context->getCaps().maxImageUnits;
        if (unit >= maxImageUnits)
        {
            context->handleError(InvalidValue()
                                 << "unit cannot be greater than or equal than MAX_IMAGE_UNITS = "
                                 << maxImageUnits);
            return false;
        }
    
        if (level < 0)
        {
            context->handleError(InvalidValue() << "level is negative.");
            return false;
        }
    
        if (layer < 0)
        {
            context->handleError(InvalidValue() << "layer is negative.");
            return false;
        }
    
        if (access != GL_READ_ONLY && access != GL_WRITE_ONLY && access != GL_READ_WRITE)
        {
            context->handleError(InvalidEnum() << "access is not one of the supported tokens.");
            return false;
        }
    
        switch (format)
        {
            case GL_RGBA32F:
            case GL_RGBA16F:
            case GL_R32F:
            case GL_RGBA32UI:
            case GL_RGBA16UI:
            case GL_RGBA8UI:
            case GL_R32UI:
            case GL_RGBA32I:
            case GL_RGBA16I:
            case GL_RGBA8I:
            case GL_R32I:
            case GL_RGBA8:
            case GL_RGBA8_SNORM:
                break;
            default:
                context->handleError(InvalidValue()
                                     << "format is not one of supported image unit formats.");
                return false;
        }
    
        if (texture != 0)
        {
            Texture *tex = context->getTexture(texture);
    
            if (tex == nullptr)
            {
                context->handleError(InvalidValue()
                                     << "texture is not the name of an existing texture object.");
                return false;
            }
    
            if (!tex->getImmutableFormat())
            {
                context->handleError(InvalidOperation()
                                     << "texture is not the name of an immutable texture object.");
                return false;
            }
        }
    
        return true;
    }
    
    bool ValidateGetProgramResourceLocation(Context *context,
                                            GLuint program,
                                            GLenum programInterface,
                                            const GLchar *name)
    {
        if (context->getClientVersion() < ES_3_1)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES31Required);
            return false;
        }
    
        Program *programObject = GetValidProgram(context, program);
        if (programObject == nullptr)
        {
            return false;
        }
    
        if (!programObject->isLinked())
        {
            context->handleError(InvalidOperation() << "Program is not successfully linked.");
            return false;
        }
    
        if (!ValidateLocationProgramInterface(programInterface))
        {
            context->handleError(InvalidEnum() << "Invalid program interface.");
            return false;
        }
        return true;
    }
    
    bool ValidateGetProgramResourceiv(Context *context,
                                      GLuint program,
                                      GLenum programInterface,
                                      GLuint index,
                                      GLsizei propCount,
                                      const GLenum *props,
                                      GLsizei bufSize,
                                      GLsizei *length,
                                      GLint *params)
    {
        if (context->getClientVersion() < ES_3_1)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES31Required);
            return false;
        }
    
        Program *programObject = GetValidProgram(context, program);
        if (programObject == nullptr)
        {
            return false;
        }
        if (!ValidateProgramInterface(programInterface))
        {
            context->handleError(InvalidEnum() << "Invalid program interface.");
            return false;
        }
        if (propCount <= 0)
        {
            context->handleError(InvalidValue() << "Invalid propCount.");
            return false;
        }
        if (bufSize < 0)
        {
            context->handleError(InvalidValue() << "Invalid bufSize.");
            return false;
        }
        if (!ValidateProgramResourceIndex(programObject, programInterface, index))
        {
            context->handleError(InvalidValue() << "Invalid index: " << index);
            return false;
        }
        for (GLsizei i = 0; i < propCount; i++)
        {
            if (!ValidateProgramResourceProperty(context, props[i]))
            {
                context->handleError(InvalidEnum() << "Invalid prop.");
                return false;
            }
            if (!ValidateProgramResourcePropertyByInterface(props[i], programInterface))
            {
                context->handleError(InvalidOperation() << "Not an allowed prop for interface");
                return false;
            }
        }
        return true;
    }
    
    bool ValidateGetProgramInterfaceiv(Context *context,
                                       GLuint program,
                                       GLenum programInterface,
                                       GLenum pname,
                                       GLint *params)
    {
        if (context->getClientVersion() < ES_3_1)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES31Required);
            return false;
        }
    
        Program *programObject = GetValidProgram(context, program);
        if (programObject == nullptr)
        {
            return false;
        }
    
        if (!ValidateProgramInterface(programInterface))
        {
            context->handleError(InvalidEnum() << "Invalid program interface.");
            return false;
        }
    
        switch (pname)
        {
            case GL_ACTIVE_RESOURCES:
            case GL_MAX_NAME_LENGTH:
            case GL_MAX_NUM_ACTIVE_VARIABLES:
                break;
    
            default:
                context->handleError(InvalidEnum() << "Unknown property of program interface.");
                return false;
        }
    
        if (pname == GL_MAX_NAME_LENGTH && programInterface == GL_ATOMIC_COUNTER_BUFFER)
        {
            context->handleError(InvalidOperation()
                                 << "Active atomic counter resources are not assigned name strings.");
            return false;
        }
    
        if (pname == GL_MAX_NUM_ACTIVE_VARIABLES)
        {
            switch (programInterface)
            {
                case GL_ATOMIC_COUNTER_BUFFER:
                case GL_SHADER_STORAGE_BLOCK:
                case GL_UNIFORM_BLOCK:
                    break;
    
                default:
                    context->handleError(
                        InvalidOperation()
                        << "MAX_NUM_ACTIVE_VARIABLES requires a buffer or block interface.");
                    return false;
            }
        }
    
        return true;
    }
    
    bool ValidateGetProgramInterfaceivRobustANGLE(Context *context,
                                                  GLuint program,
                                                  GLenum programInterface,
                                                  GLenum pname,
                                                  GLsizei bufSize,
                                                  GLsizei *length,
                                                  GLint *params)
    {
        UNIMPLEMENTED();
        return false;
    }
    
    static bool ValidateGenOrDeleteES31(Context *context, GLint n)
    {
        if (context->getClientVersion() < ES_3_1)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES31Required);
            return false;
        }
    
        return ValidateGenOrDelete(context, n);
    }
    
    bool ValidateGenProgramPipelines(Context *context, GLint n, GLuint *)
    {
        return ValidateGenOrDeleteES31(context, n);
    }
    
    bool ValidateDeleteProgramPipelines(Context *context, GLint n, const GLuint *)
    {
        return ValidateGenOrDeleteES31(context, n);
    }
    
    bool ValidateBindProgramPipeline(Context *context, GLuint pipeline)
    {
        if (context->getClientVersion() < ES_3_1)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES31Required);
            return false;
        }
    
        if (!context->isProgramPipelineGenerated(pipeline))
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ObjectNotGenerated);
            return false;
        }
    
        return true;
    }
    
    bool ValidateIsProgramPipeline(Context *context, GLuint pipeline)
    {
        if (context->getClientVersion() < ES_3_1)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES31Required);
            return false;
        }
    
        return true;
    }
    
    bool ValidateUseProgramStages(Context *context, GLuint pipeline, GLbitfield stages, GLuint program)
    {
        UNIMPLEMENTED();
        return false;
    }
    
    bool ValidateActiveShaderProgram(Context *context, GLuint pipeline, GLuint program)
    {
        UNIMPLEMENTED();
        return false;
    }
    
    bool ValidateCreateShaderProgramv(Context *context,
                                      ShaderType type,
                                      GLsizei count,
                                      const GLchar *const *strings)
    {
        UNIMPLEMENTED();
        return false;
    }
    
    bool ValidateGetProgramPipelineiv(Context *context, GLuint pipeline, GLenum pname, GLint *params)
    {
        UNIMPLEMENTED();
        return false;
    }
    
    bool ValidateValidateProgramPipeline(Context *context, GLuint pipeline)
    {
        UNIMPLEMENTED();
        return false;
    }
    
    bool ValidateGetProgramPipelineInfoLog(Context *context,
                                           GLuint pipeline,
                                           GLsizei bufSize,
                                           GLsizei *length,
                                           GLchar *infoLog)
    {
        UNIMPLEMENTED();
        return false;
    }
    
    bool ValidateMemoryBarrier(Context *context, GLbitfield barriers)
    {
        if (context->getClientVersion() < ES_3_1)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES31Required);
            return false;
        }
    
        if (barriers == GL_ALL_BARRIER_BITS)
        {
            return true;
        }
    
        GLbitfield supported_barrier_bits =
            GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT | GL_ELEMENT_ARRAY_BARRIER_BIT | GL_UNIFORM_BARRIER_BIT |
            GL_TEXTURE_FETCH_BARRIER_BIT | GL_SHADER_IMAGE_ACCESS_BARRIER_BIT | GL_COMMAND_BARRIER_BIT |
            GL_PIXEL_BUFFER_BARRIER_BIT | GL_TEXTURE_UPDATE_BARRIER_BIT | GL_BUFFER_UPDATE_BARRIER_BIT |
            GL_FRAMEBUFFER_BARRIER_BIT | GL_TRANSFORM_FEEDBACK_BARRIER_BIT |
            GL_ATOMIC_COUNTER_BARRIER_BIT | GL_SHADER_STORAGE_BARRIER_BIT;
        if ((barriers & ~supported_barrier_bits) != 0)
        {
            ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidMemoryBarrierBit);
            return false;
        }
    
        return true;
    }
    
    bool ValidateMemoryBarrierByRegion(Context *context, GLbitfield barriers)
    {
        if (context->getClientVersion() < ES_3_1)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES31Required);
            return false;
        }
    
        if (barriers == GL_ALL_BARRIER_BITS)
        {
            return true;
        }
    
        GLbitfield supported_barrier_bits = GL_ATOMIC_COUNTER_BARRIER_BIT | GL_FRAMEBUFFER_BARRIER_BIT |
                                            GL_SHADER_IMAGE_ACCESS_BARRIER_BIT |
                                            GL_SHADER_STORAGE_BARRIER_BIT |
                                            GL_TEXTURE_FETCH_BARRIER_BIT | GL_UNIFORM_BARRIER_BIT;
        if ((barriers & ~supported_barrier_bits) != 0)
        {
            ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidMemoryBarrierBit);
            return false;
        }
    
        return true;
    }
    
    bool ValidateSampleMaski(Context *context, GLuint maskNumber, GLbitfield mask)
    {
        if (context->getClientVersion() < ES_3_1)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES31Required);
            return false;
        }
    
        if (maskNumber >= context->getCaps().maxSampleMaskWords)
        {
            ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidSampleMaskNumber);
            return false;
        }
    
        return true;
    }
    
    bool ValidateFramebufferTextureEXT(Context *context,
                                       GLenum target,
                                       GLenum attachment,
                                       GLuint texture,
                                       GLint level)
    {
        if (!context->getExtensions().geometryShader)
        {
            ANGLE_VALIDATION_ERR(context, InvalidOperation(), GeometryShaderExtensionNotEnabled);
            return false;
        }
    
        if (texture != 0)
        {
            gl::Texture *tex = context->getTexture(texture);
    
            // [EXT_geometry_shader] Section 9.2.8 "Attaching Texture Images to a Framebuffer"
            // An INVALID_VALUE error is generated if <texture> is not the name of a texture object.
            // We put this validation before ValidateFramebufferTextureBase because it is an
            // INVALID_OPERATION error for both FramebufferTexture2D and FramebufferTextureLayer:
            // [OpenGL ES 3.1] Chapter 9.2.8 (FramebufferTexture2D)
            // An INVALID_OPERATION error is generated if texture is not zero, and does not name an
            // existing texture object of type matching textarget.
            // [OpenGL ES 3.1 Chapter 9.2.8 (FramebufferTextureLayer)
            // An INVALID_OPERATION error is generated if texture is non-zero and is not the name of a
            // three-dimensional or two-dimensional array texture.
            if (tex == nullptr)
            {
                ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidTextureName);
                return false;
            }
    
            if (!ValidMipLevel(context, tex->getType(), level))
            {
                ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidMipLevel);
                return false;
            }
        }
    
        if (!ValidateFramebufferTextureBase(context, target, attachment, texture, level))
        {
            return false;
        }
    
        return true;
    }
    
    }  // namespace gl