/** * Called via glBegin. */ static void GLAPIENTRY vbo_exec_Begin( GLenum mode ) { GET_CURRENT_CONTEXT( ctx ); struct vbo_exec_context *exec = &vbo_context(ctx)->exec; int i; if (_mesa_inside_begin_end(ctx)) { _mesa_error(ctx, GL_INVALID_OPERATION, "glBegin"); return; } if (!_mesa_valid_prim_mode(ctx, mode, "glBegin")) { return; } vbo_draw_method(vbo_context(ctx), DRAW_BEGIN_END); if (ctx->NewState) { _mesa_update_state( ctx ); CALL_Begin(ctx->Exec, (mode)); return; } if (!_mesa_valid_to_render(ctx, "glBegin")) { return; } /* Heuristic: attempt to isolate attributes occurring outside * begin/end pairs. */ if (exec->vtx.vertex_size && !exec->vtx.attrsz[0]) vbo_exec_FlushVertices_internal(exec, GL_FALSE); i = exec->vtx.prim_count++; exec->vtx.prim[i].mode = mode; exec->vtx.prim[i].begin = 1; exec->vtx.prim[i].end = 0; exec->vtx.prim[i].indexed = 0; exec->vtx.prim[i].weak = 0; exec->vtx.prim[i].pad = 0; exec->vtx.prim[i].start = exec->vtx.vert_count; exec->vtx.prim[i].count = 0; exec->vtx.prim[i].num_instances = 1; exec->vtx.prim[i].base_instance = 0; exec->vtx.prim[i].is_indirect = 0; ctx->Driver.CurrentExecPrimitive = mode; ctx->Exec = ctx->BeginEnd; /* We may have been called from a display list, in which case we should * leave dlist.c's dispatch table in place. */ if (ctx->CurrentDispatch == ctx->OutsideBeginEnd) { ctx->CurrentDispatch = ctx->BeginEnd; _glapi_set_dispatch(ctx->CurrentDispatch); } else { assert(ctx->CurrentDispatch == ctx->Save); } }
/** * Error checking for glMultiDrawElements(). Includes parameter checking * and VBO bounds checking. * \return GL_TRUE if OK to render, GL_FALSE if error found */ GLboolean _mesa_validate_MultiDrawElements(struct gl_context *ctx, GLenum mode, const GLsizei *count, GLenum type, const GLvoid * const *indices, GLuint primcount, const GLint *basevertex) { unsigned i; FLUSH_CURRENT(ctx, 0); for (i = 0; i < primcount; i++) { if (count[i] <= 0) { if (count[i] < 0) _mesa_error(ctx, GL_INVALID_VALUE, "glMultiDrawElements(count)" ); return GL_FALSE; } } if (!_mesa_valid_prim_mode(ctx, mode, "glMultiDrawElements")) { return GL_FALSE; } if (!valid_elements_type(ctx, type, "glMultiDrawElements")) return GL_FALSE; if (!check_valid_to_render(ctx, "glMultiDrawElements")) return GL_FALSE; /* Vertex buffer object tests */ if (_mesa_is_bufferobj(ctx->Array.ArrayObj->ElementArrayBufferObj)) { /* use indices in the buffer object */ /* make sure count doesn't go outside buffer bounds */ for (i = 0; i < primcount; i++) { if (index_bytes(type, count[i]) > ctx->Array.ArrayObj->ElementArrayBufferObj->Size) { _mesa_warning(ctx, "glMultiDrawElements index out of buffer bounds"); return GL_FALSE; } } } else { /* not using a VBO */ for (i = 0; i < primcount; i++) { if (!indices[i]) return GL_FALSE; } } for (i = 0; i < primcount; i++) { if (!check_index_bounds(ctx, count[i], type, indices[i], basevertex ? basevertex[i] : 0)) return GL_FALSE; } return GL_TRUE; }
/** * Error checking for glDrawElements(). Includes parameter checking * and VBO bounds checking. * \return GL_TRUE if OK to render, GL_FALSE if error found */ GLboolean _mesa_validate_DrawElements(struct gl_context *ctx, GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLint basevertex) { FLUSH_CURRENT(ctx, 0); /* From the GLES3 specification, section 2.14.2 (Transform Feedback * Primitive Capture): * * The error INVALID_OPERATION is also generated by DrawElements, * DrawElementsInstanced, and DrawRangeElements while transform feedback * is active and not paused, regardless of mode. */ if (_mesa_is_gles3(ctx) && _mesa_is_xfb_active_and_unpaused(ctx)) { _mesa_error(ctx, GL_INVALID_OPERATION, "glDrawElements(transform feedback active)"); return GL_FALSE; } if (count <= 0) { if (count < 0) _mesa_error(ctx, GL_INVALID_VALUE, "glDrawElements(count)" ); return GL_FALSE; } if (!_mesa_valid_prim_mode(ctx, mode, "glDrawElements")) { return GL_FALSE; } if (!valid_elements_type(ctx, type, "glDrawElements")) return GL_FALSE; if (!check_valid_to_render(ctx, "glDrawElements")) return GL_FALSE; /* Vertex buffer object tests */ if (_mesa_is_bufferobj(ctx->Array.ArrayObj->ElementArrayBufferObj)) { /* use indices in the buffer object */ /* make sure count doesn't go outside buffer bounds */ if (index_bytes(type, count) > ctx->Array.ArrayObj->ElementArrayBufferObj->Size) { _mesa_warning(ctx, "glDrawElements index out of buffer bounds"); return GL_FALSE; } } else { /* not using a VBO */ if (!indices) return GL_FALSE; } if (!check_index_bounds(ctx, count, type, indices, basevertex)) return GL_FALSE; return GL_TRUE; }
GLboolean _mesa_validate_DrawTransformFeedback(struct gl_context *ctx, GLenum mode, struct gl_transform_feedback_object *obj, GLuint stream, GLsizei numInstances) { FLUSH_CURRENT(ctx, 0); if (!_mesa_valid_prim_mode(ctx, mode, "glDrawTransformFeedback*(mode)")) { return GL_FALSE; } if (!obj) { _mesa_error(ctx, GL_INVALID_VALUE, "glDrawTransformFeedback*(name)"); return GL_FALSE; } /* From the GL 4.5 specification, page 429: * "An INVALID_VALUE error is generated if id is not the name of a * transform feedback object." */ if (!obj->EverBound) { _mesa_error(ctx, GL_INVALID_VALUE, "glDrawTransformFeedback*(name)"); return GL_FALSE; } if (stream >= ctx->Const.MaxVertexStreams) { _mesa_error(ctx, GL_INVALID_VALUE, "glDrawTransformFeedbackStream*(index>=MaxVertexStream)"); return GL_FALSE; } if (!obj->EndedAnytime) { _mesa_error(ctx, GL_INVALID_OPERATION, "glDrawTransformFeedback*"); return GL_FALSE; } if (numInstances <= 0) { if (numInstances < 0) _mesa_error(ctx, GL_INVALID_VALUE, "glDrawTransformFeedback*Instanced(numInstances=%d)", numInstances); return GL_FALSE; } if (!check_valid_to_render(ctx, "glDrawTransformFeedback*")) { return GL_FALSE; } return GL_TRUE; }
/** * Called via glBegin. */ static void GLAPIENTRY vbo_exec_Begin( GLenum mode ) { GET_CURRENT_CONTEXT( ctx ); if (ctx->Driver.CurrentExecPrimitive == PRIM_OUTSIDE_BEGIN_END) { struct vbo_exec_context *exec = &vbo_context(ctx)->exec; int i; if (!_mesa_valid_prim_mode(ctx, mode, "glBegin")) { return; } vbo_draw_method(vbo_context(ctx), DRAW_BEGIN_END); if (ctx->Driver.PrepareExecBegin) ctx->Driver.PrepareExecBegin(ctx); if (ctx->NewState) { _mesa_update_state( ctx ); CALL_Begin(ctx->Exec, (mode)); return; } if (!_mesa_valid_to_render(ctx, "glBegin")) { return; } /* Heuristic: attempt to isolate attributes occuring outside * begin/end pairs. */ if (exec->vtx.vertex_size && !exec->vtx.attrsz[0]) vbo_exec_FlushVertices_internal(exec, GL_FALSE); i = exec->vtx.prim_count++; exec->vtx.prim[i].mode = mode; exec->vtx.prim[i].begin = 1; exec->vtx.prim[i].end = 0; exec->vtx.prim[i].indexed = 0; exec->vtx.prim[i].weak = 0; exec->vtx.prim[i].pad = 0; exec->vtx.prim[i].start = exec->vtx.vert_count; exec->vtx.prim[i].count = 0; exec->vtx.prim[i].num_instances = 1; exec->vtx.prim[i].base_instance = 0; ctx->Driver.CurrentExecPrimitive = mode; } else _mesa_error( ctx, GL_INVALID_OPERATION, "glBegin" ); }
/** * Called from the tnl module to error check the function parameters and * verify that we really can draw something. * \return GL_TRUE if OK to render, GL_FALSE if error found */ GLboolean _mesa_validate_DrawArrays(struct gl_context *ctx, GLenum mode, GLint start, GLsizei count) { struct gl_transform_feedback_object *xfb_obj = ctx->TransformFeedback.CurrentObject; FLUSH_CURRENT(ctx, 0); if (count <= 0) { if (count < 0) _mesa_error(ctx, GL_INVALID_VALUE, "glDrawArrays(count)" ); return GL_FALSE; } if (!_mesa_valid_prim_mode(ctx, mode, "glDrawArrays")) { return GL_FALSE; } if (!check_valid_to_render(ctx, "glDrawArrays")) return GL_FALSE; if (ctx->Const.CheckArrayBounds) { if (start + count > (GLint) ctx->Array.ArrayObj->_MaxElement) return GL_FALSE; } /* From the GLES3 specification, section 2.14.2 (Transform Feedback * Primitive Capture): * * The error INVALID_OPERATION is generated by DrawArrays and * DrawArraysInstanced if recording the vertices of a primitive to the * buffer objects being used for transform feedback purposes would result * in either exceeding the limits of any buffer object’s size, or in * exceeding the end position offset + size − 1, as set by * BindBufferRange. * * This is in contrast to the behaviour of desktop GL, where the extra * primitives are silently dropped from the transform feedback buffer. */ if (_mesa_is_gles3(ctx) && _mesa_is_xfb_active_and_unpaused(ctx)) { size_t prim_count = vbo_count_tessellated_primitives(mode, count, 1); if (xfb_obj->GlesRemainingPrims < prim_count) { _mesa_error(ctx, GL_INVALID_OPERATION, "glDrawArrays(exceeds transform feedback size)"); return GL_FALSE; } xfb_obj->GlesRemainingPrims -= prim_count; } return GL_TRUE; }
static bool validate_DrawElements_common(struct gl_context *ctx, GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, const char *caller) { /* From the GLES3 specification, section 2.14.2 (Transform Feedback * Primitive Capture): * * The error INVALID_OPERATION is also generated by DrawElements, * DrawElementsInstanced, and DrawRangeElements while transform feedback * is active and not paused, regardless of mode. */ if (_mesa_is_gles3(ctx) && !ctx->Extensions.OES_geometry_shader && _mesa_is_xfb_active_and_unpaused(ctx)) { _mesa_error(ctx, GL_INVALID_OPERATION, "%s(transform feedback active)", caller); return false; } if (count < 0) { _mesa_error(ctx, GL_INVALID_VALUE, "%s(count)", caller); return false; } if (!_mesa_valid_prim_mode(ctx, mode, caller)) { return false; } if (!valid_elements_type(ctx, type, caller)) return false; if (!check_valid_to_render(ctx, caller)) return false; /* Not using a VBO for indices, so avoid NULL pointer derefs later. */ if (!_mesa_is_bufferobj(ctx->Array.VAO->IndexBufferObj) && indices == NULL) return false; if (count == 0) return false; return true; }
GLboolean _mesa_validate_DrawTransformFeedback(struct gl_context *ctx, GLenum mode, struct gl_transform_feedback_object *obj, GLuint stream, GLsizei numInstances) { FLUSH_CURRENT(ctx, 0); if (!_mesa_valid_prim_mode(ctx, mode, "glDrawTransformFeedback*(mode)")) { return GL_FALSE; } if (!obj) { _mesa_error(ctx, GL_INVALID_VALUE, "glDrawTransformFeedback*(name)"); return GL_FALSE; } if (!obj->EndedAnytime) { _mesa_error(ctx, GL_INVALID_OPERATION, "glDrawTransformFeedback*"); return GL_FALSE; } if (stream >= ctx->Const.MaxVertexStreams) { _mesa_error(ctx, GL_INVALID_VALUE, "glDrawTransformFeedbackStream*(index>=MaxVertexStream)"); return GL_FALSE; } if (numInstances <= 0) { if (numInstances < 0) _mesa_error(ctx, GL_INVALID_VALUE, "glDrawTransformFeedback*Instanced(numInstances=%d)", numInstances); return GL_FALSE; } if (!check_valid_to_render(ctx, "glDrawTransformFeedback*")) { return GL_FALSE; } return GL_TRUE; }
/** * Error checking for glMultiDrawElements(). Includes parameter checking * and VBO bounds checking. * \return GL_TRUE if OK to render, GL_FALSE if error found */ GLboolean _mesa_validate_MultiDrawElements(struct gl_context *ctx, GLenum mode, const GLsizei *count, GLenum type, const GLvoid * const *indices, GLuint primcount) { unsigned i; FLUSH_CURRENT(ctx, 0); for (i = 0; i < primcount; i++) { if (count[i] < 0) { _mesa_error(ctx, GL_INVALID_VALUE, "glMultiDrawElements(count)" ); return GL_FALSE; } } if (!_mesa_valid_prim_mode(ctx, mode, "glMultiDrawElements")) { return GL_FALSE; } if (!valid_elements_type(ctx, type, "glMultiDrawElements")) return GL_FALSE; if (!check_valid_to_render(ctx, "glMultiDrawElements")) return GL_FALSE; /* Not using a VBO for indices, so avoid NULL pointer derefs later. */ if (!_mesa_is_bufferobj(ctx->Array.VAO->IndexBufferObj)) { for (i = 0; i < primcount; i++) { if (!indices[i]) return GL_FALSE; } } return GL_TRUE; }
static GLboolean valid_draw_indirect(struct gl_context *ctx, GLenum mode, const GLvoid *indirect, GLsizei size, const char *name) { const uint64_t end = (uint64_t) (uintptr_t) indirect + size; /* OpenGL ES 3.1 spec. section 10.5: * * "DrawArraysIndirect requires that all data sourced for the * command, including the DrawArraysIndirectCommand * structure, be in buffer objects, and may not be called when * the default vertex array object is bound." */ if (ctx->Array.VAO == ctx->Array.DefaultVAO) { _mesa_error(ctx, GL_INVALID_OPERATION, "(no VAO bound)"); return GL_FALSE; } /* From OpenGL ES 3.1 spec. section 10.5: * "An INVALID_OPERATION error is generated if zero is bound to * VERTEX_ARRAY_BINDING, DRAW_INDIRECT_BUFFER or to any enabled * vertex array." * * Here we check that for each enabled vertex array we have a vertex * buffer bound. */ if (_mesa_is_gles31(ctx) && ctx->Array.VAO->_Enabled != ctx->Array.VAO->VertexAttribBufferMask) { _mesa_error(ctx, GL_INVALID_OPERATION, "%s(No VBO bound)", name); return GL_FALSE; } if (!_mesa_valid_prim_mode(ctx, mode, name)) return GL_FALSE; /* OpenGL ES 3.1 specification, section 10.5: * * "An INVALID_OPERATION error is generated if * transform feedback is active and not paused." */ if (_mesa_is_gles31(ctx) && !ctx->Extensions.OES_geometry_shader && _mesa_is_xfb_active_and_unpaused(ctx)) { _mesa_error(ctx, GL_INVALID_OPERATION, "%s(TransformFeedback is active and not paused)", name); } /* From OpenGL version 4.4. section 10.5 * and OpenGL ES 3.1, section 10.6: * * "An INVALID_VALUE error is generated if indirect is not a * multiple of the size, in basic machine units, of uint." */ if ((GLsizeiptr)indirect & (sizeof(GLuint) - 1)) { _mesa_error(ctx, GL_INVALID_VALUE, "%s(indirect is not aligned)", name); return GL_FALSE; } if (!_mesa_is_bufferobj(ctx->DrawIndirectBuffer)) { _mesa_error(ctx, GL_INVALID_OPERATION, "%s: no buffer bound to DRAW_INDIRECT_BUFFER", name); return GL_FALSE; } if (_mesa_check_disallowed_mapping(ctx->DrawIndirectBuffer)) { _mesa_error(ctx, GL_INVALID_OPERATION, "%s(DRAW_INDIRECT_BUFFER is mapped)", name); return GL_FALSE; } /* From the ARB_draw_indirect specification: * "An INVALID_OPERATION error is generated if the commands source data * beyond the end of the buffer object [...]" */ if (ctx->DrawIndirectBuffer->Size < end) { _mesa_error(ctx, GL_INVALID_OPERATION, "%s(DRAW_INDIRECT_BUFFER too small)", name); return GL_FALSE; } if (!check_valid_to_render(ctx, name)) return GL_FALSE; return GL_TRUE; }
static bool validate_draw_arrays(struct gl_context *ctx, const char *func, GLenum mode, GLsizei count, GLsizei numInstances) { struct gl_transform_feedback_object *xfb_obj = ctx->TransformFeedback.CurrentObject; FLUSH_CURRENT(ctx, 0); if (count < 0) { _mesa_error(ctx, GL_INVALID_VALUE, "%s(count)", func); return false; } if (!_mesa_valid_prim_mode(ctx, mode, func)) return false; if (!check_valid_to_render(ctx, func)) return false; /* From the GLES3 specification, section 2.14.2 (Transform Feedback * Primitive Capture): * * The error INVALID_OPERATION is generated by DrawArrays and * DrawArraysInstanced if recording the vertices of a primitive to the * buffer objects being used for transform feedback purposes would result * in either exceeding the limits of any buffer object’s size, or in * exceeding the end position offset + size − 1, as set by * BindBufferRange. * * This is in contrast to the behaviour of desktop GL, where the extra * primitives are silently dropped from the transform feedback buffer. * * This text is removed in ES 3.2, presumably because it's not really * implementable with geometry and tessellation shaders. In fact, * the OES_geometry_shader spec says: * * "(13) Does this extension change how transform feedback operates * compared to unextended OpenGL ES 3.0 or 3.1? * * RESOLVED: Yes. Because dynamic geometry amplification in a geometry * shader can make it difficult if not impossible to predict the amount * of geometry that may be generated in advance of executing the shader, * the draw-time error for transform feedback buffer overflow conditions * is removed and replaced with the GL behavior (primitives are not * written and the corresponding counter is not updated)..." */ if (_mesa_is_gles3(ctx) && _mesa_is_xfb_active_and_unpaused(ctx) && !_mesa_has_OES_geometry_shader(ctx) && !_mesa_has_OES_tessellation_shader(ctx)) { size_t prim_count = vbo_count_tessellated_primitives(mode, count, 1); if (xfb_obj->GlesRemainingPrims < prim_count) { _mesa_error(ctx, GL_INVALID_OPERATION, "%s(exceeds transform feedback size)", func); return false; } xfb_obj->GlesRemainingPrims -= prim_count; } if (count == 0) return false; return true; }
static bool validate_DrawElements_common(struct gl_context *ctx, GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, const char *caller) { /* Section 2.14.2 (Transform Feedback Primitive Capture) of the OpenGL ES * 3.1 spec says: * * The error INVALID_OPERATION is also generated by DrawElements, * DrawElementsInstanced, and DrawRangeElements while transform feedback * is active and not paused, regardless of mode. * * The OES_geometry_shader_spec says: * * Issues: * * ... * * (13) Does this extension change how transform feedback operates * compared to unextended OpenGL ES 3.0 or 3.1? * * RESOLVED: Yes... Since we no longer require being able to predict how * much geometry will be generated, we also lift the restriction that * only DrawArray* commands are supported and also support the * DrawElements* commands for transform feedback. * * This should also be reflected in the body of the spec, but that appears * to have been overlooked. The body of the spec only explicitly allows * the indirect versions. */ if (_mesa_is_gles3(ctx) && !ctx->Extensions.OES_geometry_shader && _mesa_is_xfb_active_and_unpaused(ctx)) { _mesa_error(ctx, GL_INVALID_OPERATION, "%s(transform feedback active)", caller); return false; } if (count < 0) { _mesa_error(ctx, GL_INVALID_VALUE, "%s(count)", caller); return false; } if (!_mesa_valid_prim_mode(ctx, mode, caller)) { return false; } if (!valid_elements_type(ctx, type, caller)) return false; if (!check_valid_to_render(ctx, caller)) return false; /* Not using a VBO for indices, so avoid NULL pointer derefs later. */ if (!_mesa_is_bufferobj(ctx->Array.VAO->IndexBufferObj) && indices == NULL) return false; if (count == 0) return false; return true; }