// // 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, 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 || 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().blendFuncExtended; 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(programObject->getState().getProgramInputs().size())); case GL_PROGRAM_OUTPUT: return (index < static_cast(programObject->getOutputResourceCount())); case GL_UNIFORM: return (index < static_cast(programObject->getActiveUniformCount())); case GL_BUFFER_VARIABLE: return (index < static_cast(programObject->getActiveBufferVariableCount())); case GL_SHADER_STORAGE_BLOCK: return (index < static_cast(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(programObject->getTransformFeedbackVaryingCount())); default: UNREACHABLE(); return false; } } bool ValidateProgramUniformBase(const Context *context, GLenum valueType, ShaderProgramID program, UniformLocation location, GLsizei count) { const LinkedUniform *uniform = nullptr; Program *programObject = GetValidProgram(context, program); return ValidateUniformCommonBase(context, programObject, location, count, &uniform) && ValidateUniformValue(context, valueType, uniform->type); } bool ValidateProgramUniformMatrixBase(const Context *context, GLenum valueType, ShaderProgramID program, UniformLocation location, GLsizei count, GLboolean transpose) { const LinkedUniform *uniform = nullptr; Program *programObject = GetValidProgram(context, program); return ValidateUniformCommonBase(context, programObject, location, count, &uniform) && ValidateUniformMatrixValue(context, valueType, uniform->type); } bool ValidateVertexAttribFormatCommon(const Context *context, GLuint relativeOffset) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } const Caps &caps = context->getCaps(); if (relativeOffset > static_cast(caps.maxVertexAttribRelativeOffset)) { context->validationError(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(GL_INVALID_OPERATION, kDefaultVertexArray); return false; } return true; } } // anonymous namespace bool ValidateGetBooleani_v(const Context *context, GLenum target, GLuint index, const GLboolean *data) { if (context->getClientVersion() < ES_3_1 && !context->getExtensions().drawBuffersIndexedAny()) { context->validationError(GL_INVALID_OPERATION, kES31OrDrawBuffersIndexedExtensionNotAvailable); return false; } if (!ValidateIndexedStateQuery(context, target, index, nullptr)) { return false; } return true; } bool ValidateGetBooleani_vRobustANGLE(const Context *context, GLenum target, GLuint index, GLsizei bufSize, const GLsizei *length, const GLboolean *data) { if (context->getClientVersion() < ES_3_1 && !context->getExtensions().drawBuffersIndexedAny()) { context->validationError(GL_INVALID_OPERATION, kES31OrDrawBuffersIndexedExtensionNotAvailable); 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(const Context *context, PrimitiveMode mode, const void *indirect) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } // Here the third parameter 1 is only to pass the count validation. if (!ValidateDrawBase(context, 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(GL_INVALID_OPERATION, kDefaultVertexArray); return false; } if (context->getStateCache().hasAnyActiveClientAttrib()) { context->validationError(GL_INVALID_OPERATION, kClientDataInVertexArray); return false; } Buffer *drawIndirectBuffer = state.getTargetBuffer(BufferBinding::DrawIndirect); if (!drawIndirectBuffer) { context->validationError(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(indirect); if ((static_cast(offset) % sizeof(GLuint)) != 0) { context->validationError(GL_INVALID_VALUE, kInvalidIndirectOffset); return false; } return true; } bool ValidateDrawArraysIndirect(const Context *context, 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().geometryShader || context->getClientVersion() >= ES_3_2) { if (!ValidateTransformFeedbackPrimitiveMode( context, curTransformFeedback->getPrimitiveMode(), mode)) { context->validationError(GL_INVALID_OPERATION, kInvalidDrawModeTransformFeedback); return false; } } else { // An INVALID_OPERATION error is generated if transform feedback is active and not // paused. context->validationError(GL_INVALID_OPERATION, kUnsupportedDrawModeForTransformFeedback); return false; } } if (!ValidateDrawIndirectBase(context, mode, indirect)) return false; Buffer *drawIndirectBuffer = state.getTargetBuffer(BufferBinding::DrawIndirect); CheckedNumeric checkedOffset(reinterpret_cast(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(drawIndirectBuffer->getSize())) { context->validationError(GL_INVALID_OPERATION, kParamOverflow); return false; } return true; } bool ValidateDrawElementsIndirect(const Context *context, PrimitiveMode mode, DrawElementsType type, const void *indirect) { if (!ValidateDrawElementsBase(context, mode, type)) { return false; } const State &state = context->getState(); const VertexArray *vao = state.getVertexArray(); Buffer *elementArrayBuffer = vao->getElementArrayBuffer(); if (!elementArrayBuffer) { context->validationError(GL_INVALID_OPERATION, kMustHaveElementArrayBinding); return false; } if (!ValidateDrawIndirectBase(context, mode, indirect)) return false; Buffer *drawIndirectBuffer = state.getTargetBuffer(BufferBinding::DrawIndirect); CheckedNumeric checkedOffset(reinterpret_cast(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(drawIndirectBuffer->getSize())) { context->validationError(GL_INVALID_OPERATION, kParamOverflow); return false; } return true; } bool ValidateProgramUniform1iBase(const Context *context, ShaderProgramID program, UniformLocation location, GLint v0) { return ValidateProgramUniform1ivBase(context, program, location, 1, &v0); } bool ValidateProgramUniform2iBase(const Context *context, ShaderProgramID program, UniformLocation location, GLint v0, GLint v1) { GLint xy[2] = {v0, v1}; return ValidateProgramUniform2ivBase(context, program, location, 1, xy); } bool ValidateProgramUniform3iBase(const Context *context, ShaderProgramID program, UniformLocation location, GLint v0, GLint v1, GLint v2) { GLint xyz[3] = {v0, v1, v2}; return ValidateProgramUniform3ivBase(context, program, location, 1, xyz); } bool ValidateProgramUniform4iBase(const Context *context, ShaderProgramID program, UniformLocation location, GLint v0, GLint v1, GLint v2, GLint v3) { GLint xyzw[4] = {v0, v1, v2, v3}; return ValidateProgramUniform4ivBase(context, program, location, 1, xyzw); } bool ValidateProgramUniform1uiBase(const Context *context, ShaderProgramID program, UniformLocation location, GLuint v0) { return ValidateProgramUniform1uivBase(context, program, location, 1, &v0); } bool ValidateProgramUniform2uiBase(const Context *context, ShaderProgramID program, UniformLocation location, GLuint v0, GLuint v1) { GLuint xy[2] = {v0, v1}; return ValidateProgramUniform2uivBase(context, program, location, 1, xy); } bool ValidateProgramUniform3uiBase(const Context *context, ShaderProgramID program, UniformLocation location, GLuint v0, GLuint v1, GLuint v2) { GLuint xyz[3] = {v0, v1, v2}; return ValidateProgramUniform3uivBase(context, program, location, 1, xyz); } bool ValidateProgramUniform4uiBase(const Context *context, ShaderProgramID program, UniformLocation location, GLuint v0, GLuint v1, GLuint v2, GLuint v3) { GLuint xyzw[4] = {v0, v1, v2, v3}; return ValidateProgramUniform4uivBase(context, program, location, 1, xyzw); } bool ValidateProgramUniform1fBase(const Context *context, ShaderProgramID program, UniformLocation location, GLfloat v0) { return ValidateProgramUniform1fvBase(context, program, location, 1, &v0); } bool ValidateProgramUniform2fBase(const Context *context, ShaderProgramID program, UniformLocation location, GLfloat v0, GLfloat v1) { GLfloat xy[2] = {v0, v1}; return ValidateProgramUniform2fvBase(context, program, location, 1, xy); } bool ValidateProgramUniform3fBase(const Context *context, ShaderProgramID program, UniformLocation location, GLfloat v0, GLfloat v1, GLfloat v2) { GLfloat xyz[3] = {v0, v1, v2}; return ValidateProgramUniform3fvBase(context, program, location, 1, xyz); } bool ValidateProgramUniform4fBase(const Context *context, ShaderProgramID program, UniformLocation location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) { GLfloat xyzw[4] = {v0, v1, v2, v3}; return ValidateProgramUniform4fvBase(context, program, location, 1, xyzw); } bool ValidateProgramUniform1ivBase(const Context *context, ShaderProgramID program, UniformLocation location, GLsizei count, const GLint *value) { const LinkedUniform *uniform = nullptr; Program *programObject = GetValidProgram(context, program); return ValidateUniformCommonBase(context, programObject, location, count, &uniform) && ValidateUniform1ivValue(context, uniform->type, count, value); } bool ValidateProgramUniform2ivBase(const Context *context, ShaderProgramID program, UniformLocation location, GLsizei count, const GLint *value) { return ValidateProgramUniformBase(context, GL_INT_VEC2, program, location, count); } bool ValidateProgramUniform3ivBase(const Context *context, ShaderProgramID program, UniformLocation location, GLsizei count, const GLint *value) { return ValidateProgramUniformBase(context, GL_INT_VEC3, program, location, count); } bool ValidateProgramUniform4ivBase(const Context *context, ShaderProgramID program, UniformLocation location, GLsizei count, const GLint *value) { return ValidateProgramUniformBase(context, GL_INT_VEC4, program, location, count); } bool ValidateProgramUniform1uivBase(const Context *context, ShaderProgramID program, UniformLocation location, GLsizei count, const GLuint *value) { return ValidateProgramUniformBase(context, GL_UNSIGNED_INT, program, location, count); } bool ValidateProgramUniform2uivBase(const Context *context, ShaderProgramID program, UniformLocation location, GLsizei count, const GLuint *value) { return ValidateProgramUniformBase(context, GL_UNSIGNED_INT_VEC2, program, location, count); } bool ValidateProgramUniform3uivBase(const Context *context, ShaderProgramID program, UniformLocation location, GLsizei count, const GLuint *value) { return ValidateProgramUniformBase(context, GL_UNSIGNED_INT_VEC3, program, location, count); } bool ValidateProgramUniform4uivBase(const Context *context, ShaderProgramID program, UniformLocation location, GLsizei count, const GLuint *value) { return ValidateProgramUniformBase(context, GL_UNSIGNED_INT_VEC4, program, location, count); } bool ValidateProgramUniform1fvBase(const Context *context, ShaderProgramID program, UniformLocation location, GLsizei count, const GLfloat *value) { return ValidateProgramUniformBase(context, GL_FLOAT, program, location, count); } bool ValidateProgramUniform2fvBase(const Context *context, ShaderProgramID program, UniformLocation location, GLsizei count, const GLfloat *value) { return ValidateProgramUniformBase(context, GL_FLOAT_VEC2, program, location, count); } bool ValidateProgramUniform3fvBase(const Context *context, ShaderProgramID program, UniformLocation location, GLsizei count, const GLfloat *value) { return ValidateProgramUniformBase(context, GL_FLOAT_VEC3, program, location, count); } bool ValidateProgramUniform4fvBase(const Context *context, ShaderProgramID program, UniformLocation location, GLsizei count, const GLfloat *value) { return ValidateProgramUniformBase(context, GL_FLOAT_VEC4, program, location, count); } bool ValidateProgramUniformMatrix2fvBase(const Context *context, ShaderProgramID program, UniformLocation location, GLsizei count, GLboolean transpose, const GLfloat *value) { return ValidateProgramUniformMatrixBase(context, GL_FLOAT_MAT2, program, location, count, transpose); } bool ValidateProgramUniformMatrix3fvBase(const Context *context, ShaderProgramID program, UniformLocation location, GLsizei count, GLboolean transpose, const GLfloat *value) { return ValidateProgramUniformMatrixBase(context, GL_FLOAT_MAT3, program, location, count, transpose); } bool ValidateProgramUniformMatrix4fvBase(const Context *context, ShaderProgramID program, UniformLocation location, GLsizei count, GLboolean transpose, const GLfloat *value) { return ValidateProgramUniformMatrixBase(context, GL_FLOAT_MAT4, program, location, count, transpose); } bool ValidateProgramUniformMatrix2x3fvBase(const Context *context, ShaderProgramID program, UniformLocation location, GLsizei count, GLboolean transpose, const GLfloat *value) { return ValidateProgramUniformMatrixBase(context, GL_FLOAT_MAT2x3, program, location, count, transpose); } bool ValidateProgramUniformMatrix3x2fvBase(const Context *context, ShaderProgramID program, UniformLocation location, GLsizei count, GLboolean transpose, const GLfloat *value) { return ValidateProgramUniformMatrixBase(context, GL_FLOAT_MAT3x2, program, location, count, transpose); } bool ValidateProgramUniformMatrix2x4fvBase(const Context *context, ShaderProgramID program, UniformLocation location, GLsizei count, GLboolean transpose, const GLfloat *value) { return ValidateProgramUniformMatrixBase(context, GL_FLOAT_MAT2x4, program, location, count, transpose); } bool ValidateProgramUniformMatrix4x2fvBase(const Context *context, ShaderProgramID program, UniformLocation location, GLsizei count, GLboolean transpose, const GLfloat *value) { return ValidateProgramUniformMatrixBase(context, GL_FLOAT_MAT4x2, program, location, count, transpose); } bool ValidateProgramUniformMatrix3x4fvBase(const Context *context, ShaderProgramID program, UniformLocation location, GLsizei count, GLboolean transpose, const GLfloat *value) { return ValidateProgramUniformMatrixBase(context, GL_FLOAT_MAT3x4, program, location, count, transpose); } bool ValidateProgramUniformMatrix4x3fvBase(const Context *context, ShaderProgramID program, UniformLocation location, GLsizei count, GLboolean transpose, const GLfloat *value) { return ValidateProgramUniformMatrixBase(context, GL_FLOAT_MAT4x3, program, location, count, transpose); } bool ValidateGetTexLevelParameterfv(const Context *context, TextureTarget target, GLint level, GLenum pname, const GLfloat *params) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateGetTexLevelParameterBase(context, target, level, pname, nullptr); } bool ValidateGetTexLevelParameterfvRobustANGLE(const Context *context, TextureTarget target, GLint level, GLenum pname, GLsizei bufSize, const GLsizei *length, const GLfloat *params) { UNIMPLEMENTED(); return false; } bool ValidateGetTexLevelParameteriv(const Context *context, TextureTarget target, GLint level, GLenum pname, const GLint *params) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateGetTexLevelParameterBase(context, target, level, pname, nullptr); } bool ValidateGetTexLevelParameterivRobustANGLE(const Context *context, TextureTarget target, GLint level, GLenum pname, GLsizei bufSize, const GLsizei *length, const GLint *params) { UNIMPLEMENTED(); return false; } bool ValidateTexStorage2DMultisample(const Context *context, TextureType target, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateTexStorage2DMultisampleBase(context, target, samples, internalFormat, width, height); } bool ValidateTexStorageMem2DMultisampleEXT(const Context *context, TextureType target, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations, MemoryObjectID memory, GLuint64 offset) { if (!context->getExtensions().memoryObject) { context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled); return false; } UNIMPLEMENTED(); return false; } bool ValidateGetMultisamplefv(const Context *context, GLenum pname, GLuint index, const GLfloat *val) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateGetMultisamplefvBase(context, pname, index, val); } bool ValidateGetMultisamplefvRobustANGLE(const Context *context, GLenum pname, GLuint index, GLsizei bufSize, const GLsizei *length, const GLfloat *val) { UNIMPLEMENTED(); return false; } bool ValidateFramebufferParameteri(const Context *context, GLenum target, GLenum pname, GLint param) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } if (!ValidFramebufferTarget(context, target)) { context->validationError(GL_INVALID_ENUM, kInvalidFramebufferTarget); return false; } switch (pname) { case GL_FRAMEBUFFER_DEFAULT_WIDTH: { GLint maxWidth = context->getCaps().maxFramebufferWidth; if (param < 0 || param > maxWidth) { context->validationError(GL_INVALID_VALUE, kExceedsFramebufferWidth); return false; } break; } case GL_FRAMEBUFFER_DEFAULT_HEIGHT: { GLint maxHeight = context->getCaps().maxFramebufferHeight; if (param < 0 || param > maxHeight) { context->validationError(GL_INVALID_VALUE, kExceedsFramebufferHeight); return false; } break; } case GL_FRAMEBUFFER_DEFAULT_SAMPLES: { GLint maxSamples = context->getCaps().maxFramebufferSamples; if (param < 0 || param > maxSamples) { context->validationError(GL_INVALID_VALUE, kExceedsFramebufferSamples); return false; } break; } case GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS: { break; } case GL_FRAMEBUFFER_DEFAULT_LAYERS_EXT: { if (!context->getExtensions().geometryShader && context->getClientVersion() < ES_3_2) { context->validationError(GL_INVALID_ENUM, kGeometryShaderExtensionNotEnabled); return false; } GLint maxLayers = context->getCaps().maxFramebufferLayers; if (param < 0 || param > maxLayers) { context->validationError(GL_INVALID_VALUE, kInvalidFramebufferLayer); return false; } break; } default: { context->validationError(GL_INVALID_ENUM, kInvalidPname); return false; } } const Framebuffer *framebuffer = context->getState().getTargetFramebuffer(target); ASSERT(framebuffer); if (framebuffer->isDefault()) { context->validationError(GL_INVALID_OPERATION, kDefaultFramebuffer); return false; } return true; } bool ValidateGetFramebufferParameteriv(const Context *context, GLenum target, GLenum pname, const GLint *params) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } if (!ValidFramebufferTarget(context, target)) { context->validationError(GL_INVALID_ENUM, kInvalidFramebufferTarget); 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 && context->getClientVersion() < ES_3_2) { context->validationError(GL_INVALID_ENUM, kGeometryShaderExtensionNotEnabled); return false; } break; default: context->validationError(GL_INVALID_ENUM, kInvalidPname); return false; } const Framebuffer *framebuffer = context->getState().getTargetFramebuffer(target); ASSERT(framebuffer); if (framebuffer->isDefault()) { context->validationError(GL_INVALID_OPERATION, kDefaultFramebuffer); return false; } return true; } bool ValidateGetFramebufferParameterivRobustANGLE(const Context *context, GLenum target, GLenum pname, GLsizei bufSize, const GLsizei *length, const GLint *params) { UNIMPLEMENTED(); return false; } bool ValidateGetProgramResourceIndex(const Context *context, ShaderProgramID program, GLenum programInterface, const GLchar *name) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } Program *programObject = GetValidProgram(context, program); if (programObject == nullptr) { return false; } if (!ValidateNamedProgramInterface(programInterface)) { context->validationError(GL_INVALID_ENUM, kInvalidProgramInterface); return false; } return true; } bool ValidateBindVertexBuffer(const Context *context, GLuint bindingIndex, BufferID buffer, GLintptr offset, GLsizei stride) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } if (!context->isBufferGenerated(buffer)) { context->validationError(GL_INVALID_OPERATION, kObjectNotGenerated); return false; } const Caps &caps = context->getCaps(); if (bindingIndex >= static_cast(caps.maxVertexAttribBindings)) { context->validationError(GL_INVALID_VALUE, kExceedsMaxVertexAttribBindings); return false; } if (offset < 0) { context->validationError(GL_INVALID_VALUE, kNegativeOffset); return false; } if (stride < 0 || stride > caps.maxVertexAttribStride) { context->validationError(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(GL_INVALID_OPERATION, kDefaultVertexArray); return false; } return true; } bool ValidateVertexBindingDivisor(const Context *context, GLuint bindingIndex, GLuint divisor) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } const Caps &caps = context->getCaps(); if (bindingIndex >= static_cast(caps.maxVertexAttribBindings)) { context->validationError(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(GL_INVALID_OPERATION, kDefaultVertexArray); return false; } return true; } bool ValidateVertexAttribFormat(const Context *context, GLuint attribindex, GLint size, VertexAttribType type, GLboolean normalized, GLuint relativeoffset) { if (!ValidateVertexAttribFormatCommon(context, relativeoffset)) { return false; } return ValidateFloatVertexFormat(context, attribindex, size, type); } bool ValidateVertexAttribIFormat(const Context *context, GLuint attribindex, GLint size, VertexAttribType type, GLuint relativeoffset) { if (!ValidateVertexAttribFormatCommon(context, relativeoffset)) { return false; } return ValidateIntegerVertexFormat(context, attribindex, size, type); } bool ValidateVertexAttribBinding(const Context *context, GLuint attribIndex, GLuint bindingIndex) { if (context->getClientVersion() < ES_3_1) { context->validationError(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(GL_INVALID_OPERATION, kDefaultVertexArray); return false; } const Caps &caps = context->getCaps(); if (attribIndex >= static_cast(caps.maxVertexAttributes)) { context->validationError(GL_INVALID_VALUE, kIndexExceedsMaxVertexAttribute); return false; } if (bindingIndex >= static_cast(caps.maxVertexAttribBindings)) { context->validationError(GL_INVALID_VALUE, kExceedsMaxVertexAttribBindings); return false; } return true; } bool ValidateGetProgramResourceName(const Context *context, ShaderProgramID program, GLenum programInterface, GLuint index, GLsizei bufSize, const GLsizei *length, const GLchar *name) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } Program *programObject = GetValidProgram(context, program); if (programObject == nullptr) { return false; } if (!ValidateNamedProgramInterface(programInterface)) { context->validationError(GL_INVALID_ENUM, kInvalidProgramInterface); return false; } if (!ValidateProgramResourceIndex(programObject, programInterface, index)) { context->validationError(GL_INVALID_VALUE, kInvalidProgramResourceIndex); return false; } if (bufSize < 0) { context->validationError(GL_INVALID_VALUE, kNegativeBufferSize); return false; } return true; } bool ValidateDispatchCompute(const Context *context, GLuint numGroupsX, GLuint numGroupsY, GLuint numGroupsZ) { if (context->getClientVersion() < ES_3_1) { context->validationError(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(GL_INVALID_OPERATION, kNoActiveProgramWithComputeShader); return false; } const Caps &caps = context->getCaps(); if (numGroupsX > static_cast(caps.maxComputeWorkGroupCount[0])) { context->validationError(GL_INVALID_VALUE, kExceedsComputeWorkGroupCountX); return false; } if (numGroupsY > static_cast(caps.maxComputeWorkGroupCount[1])) { context->validationError(GL_INVALID_VALUE, kExceedsComputeWorkGroupCountY); return false; } if (numGroupsZ > static_cast(caps.maxComputeWorkGroupCount[2])) { context->validationError(GL_INVALID_VALUE, kExceedsComputeWorkGroupCountZ); return false; } return true; } bool ValidateDispatchComputeIndirect(const Context *context, GLintptr indirect) { if (context->getClientVersion() < ES_3_1) { context->validationError(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(GL_INVALID_OPERATION, kNoActiveProgramWithComputeShader); return false; } if (indirect < 0) { context->validationError(GL_INVALID_VALUE, kNegativeOffset); return false; } if ((indirect & (sizeof(GLuint) - 1)) != 0) { context->validationError(GL_INVALID_VALUE, kOffsetMustBeMultipleOfUint); return false; } Buffer *dispatchIndirectBuffer = state.getTargetBuffer(BufferBinding::DispatchIndirect); if (!dispatchIndirectBuffer) { context->validationError(GL_INVALID_OPERATION, kDispatchIndirectBufferNotBound); return false; } CheckedNumeric checkedOffset(static_cast(indirect)); auto checkedSum = checkedOffset + static_cast(3 * sizeof(GLuint)); if (!checkedSum.IsValid() || checkedSum.ValueOrDie() > static_cast(dispatchIndirectBuffer->getSize())) { context->validationError(GL_INVALID_OPERATION, kInsufficientBufferSize); return false; } return true; } bool ValidateBindImageTexture(const Context *context, GLuint unit, TextureID texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } GLuint maxImageUnits = static_cast(context->getCaps().maxImageUnits); if (unit >= maxImageUnits) { context->validationError(GL_INVALID_VALUE, kExceedsMaxImageUnits); return false; } if (level < 0) { context->validationError(GL_INVALID_VALUE, kNegativeLevel); return false; } if (layer < 0) { context->validationError(GL_INVALID_VALUE, kNegativeLayer); return false; } if (access != GL_READ_ONLY && access != GL_WRITE_ONLY && access != GL_READ_WRITE) { context->validationError(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(GL_INVALID_VALUE, kInvalidImageFormat); return false; } if (texture.value != 0) { Texture *tex = context->getTexture(texture); if (tex == nullptr) { context->validationError(GL_INVALID_VALUE, kMissingTextureName); return false; } if (!tex->getImmutableFormat()) { context->validationError(GL_INVALID_OPERATION, kTextureIsNotImmutable); return false; } } return true; } bool ValidateGetProgramResourceLocation(const Context *context, ShaderProgramID program, GLenum programInterface, const GLchar *name) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } Program *programObject = GetValidProgram(context, program); if (programObject == nullptr) { return false; } if (!programObject->isLinked()) { context->validationError(GL_INVALID_OPERATION, kProgramNotLinked); return false; } if (!ValidateLocationProgramInterface(programInterface)) { context->validationError(GL_INVALID_ENUM, kInvalidProgramInterface); return false; } return true; } bool ValidateGetProgramResourceiv(const Context *context, 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(GL_INVALID_OPERATION, kES31Required); return false; } Program *programObject = GetValidProgram(context, program); if (programObject == nullptr) { return false; } if (!ValidateProgramInterface(programInterface)) { context->validationError(GL_INVALID_ENUM, kInvalidProgramInterface); return false; } if (propCount <= 0) { context->validationError(GL_INVALID_VALUE, kInvalidPropCount); return false; } if (bufSize < 0) { context->validationError(GL_INVALID_VALUE, kNegativeBufSize); return false; } if (!ValidateProgramResourceIndex(programObject, programInterface, index)) { context->validationError(GL_INVALID_VALUE, kInvalidProgramResourceIndex); return false; } for (GLsizei i = 0; i < propCount; i++) { if (!ValidateProgramResourceProperty(context, props[i])) { context->validationError(GL_INVALID_ENUM, kInvalidProgramResourceProperty); return false; } if (!ValidateProgramResourcePropertyByInterface(props[i], programInterface)) { context->validationError(GL_INVALID_OPERATION, kInvalidPropertyForProgramInterface); return false; } } return true; } bool ValidateGetProgramInterfaceiv(const Context *context, ShaderProgramID program, GLenum programInterface, GLenum pname, const GLint *params) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } Program *programObject = GetValidProgram(context, program); if (programObject == nullptr) { return false; } if (!ValidateProgramInterface(programInterface)) { context->validationError(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(GL_INVALID_ENUM, kInvalidPname); return false; } if (pname == GL_MAX_NAME_LENGTH && programInterface == GL_ATOMIC_COUNTER_BUFFER) { context->validationError(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(GL_INVALID_OPERATION, kMaxActiveVariablesInterface); return false; } } return true; } bool ValidateGetProgramInterfaceivRobustANGLE(const Context *context, ShaderProgramID program, GLenum programInterface, GLenum pname, GLsizei bufSize, const GLsizei *length, const GLint *params) { UNIMPLEMENTED(); return false; } bool ValidateGenProgramPipelinesBase(const Context *context, GLsizei n, const ProgramPipelineID *pipelines) { return ValidateGenOrDelete(context, n); } bool ValidateDeleteProgramPipelinesBase(const Context *context, GLsizei n, const ProgramPipelineID *pipelines) { return ValidateGenOrDelete(context, n); } bool ValidateBindProgramPipelineBase(const Context *context, ProgramPipelineID pipeline) { if (!context->isProgramPipelineGenerated({pipeline})) { context->validationError(GL_INVALID_OPERATION, kObjectNotGenerated); return false; } return true; } bool ValidateIsProgramPipelineBase(const Context *context, ProgramPipelineID pipeline) { return true; } bool ValidateUseProgramStagesBase(const Context *context, 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().geometryShader) { 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(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(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(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(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(GL_INVALID_OPERATION, kProgramNotLinked); return false; } return true; } bool ValidateActiveShaderProgramBase(const Context *context, 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(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(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(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(GL_INVALID_OPERATION, kProgramNotLinked); return false; } return true; } bool ValidateCreateShaderProgramvBase(const Context *context, ShaderType type, GLsizei count, const GLchar *const *strings) { switch (type) { case ShaderType::InvalidEnum: context->validationError(GL_INVALID_ENUM, kInvalidShaderType); return false; case ShaderType::Vertex: case ShaderType::Fragment: case ShaderType::Compute: break; case ShaderType::Geometry: if (!context->getExtensions().geometryShader && context->getClientVersion() < ES_3_2) { context->validationError(GL_INVALID_ENUM, kInvalidShaderType); return false; } break; case ShaderType::TessControl: case ShaderType::TessEvaluation: if (!context->getExtensions().tessellationShaderEXT && context->getClientVersion() < ES_3_2) { context->validationError(GL_INVALID_ENUM, kInvalidShaderType); return false; } break; default: UNREACHABLE(); } // GL_INVALID_VALUE is generated if count is negative. if (count < 0) { context->validationError(GL_INVALID_VALUE, kNegativeCount); return false; } return true; } bool ValidateCreateShaderProgramvBase(const Context *context, ShaderType type, GLsizei count, const GLchar **strings) { const GLchar *const *tmpStrings = strings; return ValidateCreateShaderProgramvBase(context, type, count, tmpStrings); } bool ValidateGetProgramPipelineivBase(const Context *context, 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(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; default: context->validationError(GL_INVALID_ENUM, kInvalidPname); return false; } return true; } bool ValidateValidateProgramPipelineBase(const Context *context, ProgramPipelineID pipeline) { if (pipeline.value == 0) { return false; } if (!context->isProgramPipelineGenerated(pipeline)) { context->validationError(GL_INVALID_OPERATION, kProgramPipelineDoesNotExist); return false; } return true; } bool ValidateGetProgramPipelineInfoLogBase(const Context *context, ProgramPipelineID pipeline, GLsizei bufSize, const GLsizei *length, const GLchar *infoLog) { if (bufSize < 0) { context->validationError(GL_INVALID_VALUE, kNegativeBufferSize); return false; } if (!context->isProgramPipelineGenerated(pipeline)) { context->validationError(GL_INVALID_VALUE, kProgramPipelineDoesNotExist); return false; } return true; } bool ValidateActiveShaderProgram(const Context *context, ProgramPipelineID pipelinePacked, ShaderProgramID programPacked) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateActiveShaderProgramBase(context, pipelinePacked, programPacked); } bool ValidateBindProgramPipeline(const Context *context, ProgramPipelineID pipelinePacked) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateBindProgramPipelineBase(context, pipelinePacked); } bool ValidateCreateShaderProgramv(const Context *context, ShaderType typePacked, GLsizei count, const GLchar *const *strings) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateCreateShaderProgramvBase(context, typePacked, count, strings); } bool ValidateDeleteProgramPipelines(const Context *context, GLsizei n, const ProgramPipelineID *pipelinesPacked) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateDeleteProgramPipelinesBase(context, n, pipelinesPacked); } bool ValidateGenProgramPipelines(const Context *context, GLsizei n, const ProgramPipelineID *pipelinesPacked) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateGenProgramPipelinesBase(context, n, pipelinesPacked); } bool ValidateGetProgramPipelineInfoLog(const Context *context, ProgramPipelineID pipelinePacked, GLsizei bufSize, const GLsizei *length, const GLchar *infoLog) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateGetProgramPipelineInfoLogBase(context, pipelinePacked, bufSize, length, infoLog); } bool ValidateGetProgramPipelineiv(const Context *context, ProgramPipelineID pipelinePacked, GLenum pname, const GLint *params) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateGetProgramPipelineivBase(context, pipelinePacked, pname, params); } bool ValidateIsProgramPipeline(const Context *context, ProgramPipelineID pipelinePacked) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateIsProgramPipelineBase(context, pipelinePacked); } bool ValidateProgramUniform1f(const Context *context, ShaderProgramID programPacked, UniformLocation locationPacked, GLfloat v0) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateProgramUniform1fBase(context, programPacked, locationPacked, v0); } bool ValidateProgramUniform1fv(const Context *context, ShaderProgramID programPacked, UniformLocation locationPacked, GLsizei count, const GLfloat *value) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateProgramUniform1fvBase(context, programPacked, locationPacked, count, value); } bool ValidateProgramUniform1i(const Context *context, ShaderProgramID programPacked, UniformLocation locationPacked, GLint v0) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateProgramUniform1iBase(context, programPacked, locationPacked, v0); } bool ValidateProgramUniform1iv(const Context *context, ShaderProgramID programPacked, UniformLocation locationPacked, GLsizei count, const GLint *value) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateProgramUniform1ivBase(context, programPacked, locationPacked, count, value); } bool ValidateProgramUniform1ui(const Context *context, ShaderProgramID programPacked, UniformLocation locationPacked, GLuint v0) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateProgramUniform1uiBase(context, programPacked, locationPacked, v0); } bool ValidateProgramUniform1uiv(const Context *context, ShaderProgramID programPacked, UniformLocation locationPacked, GLsizei count, const GLuint *value) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateProgramUniform1uivBase(context, programPacked, locationPacked, count, value); } bool ValidateProgramUniform2f(const Context *context, ShaderProgramID programPacked, UniformLocation locationPacked, GLfloat v0, GLfloat v1) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateProgramUniform2fBase(context, programPacked, locationPacked, v0, v1); } bool ValidateProgramUniform2fv(const Context *context, ShaderProgramID programPacked, UniformLocation locationPacked, GLsizei count, const GLfloat *value) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateProgramUniform2fvBase(context, programPacked, locationPacked, count, value); } bool ValidateProgramUniform2i(const Context *context, ShaderProgramID programPacked, UniformLocation locationPacked, GLint v0, GLint v1) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateProgramUniform2iBase(context, programPacked, locationPacked, v0, v1); } bool ValidateProgramUniform2iv(const Context *context, ShaderProgramID programPacked, UniformLocation locationPacked, GLsizei count, const GLint *value) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateProgramUniform2ivBase(context, programPacked, locationPacked, count, value); } bool ValidateProgramUniform2ui(const Context *context, ShaderProgramID programPacked, UniformLocation locationPacked, GLuint v0, GLuint v1) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateProgramUniform2uiBase(context, programPacked, locationPacked, v0, v1); } bool ValidateProgramUniform2uiv(const Context *context, ShaderProgramID programPacked, UniformLocation locationPacked, GLsizei count, const GLuint *value) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateProgramUniform2uivBase(context, programPacked, locationPacked, count, value); } bool ValidateProgramUniform3f(const Context *context, ShaderProgramID programPacked, UniformLocation locationPacked, GLfloat v0, GLfloat v1, GLfloat v2) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateProgramUniform3fBase(context, programPacked, locationPacked, v0, v1, v2); } bool ValidateProgramUniform3fv(const Context *context, ShaderProgramID programPacked, UniformLocation locationPacked, GLsizei count, const GLfloat *value) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateProgramUniform3fvBase(context, programPacked, locationPacked, count, value); } bool ValidateProgramUniform3i(const Context *context, ShaderProgramID programPacked, UniformLocation locationPacked, GLint v0, GLint v1, GLint v2) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateProgramUniform3iBase(context, programPacked, locationPacked, v0, v1, v2); } bool ValidateProgramUniform3iv(const Context *context, ShaderProgramID programPacked, UniformLocation locationPacked, GLsizei count, const GLint *value) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateProgramUniform3ivBase(context, programPacked, locationPacked, count, value); } bool ValidateProgramUniform3ui(const Context *context, ShaderProgramID programPacked, UniformLocation locationPacked, GLuint v0, GLuint v1, GLuint v2) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateProgramUniform3uiBase(context, programPacked, locationPacked, v0, v1, v2); } bool ValidateProgramUniform3uiv(const Context *context, ShaderProgramID programPacked, UniformLocation locationPacked, GLsizei count, const GLuint *value) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateProgramUniform3uivBase(context, programPacked, locationPacked, count, value); } bool ValidateProgramUniform4f(const Context *context, ShaderProgramID programPacked, UniformLocation locationPacked, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateProgramUniform4fBase(context, programPacked, locationPacked, v0, v1, v2, v3); } bool ValidateProgramUniform4fv(const Context *context, ShaderProgramID programPacked, UniformLocation locationPacked, GLsizei count, const GLfloat *value) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateProgramUniform4fvBase(context, programPacked, locationPacked, count, value); } bool ValidateProgramUniform4i(const Context *context, ShaderProgramID programPacked, UniformLocation locationPacked, GLint v0, GLint v1, GLint v2, GLint v3) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateProgramUniform4iBase(context, programPacked, locationPacked, v0, v1, v2, v3); } bool ValidateProgramUniform4iv(const Context *context, ShaderProgramID programPacked, UniformLocation locationPacked, GLsizei count, const GLint *value) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateProgramUniform4ivBase(context, programPacked, locationPacked, count, value); } bool ValidateProgramUniform4ui(const Context *context, ShaderProgramID programPacked, UniformLocation locationPacked, GLuint v0, GLuint v1, GLuint v2, GLuint v3) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateProgramUniform4uiBase(context, programPacked, locationPacked, v0, v1, v2, v3); } bool ValidateProgramUniform4uiv(const Context *context, ShaderProgramID programPacked, UniformLocation locationPacked, GLsizei count, const GLuint *value) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateProgramUniform4uivBase(context, programPacked, locationPacked, count, value); } bool ValidateProgramUniformMatrix2fv(const Context *context, ShaderProgramID programPacked, UniformLocation locationPacked, GLsizei count, GLboolean transpose, const GLfloat *value) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateProgramUniformMatrix2fvBase(context, programPacked, locationPacked, count, transpose, value); } bool ValidateProgramUniformMatrix2x3fv(const Context *context, ShaderProgramID programPacked, UniformLocation locationPacked, GLsizei count, GLboolean transpose, const GLfloat *value) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateProgramUniformMatrix2x3fvBase(context, programPacked, locationPacked, count, transpose, value); } bool ValidateProgramUniformMatrix2x4fv(const Context *context, ShaderProgramID programPacked, UniformLocation locationPacked, GLsizei count, GLboolean transpose, const GLfloat *value) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateProgramUniformMatrix2x4fvBase(context, programPacked, locationPacked, count, transpose, value); } bool ValidateProgramUniformMatrix3fv(const Context *context, ShaderProgramID programPacked, UniformLocation locationPacked, GLsizei count, GLboolean transpose, const GLfloat *value) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateProgramUniformMatrix3fvBase(context, programPacked, locationPacked, count, transpose, value); } bool ValidateProgramUniformMatrix3x2fv(const Context *context, ShaderProgramID programPacked, UniformLocation locationPacked, GLsizei count, GLboolean transpose, const GLfloat *value) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateProgramUniformMatrix3x2fvBase(context, programPacked, locationPacked, count, transpose, value); } bool ValidateProgramUniformMatrix3x4fv(const Context *context, ShaderProgramID programPacked, UniformLocation locationPacked, GLsizei count, GLboolean transpose, const GLfloat *value) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateProgramUniformMatrix3x4fvBase(context, programPacked, locationPacked, count, transpose, value); } bool ValidateProgramUniformMatrix4fv(const Context *context, ShaderProgramID programPacked, UniformLocation locationPacked, GLsizei count, GLboolean transpose, const GLfloat *value) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateProgramUniformMatrix4fvBase(context, programPacked, locationPacked, count, transpose, value); } bool ValidateProgramUniformMatrix4x2fv(const Context *context, ShaderProgramID programPacked, UniformLocation locationPacked, GLsizei count, GLboolean transpose, const GLfloat *value) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateProgramUniformMatrix4x2fvBase(context, programPacked, locationPacked, count, transpose, value); } bool ValidateProgramUniformMatrix4x3fv(const Context *context, ShaderProgramID programPacked, UniformLocation locationPacked, GLsizei count, GLboolean transpose, const GLfloat *value) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateProgramUniformMatrix4x3fvBase(context, programPacked, locationPacked, count, transpose, value); } bool ValidateUseProgramStages(const Context *context, ProgramPipelineID pipelinePacked, GLbitfield stages, ShaderProgramID programPacked) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateUseProgramStagesBase(context, pipelinePacked, stages, programPacked); } bool ValidateValidateProgramPipeline(const Context *context, ProgramPipelineID pipelinePacked) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateValidateProgramPipelineBase(context, pipelinePacked); } bool ValidateMemoryBarrier(const Context *context, GLbitfield barriers) { if (context->getClientVersion() < ES_3_1) { context->validationError(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(GL_INVALID_VALUE, kInvalidMemoryBarrierBit); return false; } return true; } bool ValidateMemoryBarrierByRegion(const Context *context, GLbitfield barriers) { if (context->getClientVersion() < ES_3_1) { context->validationError(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(GL_INVALID_VALUE, kInvalidMemoryBarrierBit); return false; } return true; } bool ValidateSampleMaski(const Context *context, GLuint maskNumber, GLbitfield mask) { if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } return ValidateSampleMaskiBase(context, maskNumber, mask); } bool ValidateMinSampleShadingOES(const Context *context, GLfloat value) { if (!context->getExtensions().sampleShadingOES) { context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled); return false; } return true; } bool ValidateFramebufferTextureEXT(const Context *context, GLenum target, GLenum attachment, TextureID texture, GLint level) { if (!context->getExtensions().geometryShader) { context->validationError(GL_INVALID_OPERATION, kGeometryShaderExtensionNotEnabled); return false; } 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 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(GL_INVALID_VALUE, kInvalidTextureName); return false; } if (!ValidMipLevel(context, tex->getType(), level)) { context->validationError(GL_INVALID_VALUE, kInvalidMipLevel); return false; } } if (!ValidateFramebufferTextureBase(context, target, attachment, texture, level)) { return false; } return true; } // GL_OES_texture_storage_multisample_2d_array bool ValidateTexStorage3DMultisampleOES(const Context *context, TextureType target, GLsizei samples, GLenum sizedinternalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations) { if (!context->getExtensions().textureStorageMultisample2DArrayOES) { context->validationError(GL_INVALID_ENUM, kMultisampleArrayExtensionRequired); return false; } if (target != TextureType::_2DMultisampleArray) { context->validationError(GL_INVALID_ENUM, kTargetMustBeTexture2DMultisampleArrayOES); return false; } if (width < 1 || height < 1 || depth < 1) { context->validationError(GL_INVALID_VALUE, kNegativeSize); return false; } if (depth > context->getCaps().maxArrayTextureLayers) { context->validationError(GL_INVALID_VALUE, kTextureDepthOutOfRange); return false; } return ValidateTexStorageMultisample(context, target, samples, sizedinternalformat, width, height); } bool ValidateTexStorageMem3DMultisampleEXT(const Context *context, TextureType target, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations, MemoryObjectID memory, GLuint64 offset) { if (!context->getExtensions().memoryObject) { context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled); return false; } UNIMPLEMENTED(); return false; } bool ValidateGetProgramResourceLocationIndexEXT(const Context *context, ShaderProgramID program, GLenum programInterface, const char *name) { if (!context->getExtensions().blendFuncExtended) { context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled); return false; } if (context->getClientVersion() < ES_3_1) { context->validationError(GL_INVALID_OPERATION, kES31Required); return false; } if (programInterface != GL_PROGRAM_OUTPUT) { context->validationError(GL_INVALID_ENUM, kProgramInterfaceMustBeProgramOutput); return false; } Program *programObject = GetValidProgram(context, program); if (!programObject) { return false; } if (!programObject->isLinked()) { context->validationError(GL_INVALID_OPERATION, kProgramNotLinked); return false; } return true; } // GL_OES_texture_buffer bool ValidateTexBufferOES(const Context *context, TextureType target, GLenum internalformat, BufferID bufferPacked) { if (!context->getExtensions().textureBufferOES) { context->validationError(GL_INVALID_OPERATION, kTextureBufferExtensionNotAvailable); return false; } return ValidateTexBufferBase(context, target, internalformat, bufferPacked); } bool ValidateTexBufferRangeOES(const Context *context, TextureType target, GLenum internalformat, BufferID bufferPacked, GLintptr offset, GLsizeiptr size) { if (!context->getExtensions().textureBufferOES) { context->validationError(GL_INVALID_OPERATION, kTextureBufferExtensionNotAvailable); return false; } return ValidateTexBufferRangeBase(context, target, internalformat, bufferPacked, offset, size); } // GL_EXT_texture_buffer bool ValidateTexBufferEXT(const Context *context, TextureType target, GLenum internalformat, BufferID bufferPacked) { if (!context->getExtensions().textureBufferEXT) { context->validationError(GL_INVALID_OPERATION, kTextureBufferExtensionNotAvailable); return false; } return ValidateTexBufferBase(context, target, internalformat, bufferPacked); } bool ValidateTexBufferRangeEXT(const Context *context, TextureType target, GLenum internalformat, BufferID bufferPacked, GLintptr offset, GLsizeiptr size) { if (!context->getExtensions().textureBufferEXT) { context->validationError(GL_INVALID_OPERATION, kTextureBufferExtensionNotAvailable); return false; } return ValidateTexBufferRangeBase(context, target, internalformat, bufferPacked, offset, size); } bool ValidateTexBufferBase(const Context *context, TextureType target, GLenum internalformat, BufferID bufferPacked) { if (target != TextureType::Buffer) { context->validationError(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(GL_INVALID_ENUM, kTextureBufferInternalFormat); return false; } if (bufferPacked.value != 0) { if (!context->isBufferGenerated(bufferPacked)) { context->validationError(GL_INVALID_OPERATION, kTextureBufferInvalidBuffer); return false; } } return true; } bool ValidateTexBufferRangeBase(const Context *context, TextureType target, GLenum internalformat, BufferID bufferPacked, GLintptr offset, GLsizeiptr size) { const Caps &caps = context->getCaps(); if (offset < 0 || (offset % caps.textureBufferOffsetAlignment) != 0) { context->validationError(GL_INVALID_VALUE, kTextureBufferOffsetAlignment); return false; } if (size <= 0) { context->validationError(GL_INVALID_VALUE, kTextureBufferSize); return false; } const Buffer *buffer = context->getBuffer(bufferPacked); if (!buffer) { context->validationError(GL_INVALID_OPERATION, kBufferNotBound); return false; } if (offset + size > buffer->getSize()) { context->validationError(GL_INVALID_VALUE, kTextureBufferSizeOffset); return false; } return ValidateTexBufferBase(context, target, internalformat, bufferPacked); } } // namespace gl