diff options
Diffstat (limited to 'gfx/angle/checkout/src/libANGLE/validationES31.cpp')
-rw-r--r-- | gfx/angle/checkout/src/libANGLE/validationES31.cpp | 3157 |
1 files changed, 3157 insertions, 0 deletions
diff --git a/gfx/angle/checkout/src/libANGLE/validationES31.cpp b/gfx/angle/checkout/src/libANGLE/validationES31.cpp new file mode 100644 index 0000000000..c0222f7497 --- /dev/null +++ b/gfx/angle/checkout/src/libANGLE/validationES31.cpp @@ -0,0 +1,3157 @@ +// +// Copyright 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_autogen.h" + +#include "libANGLE/Context.h" +#include "libANGLE/ErrorStrings.h" +#include "libANGLE/Framebuffer.h" +#include "libANGLE/ProgramExecutable.h" +#include "libANGLE/VertexArray.h" +#include "libANGLE/validationES.h" +#include "libANGLE/validationES2_autogen.h" +#include "libANGLE/validationES31.h" +#include "libANGLE/validationES3_autogen.h" + +#include "common/utilities.h" + +using namespace angle; + +namespace gl +{ +using namespace err; + +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, + angle::EntryPoint entryPoint, + 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().geometryShaderAny() || + context->getClientVersion() >= ES_3_2; + + case GL_REFERENCED_BY_TESS_CONTROL_SHADER_EXT: + case GL_REFERENCED_BY_TESS_EVALUATION_SHADER_EXT: + case GL_IS_PER_PATCH_EXT: + return context->getExtensions().tessellationShaderEXT || + context->getClientVersion() >= ES_3_2; + + case GL_LOCATION_INDEX_EXT: + return context->getExtensions().blendFuncExtendedEXT; + + 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_LOCATION_INDEX_EXT: + { + // EXT_blend_func_extended + return (programInterface == GL_PROGRAM_OUTPUT); + } + + 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: + case GL_REFERENCED_BY_TESS_CONTROL_SHADER_EXT: + case GL_REFERENCED_BY_TESS_EVALUATION_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; + } + } + case GL_IS_PER_PATCH_EXT: + switch (programInterface) + { + case GL_PROGRAM_INPUT: + case GL_PROGRAM_OUTPUT: + return true; + } + 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->getState().getProgramInputs().size())); + + 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 ValidateProgramUniformBase(const Context *context, + angle::EntryPoint entryPoint, + GLenum valueType, + ShaderProgramID program, + UniformLocation location, + GLsizei count) +{ + const LinkedUniform *uniform = nullptr; + Program *programObject = GetValidProgram(context, entryPoint, program); + return ValidateUniformCommonBase(context, entryPoint, programObject, location, count, + &uniform) && + ValidateUniformValue(context, entryPoint, valueType, uniform->type); +} + +bool ValidateProgramUniformMatrixBase(const Context *context, + angle::EntryPoint entryPoint, + GLenum valueType, + ShaderProgramID program, + UniformLocation location, + GLsizei count, + GLboolean transpose) +{ + const LinkedUniform *uniform = nullptr; + Program *programObject = GetValidProgram(context, entryPoint, program); + return ValidateUniformCommonBase(context, entryPoint, programObject, location, count, + &uniform) && + ValidateUniformMatrixValue(context, entryPoint, valueType, uniform->type); +} + +bool ValidateVertexAttribFormatCommon(const Context *context, + angle::EntryPoint entryPoint, + GLuint relativeOffset) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + const Caps &caps = context->getCaps(); + if (relativeOffset > static_cast<GLuint>(caps.maxVertexAttribRelativeOffset)) + { + context->validationError(entryPoint, GL_INVALID_VALUE, kRelativeOffsetTooLarge); + 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->getState().getVertexArrayId().value == 0) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kDefaultVertexArray); + return false; + } + + return true; +} + +} // anonymous namespace + +bool ValidateGetBooleani_v(const Context *context, + angle::EntryPoint entryPoint, + GLenum target, + GLuint index, + const GLboolean *data) +{ + if (context->getClientVersion() < ES_3_1 && !context->getExtensions().drawBuffersIndexedAny()) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, + kES31OrDrawBuffersIndexedExtensionNotAvailable); + return false; + } + + if (!ValidateIndexedStateQuery(context, entryPoint, target, index, nullptr)) + { + return false; + } + + return true; +} + +bool ValidateGetBooleani_vRobustANGLE(const Context *context, + angle::EntryPoint entryPoint, + GLenum target, + GLuint index, + GLsizei bufSize, + const GLsizei *length, + const GLboolean *data) +{ + if (context->getClientVersion() < ES_3_1 && !context->getExtensions().drawBuffersIndexedAny()) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, + kES31OrDrawBuffersIndexedExtensionNotAvailable); + return false; + } + + if (!ValidateRobustEntryPoint(context, entryPoint, bufSize)) + { + return false; + } + + GLsizei numParams = 0; + + if (!ValidateIndexedStateQuery(context, entryPoint, target, index, &numParams)) + { + return false; + } + + if (!ValidateRobustBufferSize(context, entryPoint, bufSize, numParams)) + { + return false; + } + + SetRobustLengthParam(length, numParams); + return true; +} + +bool ValidateDrawIndirectBase(const Context *context, + angle::EntryPoint entryPoint, + PrimitiveMode mode, + const void *indirect) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + // Here the third parameter 1 is only to pass the count validation. + if (!ValidateDrawBase(context, entryPoint, mode)) + { + return false; + } + + const State &state = context->getState(); + + // 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().value == 0) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kDefaultVertexArray); + return false; + } + + if (context->getStateCache().hasAnyActiveClientAttrib()) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kClientDataInVertexArray); + return false; + } + + Buffer *drawIndirectBuffer = state.getTargetBuffer(BufferBinding::DrawIndirect); + if (!drawIndirectBuffer) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kDrawIndirectBufferNotBound); + 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->validationError(entryPoint, GL_INVALID_VALUE, kInvalidIndirectOffset); + return false; + } + + return true; +} + +bool ValidateDrawArraysIndirect(const Context *context, + angle::EntryPoint entryPoint, + PrimitiveMode mode, + const void *indirect) +{ + const State &state = context->getState(); + 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().geometryShaderAny() || context->getClientVersion() >= ES_3_2) + { + if (!ValidateTransformFeedbackPrimitiveMode( + context, entryPoint, curTransformFeedback->getPrimitiveMode(), mode)) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, + kInvalidDrawModeTransformFeedback); + return false; + } + } + else + { + // An INVALID_OPERATION error is generated if transform feedback is active and not + // paused. + context->validationError(entryPoint, GL_INVALID_OPERATION, + kUnsupportedDrawModeForTransformFeedback); + return false; + } + } + + if (!ValidateDrawIndirectBase(context, entryPoint, 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->validationError(entryPoint, GL_INVALID_OPERATION, kParamOverflow); + return false; + } + + return true; +} + +bool ValidateDrawElementsIndirect(const Context *context, + angle::EntryPoint entryPoint, + PrimitiveMode mode, + DrawElementsType type, + const void *indirect) +{ + if (!ValidateDrawElementsBase(context, entryPoint, mode, type)) + { + return false; + } + + const State &state = context->getState(); + const VertexArray *vao = state.getVertexArray(); + Buffer *elementArrayBuffer = vao->getElementArrayBuffer(); + if (!elementArrayBuffer) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kMustHaveElementArrayBinding); + return false; + } + + if (!ValidateDrawIndirectBase(context, entryPoint, 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->validationError(entryPoint, GL_INVALID_OPERATION, kParamOverflow); + return false; + } + + return true; +} + +bool ValidateMultiDrawIndirectBase(const Context *context, + angle::EntryPoint entryPoint, + GLsizei drawcount, + GLsizei stride) +{ + if (!context->getExtensions().multiDrawIndirectEXT) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled); + return false; + } + + // An INVALID_VALUE error is generated if stride is neither 0 nor a multiple of 4. + if ((stride & 3) != 0) + { + context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidDrawBufferValue); + return false; + } + + // An INVALID_VALUE error is generated if drawcount is not positive. + if (drawcount <= 0) + { + context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidValueNonPositive); + return false; + } + + return true; +} + +bool ValidateProgramUniform1iBase(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID program, + UniformLocation location, + GLint v0) +{ + return ValidateProgramUniform1ivBase(context, entryPoint, program, location, 1, &v0); +} + +bool ValidateProgramUniform2iBase(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID program, + UniformLocation location, + GLint v0, + GLint v1) +{ + GLint xy[2] = {v0, v1}; + return ValidateProgramUniform2ivBase(context, entryPoint, program, location, 1, xy); +} + +bool ValidateProgramUniform3iBase(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID program, + UniformLocation location, + GLint v0, + GLint v1, + GLint v2) +{ + GLint xyz[3] = {v0, v1, v2}; + return ValidateProgramUniform3ivBase(context, entryPoint, program, location, 1, xyz); +} + +bool ValidateProgramUniform4iBase(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID program, + UniformLocation location, + GLint v0, + GLint v1, + GLint v2, + GLint v3) +{ + GLint xyzw[4] = {v0, v1, v2, v3}; + return ValidateProgramUniform4ivBase(context, entryPoint, program, location, 1, xyzw); +} + +bool ValidateProgramUniform1uiBase(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID program, + UniformLocation location, + GLuint v0) +{ + return ValidateProgramUniform1uivBase(context, entryPoint, program, location, 1, &v0); +} + +bool ValidateProgramUniform2uiBase(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID program, + UniformLocation location, + GLuint v0, + GLuint v1) +{ + GLuint xy[2] = {v0, v1}; + return ValidateProgramUniform2uivBase(context, entryPoint, program, location, 1, xy); +} + +bool ValidateProgramUniform3uiBase(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID program, + UniformLocation location, + GLuint v0, + GLuint v1, + GLuint v2) +{ + GLuint xyz[3] = {v0, v1, v2}; + return ValidateProgramUniform3uivBase(context, entryPoint, program, location, 1, xyz); +} + +bool ValidateProgramUniform4uiBase(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID program, + UniformLocation location, + GLuint v0, + GLuint v1, + GLuint v2, + GLuint v3) +{ + GLuint xyzw[4] = {v0, v1, v2, v3}; + return ValidateProgramUniform4uivBase(context, entryPoint, program, location, 1, xyzw); +} + +bool ValidateProgramUniform1fBase(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID program, + UniformLocation location, + GLfloat v0) +{ + return ValidateProgramUniform1fvBase(context, entryPoint, program, location, 1, &v0); +} + +bool ValidateProgramUniform2fBase(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID program, + UniformLocation location, + GLfloat v0, + GLfloat v1) +{ + GLfloat xy[2] = {v0, v1}; + return ValidateProgramUniform2fvBase(context, entryPoint, program, location, 1, xy); +} + +bool ValidateProgramUniform3fBase(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID program, + UniformLocation location, + GLfloat v0, + GLfloat v1, + GLfloat v2) +{ + GLfloat xyz[3] = {v0, v1, v2}; + return ValidateProgramUniform3fvBase(context, entryPoint, program, location, 1, xyz); +} + +bool ValidateProgramUniform4fBase(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID program, + UniformLocation location, + GLfloat v0, + GLfloat v1, + GLfloat v2, + GLfloat v3) +{ + GLfloat xyzw[4] = {v0, v1, v2, v3}; + return ValidateProgramUniform4fvBase(context, entryPoint, program, location, 1, xyzw); +} + +bool ValidateProgramUniform1ivBase(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID program, + UniformLocation location, + GLsizei count, + const GLint *value) +{ + const LinkedUniform *uniform = nullptr; + Program *programObject = GetValidProgram(context, entryPoint, program); + return ValidateUniformCommonBase(context, entryPoint, programObject, location, count, + &uniform) && + ValidateUniform1ivValue(context, entryPoint, uniform->type, count, value); +} + +bool ValidateProgramUniform2ivBase(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID program, + UniformLocation location, + GLsizei count, + const GLint *value) +{ + return ValidateProgramUniformBase(context, entryPoint, GL_INT_VEC2, program, location, count); +} + +bool ValidateProgramUniform3ivBase(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID program, + UniformLocation location, + GLsizei count, + const GLint *value) +{ + return ValidateProgramUniformBase(context, entryPoint, GL_INT_VEC3, program, location, count); +} + +bool ValidateProgramUniform4ivBase(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID program, + UniformLocation location, + GLsizei count, + const GLint *value) +{ + return ValidateProgramUniformBase(context, entryPoint, GL_INT_VEC4, program, location, count); +} + +bool ValidateProgramUniform1uivBase(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID program, + UniformLocation location, + GLsizei count, + const GLuint *value) +{ + return ValidateProgramUniformBase(context, entryPoint, GL_UNSIGNED_INT, program, location, + count); +} + +bool ValidateProgramUniform2uivBase(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID program, + UniformLocation location, + GLsizei count, + const GLuint *value) +{ + return ValidateProgramUniformBase(context, entryPoint, GL_UNSIGNED_INT_VEC2, program, location, + count); +} + +bool ValidateProgramUniform3uivBase(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID program, + UniformLocation location, + GLsizei count, + const GLuint *value) +{ + return ValidateProgramUniformBase(context, entryPoint, GL_UNSIGNED_INT_VEC3, program, location, + count); +} + +bool ValidateProgramUniform4uivBase(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID program, + UniformLocation location, + GLsizei count, + const GLuint *value) +{ + return ValidateProgramUniformBase(context, entryPoint, GL_UNSIGNED_INT_VEC4, program, location, + count); +} + +bool ValidateProgramUniform1fvBase(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID program, + UniformLocation location, + GLsizei count, + const GLfloat *value) +{ + return ValidateProgramUniformBase(context, entryPoint, GL_FLOAT, program, location, count); +} + +bool ValidateProgramUniform2fvBase(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID program, + UniformLocation location, + GLsizei count, + const GLfloat *value) +{ + return ValidateProgramUniformBase(context, entryPoint, GL_FLOAT_VEC2, program, location, count); +} + +bool ValidateProgramUniform3fvBase(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID program, + UniformLocation location, + GLsizei count, + const GLfloat *value) +{ + return ValidateProgramUniformBase(context, entryPoint, GL_FLOAT_VEC3, program, location, count); +} + +bool ValidateProgramUniform4fvBase(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID program, + UniformLocation location, + GLsizei count, + const GLfloat *value) +{ + return ValidateProgramUniformBase(context, entryPoint, GL_FLOAT_VEC4, program, location, count); +} + +bool ValidateProgramUniformMatrix2fvBase(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID program, + UniformLocation location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + return ValidateProgramUniformMatrixBase(context, entryPoint, GL_FLOAT_MAT2, program, location, + count, transpose); +} + +bool ValidateProgramUniformMatrix3fvBase(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID program, + UniformLocation location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + return ValidateProgramUniformMatrixBase(context, entryPoint, GL_FLOAT_MAT3, program, location, + count, transpose); +} + +bool ValidateProgramUniformMatrix4fvBase(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID program, + UniformLocation location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + return ValidateProgramUniformMatrixBase(context, entryPoint, GL_FLOAT_MAT4, program, location, + count, transpose); +} + +bool ValidateProgramUniformMatrix2x3fvBase(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID program, + UniformLocation location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + return ValidateProgramUniformMatrixBase(context, entryPoint, GL_FLOAT_MAT2x3, program, location, + count, transpose); +} + +bool ValidateProgramUniformMatrix3x2fvBase(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID program, + UniformLocation location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + return ValidateProgramUniformMatrixBase(context, entryPoint, GL_FLOAT_MAT3x2, program, location, + count, transpose); +} + +bool ValidateProgramUniformMatrix2x4fvBase(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID program, + UniformLocation location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + return ValidateProgramUniformMatrixBase(context, entryPoint, GL_FLOAT_MAT2x4, program, location, + count, transpose); +} + +bool ValidateProgramUniformMatrix4x2fvBase(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID program, + UniformLocation location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + return ValidateProgramUniformMatrixBase(context, entryPoint, GL_FLOAT_MAT4x2, program, location, + count, transpose); +} + +bool ValidateProgramUniformMatrix3x4fvBase(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID program, + UniformLocation location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + return ValidateProgramUniformMatrixBase(context, entryPoint, GL_FLOAT_MAT3x4, program, location, + count, transpose); +} + +bool ValidateProgramUniformMatrix4x3fvBase(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID program, + UniformLocation location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + return ValidateProgramUniformMatrixBase(context, entryPoint, GL_FLOAT_MAT4x3, program, location, + count, transpose); +} + +bool ValidateGetTexLevelParameterfv(const Context *context, + angle::EntryPoint entryPoint, + TextureTarget target, + GLint level, + GLenum pname, + const GLfloat *params) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateGetTexLevelParameterBase(context, entryPoint, target, level, pname, nullptr); +} + +bool ValidateGetTexLevelParameterfvRobustANGLE(const Context *context, + angle::EntryPoint entryPoint, + TextureTarget target, + GLint level, + GLenum pname, + GLsizei bufSize, + const GLsizei *length, + const GLfloat *params) +{ + UNIMPLEMENTED(); + return false; +} + +bool ValidateGetTexLevelParameteriv(const Context *context, + angle::EntryPoint entryPoint, + TextureTarget target, + GLint level, + GLenum pname, + const GLint *params) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateGetTexLevelParameterBase(context, entryPoint, target, level, pname, nullptr); +} + +bool ValidateGetTexLevelParameterivRobustANGLE(const Context *context, + angle::EntryPoint entryPoint, + TextureTarget target, + GLint level, + GLenum pname, + GLsizei bufSize, + const GLsizei *length, + const GLint *params) +{ + UNIMPLEMENTED(); + return false; +} + +bool ValidateTexStorage2DMultisample(const Context *context, + angle::EntryPoint entryPoint, + TextureType target, + GLsizei samples, + GLenum internalFormat, + GLsizei width, + GLsizei height, + GLboolean fixedSampleLocations) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateTexStorage2DMultisampleBase(context, entryPoint, target, samples, internalFormat, + width, height); +} + +bool ValidateTexStorageMem2DMultisampleEXT(const Context *context, + angle::EntryPoint entryPoint, + TextureType target, + GLsizei samples, + GLenum internalFormat, + GLsizei width, + GLsizei height, + GLboolean fixedSampleLocations, + MemoryObjectID memory, + GLuint64 offset) +{ + if (!context->getExtensions().memoryObjectEXT) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled); + return false; + } + + UNIMPLEMENTED(); + return false; +} + +bool ValidateGetMultisamplefv(const Context *context, + angle::EntryPoint entryPoint, + GLenum pname, + GLuint index, + const GLfloat *val) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateGetMultisamplefvBase(context, entryPoint, pname, index, val); +} + +bool ValidateGetMultisamplefvRobustANGLE(const Context *context, + angle::EntryPoint entryPoint, + GLenum pname, + GLuint index, + GLsizei bufSize, + const GLsizei *length, + const GLfloat *val) +{ + UNIMPLEMENTED(); + return false; +} + +bool ValidateFramebufferParameteri(const Context *context, + angle::EntryPoint entryPoint, + GLenum target, + GLenum pname, + GLint param) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateFramebufferParameteriBase(context, entryPoint, target, pname, param); +} + +bool ValidateGetFramebufferParameteriv(const Context *context, + angle::EntryPoint entryPoint, + GLenum target, + GLenum pname, + const GLint *params) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateGetFramebufferParameterivBase(context, entryPoint, target, pname, params); +} + +bool ValidateGetFramebufferParameterivRobustANGLE(const Context *context, + angle::EntryPoint entryPoint, + GLenum target, + GLenum pname, + GLsizei bufSize, + const GLsizei *length, + const GLint *params) +{ + UNIMPLEMENTED(); + return false; +} + +bool ValidateGetProgramResourceIndex(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID program, + GLenum programInterface, + const GLchar *name) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + Program *programObject = GetValidProgram(context, entryPoint, program); + if (programObject == nullptr) + { + return false; + } + + if (!ValidateNamedProgramInterface(programInterface)) + { + context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidProgramInterface); + return false; + } + + return true; +} + +bool ValidateBindVertexBuffer(const Context *context, + angle::EntryPoint entryPoint, + GLuint bindingIndex, + BufferID buffer, + GLintptr offset, + GLsizei stride) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + if (!context->isBufferGenerated(buffer)) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kObjectNotGenerated); + return false; + } + + const Caps &caps = context->getCaps(); + if (bindingIndex >= static_cast<GLuint>(caps.maxVertexAttribBindings)) + { + context->validationError(entryPoint, GL_INVALID_VALUE, kExceedsMaxVertexAttribBindings); + return false; + } + + if (offset < 0) + { + context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeOffset); + return false; + } + + if (stride < 0 || stride > caps.maxVertexAttribStride) + { + context->validationError(entryPoint, GL_INVALID_VALUE, kExceedsMaxVertexAttribStride); + 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->getState().getVertexArrayId().value == 0) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kDefaultVertexArray); + return false; + } + + return true; +} + +bool ValidateVertexBindingDivisor(const Context *context, + angle::EntryPoint entryPoint, + GLuint bindingIndex, + GLuint divisor) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + const Caps &caps = context->getCaps(); + if (bindingIndex >= static_cast<GLuint>(caps.maxVertexAttribBindings)) + { + context->validationError(entryPoint, GL_INVALID_VALUE, kExceedsMaxVertexAttribBindings); + 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->getState().getVertexArrayId().value == 0) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kDefaultVertexArray); + return false; + } + + return true; +} + +bool ValidateVertexAttribFormat(const Context *context, + angle::EntryPoint entryPoint, + GLuint attribindex, + GLint size, + VertexAttribType type, + GLboolean normalized, + GLuint relativeoffset) +{ + if (!ValidateVertexAttribFormatCommon(context, entryPoint, relativeoffset)) + { + return false; + } + + return ValidateFloatVertexFormat(context, entryPoint, attribindex, size, type); +} + +bool ValidateVertexAttribIFormat(const Context *context, + angle::EntryPoint entryPoint, + GLuint attribindex, + GLint size, + VertexAttribType type, + GLuint relativeoffset) +{ + if (!ValidateVertexAttribFormatCommon(context, entryPoint, relativeoffset)) + { + return false; + } + + return ValidateIntegerVertexFormat(context, entryPoint, attribindex, size, type); +} + +bool ValidateVertexAttribBinding(const Context *context, + angle::EntryPoint entryPoint, + GLuint attribIndex, + GLuint bindingIndex) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + 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->getState().getVertexArrayId().value == 0) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kDefaultVertexArray); + return false; + } + + const Caps &caps = context->getCaps(); + if (attribIndex >= static_cast<GLuint>(caps.maxVertexAttributes)) + { + context->validationError(entryPoint, GL_INVALID_VALUE, kIndexExceedsMaxVertexAttribute); + return false; + } + + if (bindingIndex >= static_cast<GLuint>(caps.maxVertexAttribBindings)) + { + context->validationError(entryPoint, GL_INVALID_VALUE, kExceedsMaxVertexAttribBindings); + return false; + } + + return true; +} + +bool ValidateGetProgramResourceName(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID program, + GLenum programInterface, + GLuint index, + GLsizei bufSize, + const GLsizei *length, + const GLchar *name) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + Program *programObject = GetValidProgram(context, entryPoint, program); + if (programObject == nullptr) + { + return false; + } + + if (!ValidateNamedProgramInterface(programInterface)) + { + context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidProgramInterface); + return false; + } + + if (!ValidateProgramResourceIndex(programObject, programInterface, index)) + { + context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidProgramResourceIndex); + return false; + } + + if (bufSize < 0) + { + context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeBufferSize); + return false; + } + + return true; +} + +bool ValidateDispatchCompute(const Context *context, + angle::EntryPoint entryPoint, + GLuint numGroupsX, + GLuint numGroupsY, + GLuint numGroupsZ) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + const State &state = context->getState(); + const ProgramExecutable *executable = state.getProgramExecutable(); + + if (executable == nullptr || !executable->hasLinkedShaderStage(ShaderType::Compute)) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, + kNoActiveProgramWithComputeShader); + return false; + } + + const Caps &caps = context->getCaps(); + if (numGroupsX > static_cast<GLuint>(caps.maxComputeWorkGroupCount[0])) + { + context->validationError(entryPoint, GL_INVALID_VALUE, kExceedsComputeWorkGroupCountX); + return false; + } + if (numGroupsY > static_cast<GLuint>(caps.maxComputeWorkGroupCount[1])) + { + context->validationError(entryPoint, GL_INVALID_VALUE, kExceedsComputeWorkGroupCountY); + return false; + } + if (numGroupsZ > static_cast<GLuint>(caps.maxComputeWorkGroupCount[2])) + { + context->validationError(entryPoint, GL_INVALID_VALUE, kExceedsComputeWorkGroupCountZ); + return false; + } + + return true; +} + +bool ValidateDispatchComputeIndirect(const Context *context, + angle::EntryPoint entryPoint, + GLintptr indirect) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + const State &state = context->getState(); + const ProgramExecutable *executable = state.getProgramExecutable(); + + if (executable == nullptr || !executable->hasLinkedShaderStage(ShaderType::Compute)) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, + kNoActiveProgramWithComputeShader); + return false; + } + + if (indirect < 0) + { + context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeOffset); + return false; + } + + if ((indirect & (sizeof(GLuint) - 1)) != 0) + { + context->validationError(entryPoint, GL_INVALID_VALUE, kOffsetMustBeMultipleOfUint); + return false; + } + + Buffer *dispatchIndirectBuffer = state.getTargetBuffer(BufferBinding::DispatchIndirect); + if (!dispatchIndirectBuffer) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kDispatchIndirectBufferNotBound); + 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())) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kInsufficientBufferSize); + return false; + } + + return true; +} + +bool ValidateBindImageTexture(const Context *context, + angle::EntryPoint entryPoint, + GLuint unit, + TextureID texture, + GLint level, + GLboolean layered, + GLint layer, + GLenum access, + GLenum format) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + GLuint maxImageUnits = static_cast<GLuint>(context->getCaps().maxImageUnits); + if (unit >= maxImageUnits) + { + context->validationError(entryPoint, GL_INVALID_VALUE, kExceedsMaxImageUnits); + return false; + } + + if (level < 0) + { + context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeLevel); + return false; + } + + if (layer < 0) + { + context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeLayer); + return false; + } + + if (access != GL_READ_ONLY && access != GL_WRITE_ONLY && access != GL_READ_WRITE) + { + context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidImageAccess); + 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->validationError(entryPoint, GL_INVALID_VALUE, kInvalidImageFormat); + return false; + } + + if (texture.value != 0) + { + Texture *tex = context->getTexture(texture); + + if (tex == nullptr) + { + context->validationError(entryPoint, GL_INVALID_VALUE, kMissingTextureName); + return false; + } + + if (!tex->getImmutableFormat() && tex->getType() != gl::TextureType::Buffer) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, + kTextureIsNeitherImmutableNorTextureBuffer); + return false; + } + } + + return true; +} + +bool ValidateGetProgramResourceLocation(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID program, + GLenum programInterface, + const GLchar *name) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + Program *programObject = GetValidProgram(context, entryPoint, program); + if (programObject == nullptr) + { + return false; + } + + if (!programObject->isLinked()) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kProgramNotLinked); + return false; + } + + if (!ValidateLocationProgramInterface(programInterface)) + { + context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidProgramInterface); + return false; + } + return true; +} + +bool ValidateGetProgramResourceiv(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID program, + GLenum programInterface, + GLuint index, + GLsizei propCount, + const GLenum *props, + GLsizei bufSize, + const GLsizei *length, + const GLint *params) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + Program *programObject = GetValidProgram(context, entryPoint, program); + if (programObject == nullptr) + { + return false; + } + if (!ValidateProgramInterface(programInterface)) + { + context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidProgramInterface); + return false; + } + if (propCount <= 0) + { + context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidPropCount); + return false; + } + if (bufSize < 0) + { + context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeBufSize); + return false; + } + if (!ValidateProgramResourceIndex(programObject, programInterface, index)) + { + context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidProgramResourceIndex); + return false; + } + for (GLsizei i = 0; i < propCount; i++) + { + if (!ValidateProgramResourceProperty(context, entryPoint, props[i])) + { + context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidProgramResourceProperty); + return false; + } + if (!ValidateProgramResourcePropertyByInterface(props[i], programInterface)) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, + kInvalidPropertyForProgramInterface); + return false; + } + } + return true; +} + +bool ValidateGetProgramInterfaceiv(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID program, + GLenum programInterface, + GLenum pname, + const GLint *params) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + Program *programObject = GetValidProgram(context, entryPoint, program); + if (programObject == nullptr) + { + return false; + } + + if (!ValidateProgramInterface(programInterface)) + { + context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidProgramInterface); + return false; + } + + switch (pname) + { + case GL_ACTIVE_RESOURCES: + case GL_MAX_NAME_LENGTH: + case GL_MAX_NUM_ACTIVE_VARIABLES: + break; + + default: + context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidPname); + return false; + } + + if (pname == GL_MAX_NAME_LENGTH && programInterface == GL_ATOMIC_COUNTER_BUFFER) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kAtomicCounterResourceName); + 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->validationError(entryPoint, GL_INVALID_OPERATION, + kMaxActiveVariablesInterface); + return false; + } + } + + return true; +} + +bool ValidateGetProgramInterfaceivRobustANGLE(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID program, + GLenum programInterface, + GLenum pname, + GLsizei bufSize, + const GLsizei *length, + const GLint *params) +{ + UNIMPLEMENTED(); + return false; +} + +bool ValidateGenProgramPipelinesBase(const Context *context, + angle::EntryPoint entryPoint, + GLsizei n, + const ProgramPipelineID *pipelines) +{ + return ValidateGenOrDelete(context, entryPoint, n); +} + +bool ValidateDeleteProgramPipelinesBase(const Context *context, + angle::EntryPoint entryPoint, + GLsizei n, + const ProgramPipelineID *pipelines) +{ + return ValidateGenOrDelete(context, entryPoint, n); +} + +bool ValidateBindProgramPipelineBase(const Context *context, + angle::EntryPoint entryPoint, + ProgramPipelineID pipeline) +{ + if (!context->isProgramPipelineGenerated({pipeline})) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kObjectNotGenerated); + return false; + } + + return true; +} + +bool ValidateIsProgramPipelineBase(const Context *context, + angle::EntryPoint entryPoint, + ProgramPipelineID pipeline) +{ + return true; +} + +bool ValidateUseProgramStagesBase(const Context *context, + angle::EntryPoint entryPoint, + ProgramPipelineID pipeline, + GLbitfield stages, + ShaderProgramID programId) +{ + // GL_INVALID_VALUE is generated if shaders contains set bits that are not recognized, and is + // not the reserved value GL_ALL_SHADER_BITS. + GLbitfield knownShaderBits = + GL_VERTEX_SHADER_BIT | GL_FRAGMENT_SHADER_BIT | GL_COMPUTE_SHADER_BIT; + + if (context->getClientVersion() >= ES_3_2 || context->getExtensions().geometryShaderAny()) + { + knownShaderBits |= GL_GEOMETRY_SHADER_BIT; + } + + if (context->getClientVersion() >= ES_3_2 || context->getExtensions().tessellationShaderEXT) + { + knownShaderBits |= GL_TESS_CONTROL_SHADER_BIT; + knownShaderBits |= GL_TESS_EVALUATION_SHADER_BIT; + } + + if ((stages & ~knownShaderBits) && (stages != GL_ALL_SHADER_BITS)) + { + context->validationError(entryPoint, GL_INVALID_VALUE, kUnrecognizedShaderStageBit); + return false; + } + + // GL_INVALID_OPERATION is generated if pipeline is not a name previously returned from a call + // to glGenProgramPipelines or if such a name has been deleted by a call to + // glDeleteProgramPipelines. + if (!context->isProgramPipelineGenerated({pipeline})) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kObjectNotGenerated); + return false; + } + + // If program is zero, or refers to a program object with no valid shader executable for a given + // stage, it is as if the pipeline object has no programmable stage configured for the indicated + // shader stages. + if (programId.value == 0) + { + return true; + } + + Program *program = context->getProgramNoResolveLink(programId); + if (!program) + { + context->validationError(entryPoint, GL_INVALID_VALUE, kProgramDoesNotExist); + return false; + } + + // GL_INVALID_OPERATION is generated if program refers to a program object that was not linked + // with its GL_PROGRAM_SEPARABLE status set. + // resolveLink() may not have been called if glCreateShaderProgramv() was not used and + // glDetachShader() was not called. + program->resolveLink(context); + if (!program->isSeparable()) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kProgramNotSeparable); + return false; + } + + // GL_INVALID_OPERATION is generated if program refers to a program object that has not been + // successfully linked. + if (!program->isLinked()) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kProgramNotLinked); + return false; + } + + return true; +} + +bool ValidateActiveShaderProgramBase(const Context *context, + angle::EntryPoint entryPoint, + ProgramPipelineID pipeline, + ShaderProgramID programId) +{ + // An INVALID_OPERATION error is generated if pipeline is not a name returned from a previous + // call to GenProgramPipelines or if such a name has since been deleted by + // DeleteProgramPipelines. + if (!context->isProgramPipelineGenerated({pipeline})) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kObjectNotGenerated); + return false; + } + + // An INVALID_VALUE error is generated if program is not zero and is not the name of either a + // program or shader object. + if ((programId.value != 0) && !context->isProgram(programId) && !context->isShader(programId)) + { + context->validationError(entryPoint, GL_INVALID_VALUE, kProgramDoesNotExist); + return false; + } + + // An INVALID_OPERATION error is generated if program is the name of a shader object. + if (context->isShader(programId)) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kExpectedProgramName); + return false; + } + + // An INVALID_OPERATION error is generated if program is not zero and has not been linked, or + // was last linked unsuccessfully. The active program is not modified. + Program *program = context->getProgramNoResolveLink(programId); + if ((programId.value != 0) && !program->isLinked()) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kProgramNotLinked); + return false; + } + + return true; +} + +bool ValidateCreateShaderProgramvBase(const Context *context, + angle::EntryPoint entryPoint, + ShaderType type, + GLsizei count, + const GLchar *const *strings) +{ + switch (type) + { + case ShaderType::InvalidEnum: + context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidShaderType); + return false; + case ShaderType::Vertex: + case ShaderType::Fragment: + case ShaderType::Compute: + break; + case ShaderType::Geometry: + if (!context->getExtensions().geometryShaderAny() && + context->getClientVersion() < ES_3_2) + { + context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidShaderType); + return false; + } + break; + case ShaderType::TessControl: + case ShaderType::TessEvaluation: + if (!context->getExtensions().tessellationShaderEXT && + context->getClientVersion() < ES_3_2) + { + context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidShaderType); + return false; + } + break; + default: + UNREACHABLE(); + } + + // GL_INVALID_VALUE is generated if count is negative. + if (count < 0) + { + context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeCount); + return false; + } + + return true; +} + +bool ValidateCreateShaderProgramvBase(const Context *context, + angle::EntryPoint entryPoint, + ShaderType type, + GLsizei count, + const GLchar **strings) +{ + const GLchar *const *tmpStrings = strings; + return ValidateCreateShaderProgramvBase(context, entryPoint, type, count, tmpStrings); +} + +bool ValidateGetProgramPipelineivBase(const Context *context, + angle::EntryPoint entryPoint, + ProgramPipelineID pipeline, + GLenum pname, + const GLint *params) +{ + // An INVALID_OPERATION error is generated if pipeline is not a name returned from a previous + // call to GenProgramPipelines or if such a name has since been deleted by + // DeleteProgramPipelines. + if ((pipeline.value == 0) || (!context->isProgramPipelineGenerated(pipeline))) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kProgramPipelineDoesNotExist); + return false; + } + + // An INVALID_ENUM error is generated if pname is not ACTIVE_PROGRAM, + // INFO_LOG_LENGTH, VALIDATE_STATUS, or one of the type arguments in + // table 7.1. + switch (pname) + { + case GL_ACTIVE_PROGRAM: + case GL_INFO_LOG_LENGTH: + case GL_VALIDATE_STATUS: + case GL_VERTEX_SHADER: + case GL_FRAGMENT_SHADER: + case GL_COMPUTE_SHADER: + break; + case GL_GEOMETRY_SHADER: + return context->getExtensions().geometryShaderAny() || + context->getClientVersion() >= ES_3_2; + case GL_TESS_CONTROL_SHADER: + case GL_TESS_EVALUATION_SHADER: + return context->getExtensions().tessellationShaderEXT || + context->getClientVersion() >= ES_3_2; + + default: + context->validationError(entryPoint, GL_INVALID_ENUM, kInvalidPname); + return false; + } + + return true; +} + +bool ValidateValidateProgramPipelineBase(const Context *context, + angle::EntryPoint entryPoint, + ProgramPipelineID pipeline) +{ + if (pipeline.value == 0) + { + return false; + } + + if (!context->isProgramPipelineGenerated(pipeline)) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kProgramPipelineDoesNotExist); + return false; + } + + return true; +} + +bool ValidateGetProgramPipelineInfoLogBase(const Context *context, + angle::EntryPoint entryPoint, + ProgramPipelineID pipeline, + GLsizei bufSize, + const GLsizei *length, + const GLchar *infoLog) +{ + if (bufSize < 0) + { + context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeBufferSize); + return false; + } + + if (!context->isProgramPipelineGenerated(pipeline)) + { + context->validationError(entryPoint, GL_INVALID_VALUE, kProgramPipelineDoesNotExist); + return false; + } + + return true; +} + +bool ValidateActiveShaderProgram(const Context *context, + angle::EntryPoint entryPoint, + ProgramPipelineID pipelinePacked, + ShaderProgramID programPacked) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateActiveShaderProgramBase(context, entryPoint, pipelinePacked, programPacked); +} + +bool ValidateBindProgramPipeline(const Context *context, + angle::EntryPoint entryPoint, + ProgramPipelineID pipelinePacked) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateBindProgramPipelineBase(context, entryPoint, pipelinePacked); +} + +bool ValidateCreateShaderProgramv(const Context *context, + angle::EntryPoint entryPoint, + ShaderType typePacked, + GLsizei count, + const GLchar *const *strings) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateCreateShaderProgramvBase(context, entryPoint, typePacked, count, strings); +} + +bool ValidateDeleteProgramPipelines(const Context *context, + angle::EntryPoint entryPoint, + GLsizei n, + const ProgramPipelineID *pipelinesPacked) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateDeleteProgramPipelinesBase(context, entryPoint, n, pipelinesPacked); +} + +bool ValidateGenProgramPipelines(const Context *context, + angle::EntryPoint entryPoint, + GLsizei n, + const ProgramPipelineID *pipelinesPacked) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateGenProgramPipelinesBase(context, entryPoint, n, pipelinesPacked); +} + +bool ValidateGetProgramPipelineInfoLog(const Context *context, + angle::EntryPoint entryPoint, + ProgramPipelineID pipelinePacked, + GLsizei bufSize, + const GLsizei *length, + const GLchar *infoLog) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateGetProgramPipelineInfoLogBase(context, entryPoint, pipelinePacked, bufSize, + length, infoLog); +} + +bool ValidateGetProgramPipelineiv(const Context *context, + angle::EntryPoint entryPoint, + ProgramPipelineID pipelinePacked, + GLenum pname, + const GLint *params) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateGetProgramPipelineivBase(context, entryPoint, pipelinePacked, pname, params); +} + +bool ValidateIsProgramPipeline(const Context *context, + angle::EntryPoint entryPoint, + ProgramPipelineID pipelinePacked) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateIsProgramPipelineBase(context, entryPoint, pipelinePacked); +} + +bool ValidateProgramUniform1f(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID programPacked, + UniformLocation locationPacked, + GLfloat v0) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateProgramUniform1fBase(context, entryPoint, programPacked, locationPacked, v0); +} + +bool ValidateProgramUniform1fv(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID programPacked, + UniformLocation locationPacked, + GLsizei count, + const GLfloat *value) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateProgramUniform1fvBase(context, entryPoint, programPacked, locationPacked, count, + value); +} + +bool ValidateProgramUniform1i(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID programPacked, + UniformLocation locationPacked, + GLint v0) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateProgramUniform1iBase(context, entryPoint, programPacked, locationPacked, v0); +} + +bool ValidateProgramUniform1iv(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID programPacked, + UniformLocation locationPacked, + GLsizei count, + const GLint *value) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateProgramUniform1ivBase(context, entryPoint, programPacked, locationPacked, count, + value); +} + +bool ValidateProgramUniform1ui(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID programPacked, + UniformLocation locationPacked, + GLuint v0) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateProgramUniform1uiBase(context, entryPoint, programPacked, locationPacked, v0); +} + +bool ValidateProgramUniform1uiv(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID programPacked, + UniformLocation locationPacked, + GLsizei count, + const GLuint *value) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateProgramUniform1uivBase(context, entryPoint, programPacked, locationPacked, count, + value); +} + +bool ValidateProgramUniform2f(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID programPacked, + UniformLocation locationPacked, + GLfloat v0, + GLfloat v1) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateProgramUniform2fBase(context, entryPoint, programPacked, locationPacked, v0, v1); +} + +bool ValidateProgramUniform2fv(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID programPacked, + UniformLocation locationPacked, + GLsizei count, + const GLfloat *value) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateProgramUniform2fvBase(context, entryPoint, programPacked, locationPacked, count, + value); +} + +bool ValidateProgramUniform2i(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID programPacked, + UniformLocation locationPacked, + GLint v0, + GLint v1) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateProgramUniform2iBase(context, entryPoint, programPacked, locationPacked, v0, v1); +} + +bool ValidateProgramUniform2iv(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID programPacked, + UniformLocation locationPacked, + GLsizei count, + const GLint *value) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateProgramUniform2ivBase(context, entryPoint, programPacked, locationPacked, count, + value); +} + +bool ValidateProgramUniform2ui(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID programPacked, + UniformLocation locationPacked, + GLuint v0, + GLuint v1) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateProgramUniform2uiBase(context, entryPoint, programPacked, locationPacked, v0, + v1); +} + +bool ValidateProgramUniform2uiv(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID programPacked, + UniformLocation locationPacked, + GLsizei count, + const GLuint *value) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateProgramUniform2uivBase(context, entryPoint, programPacked, locationPacked, count, + value); +} + +bool ValidateProgramUniform3f(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID programPacked, + UniformLocation locationPacked, + GLfloat v0, + GLfloat v1, + GLfloat v2) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateProgramUniform3fBase(context, entryPoint, programPacked, locationPacked, v0, v1, + v2); +} + +bool ValidateProgramUniform3fv(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID programPacked, + UniformLocation locationPacked, + GLsizei count, + const GLfloat *value) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateProgramUniform3fvBase(context, entryPoint, programPacked, locationPacked, count, + value); +} + +bool ValidateProgramUniform3i(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID programPacked, + UniformLocation locationPacked, + GLint v0, + GLint v1, + GLint v2) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateProgramUniform3iBase(context, entryPoint, programPacked, locationPacked, v0, v1, + v2); +} + +bool ValidateProgramUniform3iv(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID programPacked, + UniformLocation locationPacked, + GLsizei count, + const GLint *value) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateProgramUniform3ivBase(context, entryPoint, programPacked, locationPacked, count, + value); +} + +bool ValidateProgramUniform3ui(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID programPacked, + UniformLocation locationPacked, + GLuint v0, + GLuint v1, + GLuint v2) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateProgramUniform3uiBase(context, entryPoint, programPacked, locationPacked, v0, v1, + v2); +} + +bool ValidateProgramUniform3uiv(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID programPacked, + UniformLocation locationPacked, + GLsizei count, + const GLuint *value) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateProgramUniform3uivBase(context, entryPoint, programPacked, locationPacked, count, + value); +} + +bool ValidateProgramUniform4f(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID programPacked, + UniformLocation locationPacked, + GLfloat v0, + GLfloat v1, + GLfloat v2, + GLfloat v3) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateProgramUniform4fBase(context, entryPoint, programPacked, locationPacked, v0, v1, + v2, v3); +} + +bool ValidateProgramUniform4fv(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID programPacked, + UniformLocation locationPacked, + GLsizei count, + const GLfloat *value) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateProgramUniform4fvBase(context, entryPoint, programPacked, locationPacked, count, + value); +} + +bool ValidateProgramUniform4i(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID programPacked, + UniformLocation locationPacked, + GLint v0, + GLint v1, + GLint v2, + GLint v3) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateProgramUniform4iBase(context, entryPoint, programPacked, locationPacked, v0, v1, + v2, v3); +} + +bool ValidateProgramUniform4iv(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID programPacked, + UniformLocation locationPacked, + GLsizei count, + const GLint *value) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateProgramUniform4ivBase(context, entryPoint, programPacked, locationPacked, count, + value); +} + +bool ValidateProgramUniform4ui(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID programPacked, + UniformLocation locationPacked, + GLuint v0, + GLuint v1, + GLuint v2, + GLuint v3) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateProgramUniform4uiBase(context, entryPoint, programPacked, locationPacked, v0, v1, + v2, v3); +} + +bool ValidateProgramUniform4uiv(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID programPacked, + UniformLocation locationPacked, + GLsizei count, + const GLuint *value) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateProgramUniform4uivBase(context, entryPoint, programPacked, locationPacked, count, + value); +} + +bool ValidateProgramUniformMatrix2fv(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID programPacked, + UniformLocation locationPacked, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateProgramUniformMatrix2fvBase(context, entryPoint, programPacked, locationPacked, + count, transpose, value); +} + +bool ValidateProgramUniformMatrix2x3fv(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID programPacked, + UniformLocation locationPacked, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateProgramUniformMatrix2x3fvBase(context, entryPoint, programPacked, locationPacked, + count, transpose, value); +} + +bool ValidateProgramUniformMatrix2x4fv(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID programPacked, + UniformLocation locationPacked, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateProgramUniformMatrix2x4fvBase(context, entryPoint, programPacked, locationPacked, + count, transpose, value); +} + +bool ValidateProgramUniformMatrix3fv(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID programPacked, + UniformLocation locationPacked, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateProgramUniformMatrix3fvBase(context, entryPoint, programPacked, locationPacked, + count, transpose, value); +} + +bool ValidateProgramUniformMatrix3x2fv(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID programPacked, + UniformLocation locationPacked, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateProgramUniformMatrix3x2fvBase(context, entryPoint, programPacked, locationPacked, + count, transpose, value); +} + +bool ValidateProgramUniformMatrix3x4fv(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID programPacked, + UniformLocation locationPacked, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateProgramUniformMatrix3x4fvBase(context, entryPoint, programPacked, locationPacked, + count, transpose, value); +} + +bool ValidateProgramUniformMatrix4fv(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID programPacked, + UniformLocation locationPacked, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateProgramUniformMatrix4fvBase(context, entryPoint, programPacked, locationPacked, + count, transpose, value); +} + +bool ValidateProgramUniformMatrix4x2fv(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID programPacked, + UniformLocation locationPacked, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateProgramUniformMatrix4x2fvBase(context, entryPoint, programPacked, locationPacked, + count, transpose, value); +} + +bool ValidateProgramUniformMatrix4x3fv(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID programPacked, + UniformLocation locationPacked, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateProgramUniformMatrix4x3fvBase(context, entryPoint, programPacked, locationPacked, + count, transpose, value); +} + +bool ValidateUseProgramStages(const Context *context, + angle::EntryPoint entryPoint, + ProgramPipelineID pipelinePacked, + GLbitfield stages, + ShaderProgramID programPacked) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateUseProgramStagesBase(context, entryPoint, pipelinePacked, stages, programPacked); +} + +bool ValidateValidateProgramPipeline(const Context *context, + angle::EntryPoint entryPoint, + ProgramPipelineID pipelinePacked) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateValidateProgramPipelineBase(context, entryPoint, pipelinePacked); +} + +bool ValidateMemoryBarrier(const Context *context, + angle::EntryPoint entryPoint, + GLbitfield barriers) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + 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 (context->getExtensions().bufferStorageEXT) + { + supported_barrier_bits |= GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT_EXT; + } + + if (barriers == 0 || (barriers & ~supported_barrier_bits) != 0) + { + context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidMemoryBarrierBit); + return false; + } + + return true; +} + +bool ValidateMemoryBarrierByRegion(const Context *context, + angle::EntryPoint entryPoint, + GLbitfield barriers) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + 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 == 0 || (barriers & ~supported_barrier_bits) != 0) + { + context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidMemoryBarrierBit); + return false; + } + + return true; +} + +bool ValidateSampleMaski(const Context *context, + angle::EntryPoint entryPoint, + GLuint maskNumber, + GLbitfield mask) +{ + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + + return ValidateSampleMaskiBase(context, entryPoint, maskNumber, mask); +} + +bool ValidateMinSampleShadingOES(const Context *context, + angle::EntryPoint entryPoint, + GLfloat value) +{ + if (!context->getExtensions().sampleShadingOES) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled); + return false; + } + + return true; +} + +bool ValidateFramebufferTextureCommon(const Context *context, + angle::EntryPoint entryPoint, + GLenum target, + GLenum attachment, + TextureID texture, + GLint level) +{ + if (texture.value != 0) + { + 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) + { + context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidTextureName); + return false; + } + + if (!ValidMipLevel(context, tex->getType(), level)) + { + context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidMipLevel); + return false; + } + + // GLES spec 3.1, Section 9.2.8 "Attaching Texture Images to a Framebuffer" + // If textarget is TEXTURE_2D_MULTISAMPLE, then level must be zero. + if (tex->getType() == TextureType::_2DMultisample && level != 0) + { + context->validationError(entryPoint, GL_INVALID_VALUE, kLevelNotZero); + return false; + } + + // [OES_texture_storage_multisample_2d_array] Section 9.2.2 "Attaching Images to Framebuffer + // Objects" + // If texture is a two-dimensional multisample array texture, then level must be zero. + if (context->getExtensions().textureStorageMultisample2dArrayOES && + tex->getType() == TextureType::_2DMultisampleArray && level != 0) + { + context->validationError(entryPoint, GL_INVALID_VALUE, kLevelNotZero); + return false; + } + } + + if (!ValidateFramebufferTextureBase(context, entryPoint, target, attachment, texture, level)) + { + return false; + } + + return true; +} + +bool ValidateFramebufferTextureEXT(const Context *context, + angle::EntryPoint entryPoint, + GLenum target, + GLenum attachment, + TextureID texture, + GLint level) +{ + if (!context->getExtensions().geometryShaderEXT) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, + kGeometryShaderExtensionNotEnabled); + return false; + } + + return ValidateFramebufferTextureCommon(context, entryPoint, target, attachment, texture, + level); +} + +bool ValidateFramebufferTextureOES(const Context *context, + angle::EntryPoint entryPoint, + GLenum target, + GLenum attachment, + TextureID texture, + GLint level) +{ + if (!context->getExtensions().geometryShaderOES) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, + kGeometryShaderExtensionNotEnabled); + return false; + } + + return ValidateFramebufferTextureCommon(context, entryPoint, target, attachment, texture, + level); +} + +// GL_OES_texture_storage_multisample_2d_array +bool ValidateTexStorage3DMultisampleOES(const Context *context, + angle::EntryPoint entryPoint, + TextureType target, + GLsizei samples, + GLenum sizedinternalformat, + GLsizei width, + GLsizei height, + GLsizei depth, + GLboolean fixedsamplelocations) +{ + if (!context->getExtensions().textureStorageMultisample2dArrayOES) + { + context->validationError(entryPoint, GL_INVALID_ENUM, kMultisampleArrayExtensionRequired); + return false; + } + + if (target != TextureType::_2DMultisampleArray) + { + context->validationError(entryPoint, GL_INVALID_ENUM, + kTargetMustBeTexture2DMultisampleArrayOES); + return false; + } + + if (width < 1 || height < 1 || depth < 1) + { + context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeSize); + return false; + } + + if (depth > context->getCaps().maxArrayTextureLayers) + { + context->validationError(entryPoint, GL_INVALID_VALUE, kTextureDepthOutOfRange); + return false; + } + + return ValidateTexStorageMultisample(context, entryPoint, target, samples, sizedinternalformat, + width, height); +} + +bool ValidateTexStorageMem3DMultisampleEXT(const Context *context, + angle::EntryPoint entryPoint, + TextureType target, + GLsizei samples, + GLenum internalFormat, + GLsizei width, + GLsizei height, + GLsizei depth, + GLboolean fixedSampleLocations, + MemoryObjectID memory, + GLuint64 offset) +{ + if (!context->getExtensions().memoryObjectEXT) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled); + return false; + } + + UNIMPLEMENTED(); + return false; +} + +bool ValidateGetProgramResourceLocationIndexEXT(const Context *context, + angle::EntryPoint entryPoint, + ShaderProgramID program, + GLenum programInterface, + const char *name) +{ + if (!context->getExtensions().blendFuncExtendedEXT) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kExtensionNotEnabled); + return false; + } + if (context->getClientVersion() < ES_3_1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kES31Required); + return false; + } + if (programInterface != GL_PROGRAM_OUTPUT) + { + context->validationError(entryPoint, GL_INVALID_ENUM, kProgramInterfaceMustBeProgramOutput); + return false; + } + Program *programObject = GetValidProgram(context, entryPoint, program); + if (!programObject) + { + return false; + } + if (!programObject->isLinked()) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kProgramNotLinked); + return false; + } + return true; +} + +// GL_OES_texture_buffer +bool ValidateTexBufferOES(const Context *context, + angle::EntryPoint entryPoint, + TextureType target, + GLenum internalformat, + BufferID bufferPacked) +{ + if (!context->getExtensions().textureBufferOES) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, + kTextureBufferExtensionNotAvailable); + return false; + } + + return ValidateTexBufferBase(context, entryPoint, target, internalformat, bufferPacked); +} + +bool ValidateTexBufferRangeOES(const Context *context, + angle::EntryPoint entryPoint, + TextureType target, + GLenum internalformat, + BufferID bufferPacked, + GLintptr offset, + GLsizeiptr size) +{ + if (!context->getExtensions().textureBufferOES) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, + kTextureBufferExtensionNotAvailable); + return false; + } + + return ValidateTexBufferRangeBase(context, entryPoint, target, internalformat, bufferPacked, + offset, size); +} + +// GL_EXT_texture_buffer +bool ValidateTexBufferEXT(const Context *context, + angle::EntryPoint entryPoint, + TextureType target, + GLenum internalformat, + BufferID bufferPacked) +{ + if (!context->getExtensions().textureBufferEXT) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, + kTextureBufferExtensionNotAvailable); + return false; + } + + return ValidateTexBufferBase(context, entryPoint, target, internalformat, bufferPacked); +} + +bool ValidateTexBufferRangeEXT(const Context *context, + angle::EntryPoint entryPoint, + TextureType target, + GLenum internalformat, + BufferID bufferPacked, + GLintptr offset, + GLsizeiptr size) +{ + if (!context->getExtensions().textureBufferEXT) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, + kTextureBufferExtensionNotAvailable); + return false; + } + + return ValidateTexBufferRangeBase(context, entryPoint, target, internalformat, bufferPacked, + offset, size); +} + +bool ValidateTexBufferBase(const Context *context, + angle::EntryPoint entryPoint, + TextureType target, + GLenum internalformat, + BufferID bufferPacked) +{ + if (target != TextureType::Buffer) + { + context->validationError(entryPoint, GL_INVALID_ENUM, kTextureBufferTarget); + return false; + } + + switch (internalformat) + { + case GL_R8: + case GL_R16F: + case GL_R32F: + case GL_R8I: + case GL_R16I: + case GL_R32I: + case GL_R8UI: + case GL_R16UI: + case GL_R32UI: + case GL_RG8: + case GL_RG16F: + case GL_RG32F: + case GL_RG8I: + case GL_RG16I: + case GL_RG32I: + case GL_RG8UI: + case GL_RG16UI: + case GL_RG32UI: + case GL_RGB32F: + case GL_RGB32I: + case GL_RGB32UI: + case GL_RGBA8: + case GL_RGBA16F: + case GL_RGBA32F: + case GL_RGBA8I: + case GL_RGBA16I: + case GL_RGBA32I: + case GL_RGBA8UI: + case GL_RGBA16UI: + case GL_RGBA32UI: + break; + + default: + context->validationError(entryPoint, GL_INVALID_ENUM, kTextureBufferInternalFormat); + return false; + } + + if (bufferPacked.value != 0) + { + if (!context->isBufferGenerated(bufferPacked)) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kTextureBufferInvalidBuffer); + return false; + } + } + + return true; +} + +bool ValidateTexBufferRangeBase(const Context *context, + angle::EntryPoint entryPoint, + TextureType target, + GLenum internalformat, + BufferID bufferPacked, + GLintptr offset, + GLsizeiptr size) +{ + const Caps &caps = context->getCaps(); + + if (offset < 0 || (offset % caps.textureBufferOffsetAlignment) != 0) + { + context->validationError(entryPoint, GL_INVALID_VALUE, kTextureBufferOffsetAlignment); + return false; + } + if (size <= 0) + { + context->validationError(entryPoint, GL_INVALID_VALUE, kTextureBufferSize); + return false; + } + const Buffer *buffer = context->getBuffer(bufferPacked); + + if (!buffer) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kBufferNotBound); + return false; + } + + if (offset + size > buffer->getSize()) + { + context->validationError(entryPoint, GL_INVALID_VALUE, kTextureBufferSizeOffset); + return false; + } + + return ValidateTexBufferBase(context, entryPoint, target, internalformat, bufferPacked); +} + +} // namespace gl |