Refactor ValidateDrawBase.
Split the parameter-dependent and parameter-independent validation.
This will more easily let us cache independent validation in Context.
Bug: angleproject:2747
Change-Id: I78a12798cd03a398392ca213bf51e2079295b97e
Reviewed-on: https://chromium-review.googlesource.com/1158610
Commit-Queue: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Frank Henigman <fjhenigman@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/libANGLE/validationES.cpp b/src/libANGLE/validationES.cpp
index 99b7009..d05cb75 100644
--- a/src/libANGLE/validationES.cpp
+++ b/src/libANGLE/validationES.cpp
@@ -79,33 +79,6 @@
return !checkedA.IsValid();
}
-bool ValidateDrawClientAttribs(Context *context)
-{
- ASSERT(context->getStateCache().hasAnyEnabledClientAttrib());
-
- const State &state = context->getGLState();
-
- if (context->getExtensions().webglCompatibility || !state.areClientArraysEnabled())
- {
- // [WebGL 1.0] Section 6.5 Enabled Vertex Attributes and Range Checking
- // If a vertex attribute is enabled as an array via enableVertexAttribArray but no
- // buffer is bound to that attribute via bindBuffer and vertexAttribPointer, then calls
- // to drawArrays or drawElements will generate an INVALID_OPERATION error.
- ANGLE_VALIDATION_ERR(context, InvalidOperation(), VertexArrayNoBuffer);
- return false;
- }
-
- if (state.getVertexArray()->hasEnabledNullPointerClientArray())
- {
- // This is an application error that would normally result in a crash, but we catch it
- // and return an error
- ANGLE_VALIDATION_ERR(context, InvalidOperation(), VertexArrayNoBufferPointer);
- return false;
- }
-
- return true;
-}
-
bool ValidateDrawAttribs(Context *context, GLint primcount, GLint maxVertex)
{
// If we're drawing zero vertices, we have enough data.
@@ -408,16 +381,10 @@
const Program *program = context->getGLState().getProgram();
const Framebuffer *framebuffer = context->getGLState().getDrawFramebuffer();
- if (!ComponentTypeMask::Validate(program->getDrawBufferTypeMask().to_ulong(),
- framebuffer->getDrawBufferTypeMask().to_ulong(),
- program->getActiveOutputVariables().to_ulong(),
- framebuffer->getDrawBufferMask().to_ulong()))
- {
- ANGLE_VALIDATION_ERR(context, InvalidOperation(), DrawBufferTypeMismatch);
- return false;
- }
-
- return true;
+ return ComponentTypeMask::Validate(program->getDrawBufferTypeMask().to_ulong(),
+ framebuffer->getDrawBufferTypeMask().to_ulong(),
+ program->getActiveOutputVariables().to_ulong(),
+ framebuffer->getDrawBufferMask().to_ulong());
}
bool ValidateVertexShaderAttributeTypeMatch(Context *context)
@@ -434,13 +401,9 @@
vaoAttribTypeBits = (vaoAttribEnabledMask & vaoAttribTypeBits);
vaoAttribTypeBits |= (~vaoAttribEnabledMask & stateCurrentValuesTypeBits);
- if (!ComponentTypeMask::Validate(program->getAttributesTypeMask().to_ulong(), vaoAttribTypeBits,
- program->getAttributesMask().to_ulong(), 0xFFFF))
- {
- ANGLE_VALIDATION_ERR(context, InvalidOperation(), VertexShaderTypeMismatch);
- return false;
- }
- return true;
+ return ComponentTypeMask::Validate(program->getAttributesTypeMask().to_ulong(),
+ vaoAttribTypeBits, program->getAttributesMask().to_ulong(),
+ 0xFFFF);
}
bool IsCompatibleDrawModeWithGeometryShader(PrimitiveMode drawMode,
@@ -488,7 +451,6 @@
return false;
}
}
-
} // anonymous namespace
void SetRobustLengthParam(GLsizei *length, GLsizei value)
@@ -2588,6 +2550,188 @@
return true;
}
+ErrorAndMessage ValidateDrawStates(Context *context)
+{
+ const Extensions &extensions = context->getExtensions();
+ const State &state = context->getGLState();
+
+ // WebGL buffers cannot be mapped/unmapped because the MapBufferRange, FlushMappedBufferRange,
+ // and UnmapBuffer entry points are removed from the WebGL 2.0 API.
+ // https://www.khronos.org/registry/webgl/specs/latest/2.0/#5.14
+ if (!extensions.webglCompatibility && state.getVertexArray()->hasMappedEnabledArrayBuffer())
+ {
+ return {GL_INVALID_OPERATION, nullptr};
+ }
+
+ // Note: these separate values are not supported in WebGL, due to D3D's limitations. See
+ // Section 6.10 of the WebGL 1.0 spec.
+ Framebuffer *framebuffer = state.getDrawFramebuffer();
+ if (context->getLimitations().noSeparateStencilRefsAndMasks || extensions.webglCompatibility)
+ {
+ ASSERT(framebuffer);
+ const FramebufferAttachment *dsAttachment =
+ framebuffer->getStencilOrDepthStencilAttachment();
+ const GLuint stencilBits = dsAttachment ? dsAttachment->getStencilSize() : 0;
+ ASSERT(stencilBits <= 8);
+
+ const DepthStencilState &depthStencilState = state.getDepthStencilState();
+ if (depthStencilState.stencilTest && stencilBits > 0)
+ {
+ GLuint maxStencilValue = (1 << stencilBits) - 1;
+
+ bool differentRefs =
+ clamp(state.getStencilRef(), 0, static_cast<GLint>(maxStencilValue)) !=
+ clamp(state.getStencilBackRef(), 0, static_cast<GLint>(maxStencilValue));
+ bool differentWritemasks = (depthStencilState.stencilWritemask & maxStencilValue) !=
+ (depthStencilState.stencilBackWritemask & maxStencilValue);
+ bool differentMasks = (depthStencilState.stencilMask & maxStencilValue) !=
+ (depthStencilState.stencilBackMask & maxStencilValue);
+
+ if (differentRefs || differentWritemasks || differentMasks)
+ {
+ if (!extensions.webglCompatibility)
+ {
+ WARN() << "This ANGLE implementation does not support separate front/back "
+ "stencil writemasks, reference values, or stencil mask values.";
+ }
+ return {GL_INVALID_OPERATION, kErrorStencilReferenceMaskOrMismatch};
+ }
+ }
+ }
+
+ if (!framebuffer->isComplete(context))
+ {
+ return {GL_INVALID_FRAMEBUFFER_OPERATION, nullptr};
+ }
+
+ if (context->getStateCache().hasAnyEnabledClientAttrib())
+ {
+ if (context->getExtensions().webglCompatibility || !state.areClientArraysEnabled())
+ {
+ // [WebGL 1.0] Section 6.5 Enabled Vertex Attributes and Range Checking
+ // If a vertex attribute is enabled as an array via enableVertexAttribArray but no
+ // buffer is bound to that attribute via bindBuffer and vertexAttribPointer, then calls
+ // to drawArrays or drawElements will generate an INVALID_OPERATION error.
+ return {GL_INVALID_OPERATION, kErrorVertexArrayNoBuffer};
+ }
+
+ if (state.getVertexArray()->hasEnabledNullPointerClientArray())
+ {
+ // This is an application error that would normally result in a crash, but we catch it
+ // and return an error
+ return {GL_INVALID_OPERATION, kErrorVertexArrayNoBufferPointer};
+ }
+ }
+
+ // If we are running GLES1, there is no current program.
+ if (context->getClientVersion() >= Version(2, 0))
+ {
+ Program *program = state.getProgram();
+ if (!program)
+ {
+ return {GL_INVALID_OPERATION, kErrorProgramNotBound};
+ }
+
+ // In OpenGL ES spec for UseProgram at section 7.3, trying to render without
+ // vertex shader stage or fragment shader stage is a undefined behaviour.
+ // But ANGLE should clearly generate an INVALID_OPERATION error instead of
+ // produce undefined result.
+ if (!program->hasLinkedShaderStage(ShaderType::Vertex) ||
+ !program->hasLinkedShaderStage(ShaderType::Fragment))
+ {
+ return {GL_INVALID_OPERATION, kErrorNoActiveGraphicsShaderStage};
+ }
+
+ if (!program->validateSamplers(nullptr, context->getCaps()))
+ {
+ return {GL_INVALID_OPERATION, nullptr};
+ }
+
+ if (extensions.multiview)
+ {
+ const int programNumViews = program->usesMultiview() ? program->getNumViews() : 1;
+ const int framebufferNumViews = framebuffer->getNumViews();
+ if (framebufferNumViews != programNumViews)
+ {
+ return {GL_INVALID_OPERATION, kErrorMultiviewMismatch};
+ }
+
+ const TransformFeedback *transformFeedbackObject = state.getCurrentTransformFeedback();
+ if (transformFeedbackObject != nullptr && transformFeedbackObject->isActive() &&
+ framebufferNumViews > 1)
+ {
+ return {GL_INVALID_OPERATION, kErrorMultiviewTransformFeedback};
+ }
+
+ if (extensions.disjointTimerQuery && framebufferNumViews > 1 &&
+ state.isQueryActive(QueryType::TimeElapsed))
+ {
+ return {GL_INVALID_OPERATION, kErrorMultiviewTimerQuery};
+ }
+ }
+
+ // Uniform buffer validation
+ for (unsigned int uniformBlockIndex = 0;
+ uniformBlockIndex < program->getActiveUniformBlockCount(); uniformBlockIndex++)
+ {
+ const InterfaceBlock &uniformBlock = program->getUniformBlockByIndex(uniformBlockIndex);
+ GLuint blockBinding = program->getUniformBlockBinding(uniformBlockIndex);
+ const OffsetBindingPointer<Buffer> &uniformBuffer =
+ state.getIndexedUniformBuffer(blockBinding);
+
+ if (uniformBuffer.get() == nullptr)
+ {
+ // undefined behaviour
+ return {GL_INVALID_OPERATION, kErrorUniformBufferUnbound};
+ }
+
+ size_t uniformBufferSize = GetBoundBufferAvailableSize(uniformBuffer);
+ if (uniformBufferSize < uniformBlock.dataSize)
+ {
+ // undefined behaviour
+ return {GL_INVALID_OPERATION, kErrorUniformBufferTooSmall};
+ }
+
+ if (extensions.webglCompatibility &&
+ uniformBuffer->isBoundForTransformFeedbackAndOtherUse())
+ {
+ return {GL_INVALID_OPERATION, kErrorUniformBufferBoundForTransformFeedback};
+ }
+ }
+
+ // Do some additonal WebGL-specific validation
+ if (extensions.webglCompatibility)
+ {
+ const TransformFeedback *transformFeedbackObject = state.getCurrentTransformFeedback();
+ if (transformFeedbackObject != nullptr && transformFeedbackObject->isActive() &&
+ transformFeedbackObject->buffersBoundForOtherUse())
+ {
+ return {GL_INVALID_OPERATION, kErrorTransformFeedbackBufferDoubleBound};
+ }
+
+ // Detect rendering feedback loops for WebGL.
+ if (framebuffer->formsRenderingFeedbackLoopWith(state))
+ {
+ return {GL_INVALID_OPERATION, kErrorFeedbackLoop};
+ }
+
+ // Detect that the vertex shader input types match the attribute types
+ if (!ValidateVertexShaderAttributeTypeMatch(context))
+ {
+ return {GL_INVALID_OPERATION, kErrorVertexShaderTypeMismatch};
+ }
+
+ // Detect that the color buffer types match the fragment shader output types
+ if (!ValidateFragmentShaderColorBufferTypeMatch(context))
+ {
+ return {GL_INVALID_OPERATION, kErrorDrawBufferTypeMismatch};
+ }
+ }
+ }
+
+ return {GL_NO_ERROR, nullptr};
+}
+
bool ValidateDrawBase(Context *context, PrimitiveMode mode, GLsizei count)
{
const Extensions &extensions = context->getExtensions();
@@ -2626,128 +2770,25 @@
const State &state = context->getGLState();
- // WebGL buffers cannot be mapped/unmapped because the MapBufferRange, FlushMappedBufferRange,
- // and UnmapBuffer entry points are removed from the WebGL 2.0 API.
- // https://www.khronos.org/registry/webgl/specs/latest/2.0/#5.14
- if (!extensions.webglCompatibility && state.getVertexArray()->hasMappedEnabledArrayBuffer())
+ const ErrorAndMessage &errorAndMessage = ValidateDrawStates(context);
+ if (errorAndMessage.errorType != GL_NO_ERROR)
{
- context->handleError(InvalidOperation());
- return false;
- }
-
- // Note: these separate values are not supported in WebGL, due to D3D's limitations. See
- // Section 6.10 of the WebGL 1.0 spec.
- Framebuffer *framebuffer = state.getDrawFramebuffer();
- if (context->getLimitations().noSeparateStencilRefsAndMasks || extensions.webglCompatibility)
- {
- ASSERT(framebuffer);
- const FramebufferAttachment *dsAttachment =
- framebuffer->getStencilOrDepthStencilAttachment();
- const GLuint stencilBits = dsAttachment ? dsAttachment->getStencilSize() : 0;
- ASSERT(stencilBits <= 8);
-
- const DepthStencilState &depthStencilState = state.getDepthStencilState();
- if (depthStencilState.stencilTest && stencilBits > 0)
+ if (errorAndMessage.message)
{
- GLuint maxStencilValue = (1 << stencilBits) - 1;
-
- bool differentRefs =
- clamp(state.getStencilRef(), 0, static_cast<GLint>(maxStencilValue)) !=
- clamp(state.getStencilBackRef(), 0, static_cast<GLint>(maxStencilValue));
- bool differentWritemasks = (depthStencilState.stencilWritemask & maxStencilValue) !=
- (depthStencilState.stencilBackWritemask & maxStencilValue);
- bool differentMasks = (depthStencilState.stencilMask & maxStencilValue) !=
- (depthStencilState.stencilBackMask & maxStencilValue);
-
- if (differentRefs || differentWritemasks || differentMasks)
- {
- if (!extensions.webglCompatibility)
- {
- WARN() << "This ANGLE implementation does not support separate front/back "
- "stencil writemasks, reference values, or stencil mask values.";
- }
- ANGLE_VALIDATION_ERR(context, InvalidOperation(), StencilReferenceMaskOrMismatch);
- return false;
- }
+ context->handleError(Error(errorAndMessage.errorType, errorAndMessage.message));
}
- }
-
- if (!ValidateFramebufferComplete(context, framebuffer))
- {
- return false;
- }
-
- if (context->getStateCache().hasAnyEnabledClientAttrib())
- {
- if (!ValidateDrawClientAttribs(context))
+ else
{
- return false;
+ context->handleError(Error(errorAndMessage.errorType));
}
+ return false;
}
// If we are running GLES1, there is no current program.
if (context->getClientVersion() >= Version(2, 0))
{
Program *program = state.getProgram();
- if (!program)
- {
- ANGLE_VALIDATION_ERR(context, InvalidOperation(), ProgramNotBound);
- return false;
- }
-
- // In OpenGL ES spec for UseProgram at section 7.3, trying to render without
- // vertex shader stage or fragment shader stage is a undefined behaviour.
- // But ANGLE should clearly generate an INVALID_OPERATION error instead of
- // produce undefined result.
- if (!program->hasLinkedShaderStage(ShaderType::Vertex) ||
- !program->hasLinkedShaderStage(ShaderType::Fragment))
- {
- context->handleError(InvalidOperation()
- << "It is a undefined behaviour to render without "
- "vertex shader stage or fragment shader stage.");
- return false;
- }
-
- if (!program->validateSamplers(nullptr, context->getCaps()))
- {
- context->handleError(InvalidOperation());
- return false;
- }
-
- if (extensions.multiview)
- {
- const int programNumViews = program->usesMultiview() ? program->getNumViews() : 1;
- const int framebufferNumViews = framebuffer->getNumViews();
- if (framebufferNumViews != programNumViews)
- {
- context->handleError(InvalidOperation()
- << "The number of views in the active program "
- "and draw framebuffer does not match.");
- return false;
- }
-
- const TransformFeedback *transformFeedbackObject = state.getCurrentTransformFeedback();
- if (transformFeedbackObject != nullptr && transformFeedbackObject->isActive() &&
- framebufferNumViews > 1)
- {
- context->handleError(InvalidOperation()
- << "There is an active transform feedback object "
- "when the number of views in the active draw "
- "framebuffer is greater than 1.");
- return false;
- }
-
- if (extensions.disjointTimerQuery && framebufferNumViews > 1 &&
- state.isQueryActive(QueryType::TimeElapsed))
- {
- context->handleError(InvalidOperation()
- << "There is an active query for target "
- "GL_TIME_ELAPSED_EXT when the number of "
- "views in the active draw framebuffer is "
- "greater than 1.");
- return false;
- }
- }
+ ASSERT(program);
// Do geometry shader specific validations
if (program->hasLinkedShaderStage(ShaderType::Geometry))
@@ -2761,84 +2802,16 @@
}
}
- // Uniform buffer validation
- for (unsigned int uniformBlockIndex = 0;
- uniformBlockIndex < program->getActiveUniformBlockCount(); uniformBlockIndex++)
+ if (extensions.webglCompatibility && count > 0)
{
- const InterfaceBlock &uniformBlock = program->getUniformBlockByIndex(uniformBlockIndex);
- GLuint blockBinding = program->getUniformBlockBinding(uniformBlockIndex);
- const OffsetBindingPointer<Buffer> &uniformBuffer =
- state.getIndexedUniformBuffer(blockBinding);
-
- if (uniformBuffer.get() == nullptr)
- {
- // undefined behaviour
- context->handleError(
- InvalidOperation()
- << "It is undefined behaviour to have a used but unbound uniform buffer.");
- return false;
- }
-
- size_t uniformBufferSize = GetBoundBufferAvailableSize(uniformBuffer);
- if (uniformBufferSize < uniformBlock.dataSize)
- {
- // undefined behaviour
- context->handleError(
- InvalidOperation()
- << "It is undefined behaviour to use a uniform buffer that is too small.");
- return false;
- }
-
- if (extensions.webglCompatibility &&
- uniformBuffer->isBoundForTransformFeedbackAndOtherUse())
+ const VertexArray *vao = context->getGLState().getVertexArray();
+ if (vao->hasTransformFeedbackBindingConflict(context))
{
ANGLE_VALIDATION_ERR(context, InvalidOperation(),
- UniformBufferBoundForTransformFeedback);
+ VertexBufferBoundForTransformFeedback);
return false;
}
}
-
- // Do some additonal WebGL-specific validation
- if (extensions.webglCompatibility)
- {
- const TransformFeedback *transformFeedbackObject = state.getCurrentTransformFeedback();
- if (transformFeedbackObject != nullptr && transformFeedbackObject->isActive() &&
- transformFeedbackObject->buffersBoundForOtherUse())
- {
- ANGLE_VALIDATION_ERR(context, InvalidOperation(),
- TransformFeedbackBufferDoubleBound);
- return false;
- }
- // Detect rendering feedback loops for WebGL.
- if (framebuffer->formsRenderingFeedbackLoopWith(state))
- {
- ANGLE_VALIDATION_ERR(context, InvalidOperation(), FeedbackLoop);
- return false;
- }
-
- // Detect that the vertex shader input types match the attribute types
- if (!ValidateVertexShaderAttributeTypeMatch(context))
- {
- return false;
- }
-
- // Detect that the color buffer types match the fragment shader output types
- if (!ValidateFragmentShaderColorBufferTypeMatch(context))
- {
- return false;
- }
-
- if (count > 0)
- {
- const VertexArray *vao = context->getGLState().getVertexArray();
- if (vao->hasTransformFeedbackBindingConflict(context))
- {
- ANGLE_VALIDATION_ERR(context, InvalidOperation(),
- VertexBufferBoundForTransformFeedback);
- return false;
- }
- }
- }
}
return true;