summaryrefslogtreecommitdiffstats
path: root/gfx/angle/checkout/src/libANGLE/validationES31.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--gfx/angle/checkout/src/libANGLE/validationES31.cpp3157
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