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.");
ERRMSG(MismatchedVariableProgram, "Variable is not part of the current program.");
ERRMSG(MissingReadAttachment, "Missing read attachment.");
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(NegativeAttachments, "Negative number of attachments.");
ERRMSG(NegativeBufferSize, "Negative buffer size.");
......@@ -184,6 +195,9 @@ ERRMSG(NegativeStart, "Cannot have negative start.");
ERRMSG(NegativeStride, "Cannot have negative stride.");
ERRMSG(NoActiveComputeShaderStage, "No active compute 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(NonPositiveDrawTextureDimension,
"Both width and height argument of drawn texture must be positive.");
......@@ -236,6 +250,10 @@ ERRMSG(TypeMismatch,
ERRMSG(TypeNotUnsignedShortByte, "Only UNSIGNED_SHORT and UNSIGNED_BYTE types are supported.");
ERRMSG(UniformBufferBoundForTransformFeedback,
"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(UnknownParameter, "Unknown parameter value.");
ERRMSG(UnsupportedDrawModeForTransformFeedback,
......
......@@ -79,33 +79,6 @@ bool DifferenceCanOverflow(GLint a, GLint b)
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 @@ bool ValidateFragmentShaderColorBufferTypeMatch(Context *context)
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 @@ bool ValidateVertexShaderAttributeTypeMatch(Context *context)
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 @@ bool IsValidGLES1TextureParameter(GLenum pname)
return false;
}
}
} // anonymous namespace
void SetRobustLengthParam(GLsizei *length, GLsizei value)
......@@ -2588,42 +2550,9 @@ bool ValidateCopyTexImageParametersBase(Context *context,
return true;
}
bool ValidateDrawBase(Context *context, PrimitiveMode mode, GLsizei count)
ErrorAndMessage ValidateDrawStates(Context *context)
{
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();
// WebGL buffers cannot be mapped/unmapped because the MapBufferRange, FlushMappedBufferRange,
......@@ -2631,8 +2560,7 @@ bool ValidateDrawBase(Context *context, PrimitiveMode mode, GLsizei count)
// https://www.khronos.org/registry/webgl/specs/latest/2.0/#5.14
if (!extensions.webglCompatibility && state.getVertexArray()->hasMappedEnabledArrayBuffer())
{
context->handleError(InvalidOperation());
return false;
return {GL_INVALID_OPERATION, nullptr};
}
// 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)
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;
return {GL_INVALID_OPERATION, kErrorStencilReferenceMaskOrMismatch};
}
}
}
if (!ValidateFramebufferComplete(context, framebuffer))
if (!framebuffer->isComplete(context))
{
return false;
return {GL_INVALID_FRAMEBUFFER_OPERATION, nullptr};
}
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)
Program *program = state.getProgram();
if (!program)
{
ANGLE_VALIDATION_ERR(context, InvalidOperation(), ProgramNotBound);
return false;
return {GL_INVALID_OPERATION, kErrorProgramNotBound};
}
// 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)
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;
return {GL_INVALID_OPERATION, kErrorNoActiveGraphicsShaderStage};
}
if (!program->validateSamplers(nullptr, context->getCaps()))
{
context->handleError(InvalidOperation());
return false;
return {GL_INVALID_OPERATION, nullptr};
}
if (extensions.multiview)
......@@ -2720,44 +2653,20 @@ bool ValidateDrawBase(Context *context, PrimitiveMode mode, GLsizei count)
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;
return {GL_INVALID_OPERATION, kErrorMultiviewMismatch};
}
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;
return {GL_INVALID_OPERATION, kErrorMultiviewTransformFeedback};
}
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;
}
}
// Do geometry shader specific validations
if (program->hasLinkedShaderStage(ShaderType::Geometry))
{
if (!IsCompatibleDrawModeWithGeometryShader(
mode, program->getGeometryShaderInputPrimitiveType()))
{
ANGLE_VALIDATION_ERR(context, InvalidOperation(),
IncompatibleDrawModeAgainstGeometryShader);
return false;
return {GL_INVALID_OPERATION, kErrorMultiviewTimerQuery};
}
}
......@@ -2773,28 +2682,20 @@ bool ValidateDrawBase(Context *context, PrimitiveMode mode, GLsizei count)
if (uniformBuffer.get() == nullptr)
{
// undefined behaviour
context->handleError(
InvalidOperation()
<< "It is undefined behaviour to have a used but unbound uniform buffer.");
return false;
return {GL_INVALID_OPERATION, kErrorUniformBufferUnbound};
}
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;
return {GL_INVALID_OPERATION, kErrorUniformBufferTooSmall};
}
if (extensions.webglCompatibility &&
uniformBuffer->isBoundForTransformFeedbackAndOtherUse())
{
ANGLE_VALIDATION_ERR(context, InvalidOperation(),
UniformBufferBoundForTransformFeedback);
return false;
return {GL_INVALID_OPERATION, kErrorUniformBufferBoundForTransformFeedback};
}
}
......@@ -2805,38 +2706,110 @@ bool ValidateDrawBase(Context *context, PrimitiveMode mode, GLsizei count)
if (transformFeedbackObject != nullptr && transformFeedbackObject->isActive() &&
transformFeedbackObject->buffersBoundForOtherUse())
{
ANGLE_VALIDATION_ERR(context, InvalidOperation(),
TransformFeedbackBufferDoubleBound);
return false;
return {GL_INVALID_OPERATION, kErrorTransformFeedbackBufferDoubleBound};
}
// Detect rendering feedback loops for WebGL.
if (framebuffer->formsRenderingFeedbackLoopWith(state))
{
ANGLE_VALIDATION_ERR(context, InvalidOperation(), FeedbackLoop);
return false;
return {GL_INVALID_OPERATION, kErrorFeedbackLoop};
}
// Detect that the vertex shader input types match the attribute types
if (!ValidateVertexShaderAttributeTypeMatch(context))
{
return false;
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();
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 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();
if (vao->hasTransformFeedbackBindingConflict(context))
{
ANGLE_VALIDATION_ERR(context, InvalidOperation(),
VertexBufferBoundForTransformFeedback);
return false;
}
ANGLE_VALIDATION_ERR(context, InvalidOperation(),
IncompatibleDrawModeAgainstGeometryShader);
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
return true;
}
struct ErrorAndMessage
{
GLenum errorType;
const char *message;
};
} // namespace gl
#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