Commit e7d80f33 by Jamie Madill Committed by Commit Bot

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: 's avatarFrank Henigman <fjhenigman@chromium.org> Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org>
parent cc1dc5ee
...@@ -171,6 +171,17 @@ ERRMSG(MismatchedTypeAndFormat, "Invalid format and type combination."); ...@@ -171,6 +171,17 @@ ERRMSG(MismatchedTypeAndFormat, "Invalid format and type combination.");
ERRMSG(MismatchedVariableProgram, "Variable is not part of the current program."); ERRMSG(MismatchedVariableProgram, "Variable is not part of the current program.");
ERRMSG(MissingReadAttachment, "Missing read attachment."); ERRMSG(MissingReadAttachment, "Missing read attachment.");
ERRMSG(MustHaveElementArrayBinding, "Must have element array buffer binding."); ERRMSG(MustHaveElementArrayBinding, "Must have element array buffer binding.");
ERRMSG(MultiviewMismatch,
"The number of views in the active program and draw "
"framebuffer does not match.");
ERRMSG(MultiviewTransformFeedback,
"There is an active transform feedback object when "
"the number of views in the active draw framebuffer "
"is greater than 1.");
ERRMSG(MultiviewTimerQuery,
"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.");
ERRMSG(NameBeginsWithGL, "Attributes that begin with 'gl_' are not allowed."); ERRMSG(NameBeginsWithGL, "Attributes that begin with 'gl_' are not allowed.");
ERRMSG(NegativeAttachments, "Negative number of attachments."); ERRMSG(NegativeAttachments, "Negative number of attachments.");
ERRMSG(NegativeBufferSize, "Negative buffer size."); ERRMSG(NegativeBufferSize, "Negative buffer size.");
...@@ -184,6 +195,9 @@ ERRMSG(NegativeStart, "Cannot have negative start."); ...@@ -184,6 +195,9 @@ ERRMSG(NegativeStart, "Cannot have negative start.");
ERRMSG(NegativeStride, "Cannot have negative stride."); ERRMSG(NegativeStride, "Cannot have negative stride.");
ERRMSG(NoActiveComputeShaderStage, "No active compute shader stage in this program."); ERRMSG(NoActiveComputeShaderStage, "No active compute shader stage in this program.");
ERRMSG(NoActiveGeometryShaderStage, "No active geometry shader stage in this program."); ERRMSG(NoActiveGeometryShaderStage, "No active geometry shader stage in this program.");
ERRMSG(
NoActiveGraphicsShaderStage,
"It is a undefined behaviour to render without vertex shader stage or fragment shader stage.");
ERRMSG(NoActiveProgramWithComputeShader, "No active program for the compute shader stage."); ERRMSG(NoActiveProgramWithComputeShader, "No active program for the compute shader stage.");
ERRMSG(NonPositiveDrawTextureDimension, ERRMSG(NonPositiveDrawTextureDimension,
"Both width and height argument of drawn texture must be positive."); "Both width and height argument of drawn texture must be positive.");
...@@ -236,6 +250,10 @@ ERRMSG(TypeMismatch, ...@@ -236,6 +250,10 @@ ERRMSG(TypeMismatch,
ERRMSG(TypeNotUnsignedShortByte, "Only UNSIGNED_SHORT and UNSIGNED_BYTE types are supported."); ERRMSG(TypeNotUnsignedShortByte, "Only UNSIGNED_SHORT and UNSIGNED_BYTE types are supported.");
ERRMSG(UniformBufferBoundForTransformFeedback, ERRMSG(UniformBufferBoundForTransformFeedback,
"It is undefined behavior to use an uniform buffer that is bound for transform feedback."); "It is undefined behavior to use an uniform buffer that is bound for transform feedback.");
ERRMSG(UniformBufferTooSmall,
"It is undefined behaviour to use a uniform buffer that is too small.");
ERRMSG(UniformBufferUnbound,
"It is undefined behaviour to have a used but unbound uniform buffer.");
ERRMSG(UniformSizeMismatch, "Uniform size does not match uniform method."); ERRMSG(UniformSizeMismatch, "Uniform size does not match uniform method.");
ERRMSG(UnknownParameter, "Unknown parameter value."); ERRMSG(UnknownParameter, "Unknown parameter value.");
ERRMSG(UnsupportedDrawModeForTransformFeedback, ERRMSG(UnsupportedDrawModeForTransformFeedback,
......
...@@ -79,33 +79,6 @@ bool DifferenceCanOverflow(GLint a, GLint b) ...@@ -79,33 +79,6 @@ bool DifferenceCanOverflow(GLint a, GLint b)
return !checkedA.IsValid(); 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) bool ValidateDrawAttribs(Context *context, GLint primcount, GLint maxVertex)
{ {
// If we're drawing zero vertices, we have enough data. // If we're drawing zero vertices, we have enough data.
...@@ -408,16 +381,10 @@ bool ValidateFragmentShaderColorBufferTypeMatch(Context *context) ...@@ -408,16 +381,10 @@ bool ValidateFragmentShaderColorBufferTypeMatch(Context *context)
const Program *program = context->getGLState().getProgram(); const Program *program = context->getGLState().getProgram();
const Framebuffer *framebuffer = context->getGLState().getDrawFramebuffer(); const Framebuffer *framebuffer = context->getGLState().getDrawFramebuffer();
if (!ComponentTypeMask::Validate(program->getDrawBufferTypeMask().to_ulong(), return ComponentTypeMask::Validate(program->getDrawBufferTypeMask().to_ulong(),
framebuffer->getDrawBufferTypeMask().to_ulong(), framebuffer->getDrawBufferTypeMask().to_ulong(),
program->getActiveOutputVariables().to_ulong(), program->getActiveOutputVariables().to_ulong(),
framebuffer->getDrawBufferMask().to_ulong())) framebuffer->getDrawBufferMask().to_ulong());
{
ANGLE_VALIDATION_ERR(context, InvalidOperation(), DrawBufferTypeMismatch);
return false;
}
return true;
} }
bool ValidateVertexShaderAttributeTypeMatch(Context *context) bool ValidateVertexShaderAttributeTypeMatch(Context *context)
...@@ -434,13 +401,9 @@ bool ValidateVertexShaderAttributeTypeMatch(Context *context) ...@@ -434,13 +401,9 @@ bool ValidateVertexShaderAttributeTypeMatch(Context *context)
vaoAttribTypeBits = (vaoAttribEnabledMask & vaoAttribTypeBits); vaoAttribTypeBits = (vaoAttribEnabledMask & vaoAttribTypeBits);
vaoAttribTypeBits |= (~vaoAttribEnabledMask & stateCurrentValuesTypeBits); vaoAttribTypeBits |= (~vaoAttribEnabledMask & stateCurrentValuesTypeBits);
if (!ComponentTypeMask::Validate(program->getAttributesTypeMask().to_ulong(), vaoAttribTypeBits, return ComponentTypeMask::Validate(program->getAttributesTypeMask().to_ulong(),
program->getAttributesMask().to_ulong(), 0xFFFF)) vaoAttribTypeBits, program->getAttributesMask().to_ulong(),
{ 0xFFFF);
ANGLE_VALIDATION_ERR(context, InvalidOperation(), VertexShaderTypeMismatch);
return false;
}
return true;
} }
bool IsCompatibleDrawModeWithGeometryShader(PrimitiveMode drawMode, bool IsCompatibleDrawModeWithGeometryShader(PrimitiveMode drawMode,
...@@ -488,7 +451,6 @@ bool IsValidGLES1TextureParameter(GLenum pname) ...@@ -488,7 +451,6 @@ bool IsValidGLES1TextureParameter(GLenum pname)
return false; return false;
} }
} }
} // anonymous namespace } // anonymous namespace
void SetRobustLengthParam(GLsizei *length, GLsizei value) void SetRobustLengthParam(GLsizei *length, GLsizei value)
...@@ -2588,42 +2550,9 @@ bool ValidateCopyTexImageParametersBase(Context *context, ...@@ -2588,42 +2550,9 @@ bool ValidateCopyTexImageParametersBase(Context *context,
return true; return true;
} }
bool ValidateDrawBase(Context *context, PrimitiveMode mode, GLsizei count) ErrorAndMessage ValidateDrawStates(Context *context)
{ {
const Extensions &extensions = context->getExtensions(); const Extensions &extensions = context->getExtensions();
switch (mode)
{
case PrimitiveMode::Points:
case PrimitiveMode::Lines:
case PrimitiveMode::LineLoop:
case PrimitiveMode::LineStrip:
case PrimitiveMode::Triangles:
case PrimitiveMode::TriangleStrip:
case PrimitiveMode::TriangleFan:
break;
case PrimitiveMode::LinesAdjacency:
case PrimitiveMode::LineStripAdjacency:
case PrimitiveMode::TrianglesAdjacency:
case PrimitiveMode::TriangleStripAdjacency:
if (!extensions.geometryShader)
{
ANGLE_VALIDATION_ERR(context, InvalidEnum(), GeometryShaderExtensionNotEnabled);
return false;
}
break;
default:
ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidDrawMode);
return false;
}
if (count < 0)
{
ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeCount);
return false;
}
const State &state = context->getGLState(); const State &state = context->getGLState();
// WebGL buffers cannot be mapped/unmapped because the MapBufferRange, FlushMappedBufferRange, // WebGL buffers cannot be mapped/unmapped because the MapBufferRange, FlushMappedBufferRange,
...@@ -2631,8 +2560,7 @@ bool ValidateDrawBase(Context *context, PrimitiveMode mode, GLsizei count) ...@@ -2631,8 +2560,7 @@ bool ValidateDrawBase(Context *context, PrimitiveMode mode, GLsizei count)
// https://www.khronos.org/registry/webgl/specs/latest/2.0/#5.14 // https://www.khronos.org/registry/webgl/specs/latest/2.0/#5.14
if (!extensions.webglCompatibility && state.getVertexArray()->hasMappedEnabledArrayBuffer()) if (!extensions.webglCompatibility && state.getVertexArray()->hasMappedEnabledArrayBuffer())
{ {
context->handleError(InvalidOperation()); return {GL_INVALID_OPERATION, nullptr};
return false;
} }
// Note: these separate values are not supported in WebGL, due to D3D's limitations. See // Note: these separate values are not supported in WebGL, due to D3D's limitations. See
...@@ -2666,22 +2594,32 @@ bool ValidateDrawBase(Context *context, PrimitiveMode mode, GLsizei count) ...@@ -2666,22 +2594,32 @@ bool ValidateDrawBase(Context *context, PrimitiveMode mode, GLsizei count)
WARN() << "This ANGLE implementation does not support separate front/back " WARN() << "This ANGLE implementation does not support separate front/back "
"stencil writemasks, reference values, or stencil mask values."; "stencil writemasks, reference values, or stencil mask values.";
} }
ANGLE_VALIDATION_ERR(context, InvalidOperation(), StencilReferenceMaskOrMismatch); return {GL_INVALID_OPERATION, kErrorStencilReferenceMaskOrMismatch};
return false;
} }
} }
} }
if (!ValidateFramebufferComplete(context, framebuffer)) if (!framebuffer->isComplete(context))
{ {
return false; return {GL_INVALID_FRAMEBUFFER_OPERATION, nullptr};
} }
if (context->getStateCache().hasAnyEnabledClientAttrib()) if (context->getStateCache().hasAnyEnabledClientAttrib())
{ {
if (!ValidateDrawClientAttribs(context)) if (context->getExtensions().webglCompatibility || !state.areClientArraysEnabled())
{ {
return false; // [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};
} }
} }
...@@ -2691,8 +2629,7 @@ bool ValidateDrawBase(Context *context, PrimitiveMode mode, GLsizei count) ...@@ -2691,8 +2629,7 @@ bool ValidateDrawBase(Context *context, PrimitiveMode mode, GLsizei count)
Program *program = state.getProgram(); Program *program = state.getProgram();
if (!program) if (!program)
{ {
ANGLE_VALIDATION_ERR(context, InvalidOperation(), ProgramNotBound); return {GL_INVALID_OPERATION, kErrorProgramNotBound};
return false;
} }
// In OpenGL ES spec for UseProgram at section 7.3, trying to render without // In OpenGL ES spec for UseProgram at section 7.3, trying to render without
...@@ -2702,16 +2639,12 @@ bool ValidateDrawBase(Context *context, PrimitiveMode mode, GLsizei count) ...@@ -2702,16 +2639,12 @@ bool ValidateDrawBase(Context *context, PrimitiveMode mode, GLsizei count)
if (!program->hasLinkedShaderStage(ShaderType::Vertex) || if (!program->hasLinkedShaderStage(ShaderType::Vertex) ||
!program->hasLinkedShaderStage(ShaderType::Fragment)) !program->hasLinkedShaderStage(ShaderType::Fragment))
{ {
context->handleError(InvalidOperation() return {GL_INVALID_OPERATION, kErrorNoActiveGraphicsShaderStage};
<< "It is a undefined behaviour to render without "
"vertex shader stage or fragment shader stage.");
return false;
} }
if (!program->validateSamplers(nullptr, context->getCaps())) if (!program->validateSamplers(nullptr, context->getCaps()))
{ {
context->handleError(InvalidOperation()); return {GL_INVALID_OPERATION, nullptr};
return false;
} }
if (extensions.multiview) if (extensions.multiview)
...@@ -2720,44 +2653,20 @@ bool ValidateDrawBase(Context *context, PrimitiveMode mode, GLsizei count) ...@@ -2720,44 +2653,20 @@ bool ValidateDrawBase(Context *context, PrimitiveMode mode, GLsizei count)
const int framebufferNumViews = framebuffer->getNumViews(); const int framebufferNumViews = framebuffer->getNumViews();
if (framebufferNumViews != programNumViews) if (framebufferNumViews != programNumViews)
{ {
context->handleError(InvalidOperation() return {GL_INVALID_OPERATION, kErrorMultiviewMismatch};
<< "The number of views in the active program "
"and draw framebuffer does not match.");
return false;
} }
const TransformFeedback *transformFeedbackObject = state.getCurrentTransformFeedback(); const TransformFeedback *transformFeedbackObject = state.getCurrentTransformFeedback();
if (transformFeedbackObject != nullptr && transformFeedbackObject->isActive() && if (transformFeedbackObject != nullptr && transformFeedbackObject->isActive() &&
framebufferNumViews > 1) framebufferNumViews > 1)
{ {
context->handleError(InvalidOperation() return {GL_INVALID_OPERATION, kErrorMultiviewTransformFeedback};
<< "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 && if (extensions.disjointTimerQuery && framebufferNumViews > 1 &&
state.isQueryActive(QueryType::TimeElapsed)) state.isQueryActive(QueryType::TimeElapsed))
{ {
context->handleError(InvalidOperation() return {GL_INVALID_OPERATION, kErrorMultiviewTimerQuery};
<< "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;
}
}
// Do geometry shader specific validations
if (program->hasLinkedShaderStage(ShaderType::Geometry))
{
if (!IsCompatibleDrawModeWithGeometryShader(
mode, program->getGeometryShaderInputPrimitiveType()))
{
ANGLE_VALIDATION_ERR(context, InvalidOperation(),
IncompatibleDrawModeAgainstGeometryShader);
return false;
} }
} }
...@@ -2773,28 +2682,20 @@ bool ValidateDrawBase(Context *context, PrimitiveMode mode, GLsizei count) ...@@ -2773,28 +2682,20 @@ bool ValidateDrawBase(Context *context, PrimitiveMode mode, GLsizei count)
if (uniformBuffer.get() == nullptr) if (uniformBuffer.get() == nullptr)
{ {
// undefined behaviour // undefined behaviour
context->handleError( return {GL_INVALID_OPERATION, kErrorUniformBufferUnbound};
InvalidOperation()
<< "It is undefined behaviour to have a used but unbound uniform buffer.");
return false;
} }
size_t uniformBufferSize = GetBoundBufferAvailableSize(uniformBuffer); size_t uniformBufferSize = GetBoundBufferAvailableSize(uniformBuffer);
if (uniformBufferSize < uniformBlock.dataSize) if (uniformBufferSize < uniformBlock.dataSize)
{ {
// undefined behaviour // undefined behaviour
context->handleError( return {GL_INVALID_OPERATION, kErrorUniformBufferTooSmall};
InvalidOperation()
<< "It is undefined behaviour to use a uniform buffer that is too small.");
return false;
} }
if (extensions.webglCompatibility && if (extensions.webglCompatibility &&
uniformBuffer->isBoundForTransformFeedbackAndOtherUse()) uniformBuffer->isBoundForTransformFeedbackAndOtherUse())
{ {
ANGLE_VALIDATION_ERR(context, InvalidOperation(), return {GL_INVALID_OPERATION, kErrorUniformBufferBoundForTransformFeedback};
UniformBufferBoundForTransformFeedback);
return false;
} }
} }
...@@ -2805,38 +2706,110 @@ bool ValidateDrawBase(Context *context, PrimitiveMode mode, GLsizei count) ...@@ -2805,38 +2706,110 @@ bool ValidateDrawBase(Context *context, PrimitiveMode mode, GLsizei count)
if (transformFeedbackObject != nullptr && transformFeedbackObject->isActive() && if (transformFeedbackObject != nullptr && transformFeedbackObject->isActive() &&
transformFeedbackObject->buffersBoundForOtherUse()) transformFeedbackObject->buffersBoundForOtherUse())
{ {
ANGLE_VALIDATION_ERR(context, InvalidOperation(), return {GL_INVALID_OPERATION, kErrorTransformFeedbackBufferDoubleBound};
TransformFeedbackBufferDoubleBound);
return false;
} }
// Detect rendering feedback loops for WebGL. // Detect rendering feedback loops for WebGL.
if (framebuffer->formsRenderingFeedbackLoopWith(state)) if (framebuffer->formsRenderingFeedbackLoopWith(state))
{ {
ANGLE_VALIDATION_ERR(context, InvalidOperation(), FeedbackLoop); return {GL_INVALID_OPERATION, kErrorFeedbackLoop};
return false;
} }
// Detect that the vertex shader input types match the attribute types // Detect that the vertex shader input types match the attribute types
if (!ValidateVertexShaderAttributeTypeMatch(context)) if (!ValidateVertexShaderAttributeTypeMatch(context))
{ {
return false; return {GL_INVALID_OPERATION, kErrorVertexShaderTypeMismatch};
} }
// Detect that the color buffer types match the fragment shader output types // Detect that the color buffer types match the fragment shader output types
if (!ValidateFragmentShaderColorBufferTypeMatch(context)) 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();
switch (mode)
{
case PrimitiveMode::Points:
case PrimitiveMode::Lines:
case PrimitiveMode::LineLoop:
case PrimitiveMode::LineStrip:
case PrimitiveMode::Triangles:
case PrimitiveMode::TriangleStrip:
case PrimitiveMode::TriangleFan:
break;
case PrimitiveMode::LinesAdjacency:
case PrimitiveMode::LineStripAdjacency:
case PrimitiveMode::TrianglesAdjacency:
case PrimitiveMode::TriangleStripAdjacency:
if (!extensions.geometryShader)
{
ANGLE_VALIDATION_ERR(context, InvalidEnum(), GeometryShaderExtensionNotEnabled);
return false; return false;
} }
break;
default:
ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidDrawMode);
return false;
}
if (count < 0)
{
ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeCount);
return false;
}
const State &state = context->getGLState();
const ErrorAndMessage &errorAndMessage = ValidateDrawStates(context);
if (errorAndMessage.errorType != GL_NO_ERROR)
{
if (errorAndMessage.message)
{
context->handleError(Error(errorAndMessage.errorType, errorAndMessage.message));
}
else
{
context->handleError(Error(errorAndMessage.errorType));
}
return false;
}
if (count > 0) // If we are running GLES1, there is no current program.
if (context->getClientVersion() >= Version(2, 0))
{
Program *program = state.getProgram();
ASSERT(program);
// Do geometry shader specific validations
if (program->hasLinkedShaderStage(ShaderType::Geometry))
{
if (!IsCompatibleDrawModeWithGeometryShader(
mode, program->getGeometryShaderInputPrimitiveType()))
{ {
const VertexArray *vao = context->getGLState().getVertexArray(); ANGLE_VALIDATION_ERR(context, InvalidOperation(),
if (vao->hasTransformFeedbackBindingConflict(context)) IncompatibleDrawModeAgainstGeometryShader);
{ return false;
ANGLE_VALIDATION_ERR(context, InvalidOperation(), }
VertexBufferBoundForTransformFeedback); }
return false;
} if (extensions.webglCompatibility && count > 0)
{
const VertexArray *vao = context->getGLState().getVertexArray();
if (vao->hasTransformFeedbackBindingConflict(context))
{
ANGLE_VALIDATION_ERR(context, InvalidOperation(),
VertexBufferBoundForTransformFeedback);
return false;
} }
} }
} }
......
...@@ -704,6 +704,12 @@ ANGLE_INLINE bool ValidateFramebufferComplete(Context *context, Framebuffer *fra ...@@ -704,6 +704,12 @@ ANGLE_INLINE bool ValidateFramebufferComplete(Context *context, Framebuffer *fra
return true; return true;
} }
struct ErrorAndMessage
{
GLenum errorType;
const char *message;
};
} // namespace gl } // namespace gl
#endif // LIBANGLE_VALIDATION_ES_H_ #endif // LIBANGLE_VALIDATION_ES_H_
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment