/************************************************************************** * * Copyright 2003 VMware, Inc. * Copyright 2009 VMware, Inc. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sub license, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial portions * of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * **************************************************************************/ #include #include "arrayobj.h" #include "util/glheader.h" #include "c99_alloca.h" #include "context.h" #include "state.h" #include "draw.h" #include "draw_validate.h" #include "dispatch.h" #include "varray.h" #include "bufferobj.h" #include "enums.h" #include "macros.h" #include "transformfeedback.h" #include "pipe/p_state.h" #include "api_exec_decl.h" #include "glthread_marshal.h" #include "cso_cache/cso_context.h" #include "state_tracker/st_context.h" #include "state_tracker/st_draw.h" #include "util/u_draw.h" #include "util/u_threaded_context.h" typedef struct { GLuint count; GLuint primCount; GLuint first; GLuint baseInstance; } DrawArraysIndirectCommand; typedef struct { GLuint count; GLuint primCount; GLuint firstIndex; GLint baseVertex; GLuint baseInstance; } DrawElementsIndirectCommand; /** * Want to figure out which fragment program inputs are actually * constant/current values from ctx->Current. These should be * referenced as a tracked state variable rather than a fragment * program input, to save the overhead of putting a constant value in * every submitted vertex, transferring it to hardware, interpolating * it across the triangle, etc... * * When there is a VP bound, just use vp->outputs. But when we're * generating vp from fixed function state, basically want to * calculate: * * vp_out_2_fp_in( vp_in_2_vp_out( varying_inputs ) | * potential_vp_outputs ) * * Where potential_vp_outputs is calculated by looking at enabled * texgen, etc. * * The generated fragment program should then only declare inputs that * may vary or otherwise differ from the ctx->Current values. * Otherwise, the fp should track them as state values instead. */ void _mesa_set_varying_vp_inputs(struct gl_context *ctx, GLbitfield varying_inputs) { if (ctx->VertexProgram._VPModeOptimizesConstantAttribs && ctx->VertexProgram._VaryingInputs != varying_inputs) { ctx->VertexProgram._VaryingInputs = varying_inputs; ctx->NewState |= _NEW_FF_VERT_PROGRAM | _NEW_FF_FRAG_PROGRAM; } } /** * Set the _DrawVAO and the net enabled arrays. * The vao->_Enabled bitmask is transformed due to position/generic0 * as stored in vao->_AttributeMapMode. Then the filter bitmask is applied * to filter out arrays unwanted for the currently executed draw operation. * For example, the generic attributes are masked out form the _DrawVAO's * enabled arrays when a fixed function array draw is executed. */ void _mesa_set_draw_vao(struct gl_context *ctx, struct gl_vertex_array_object *vao) { struct gl_vertex_array_object **ptr = &ctx->Array._DrawVAO; if (*ptr != vao) { _mesa_reference_vao_(ctx, ptr, vao); _mesa_update_edgeflag_state_vao(ctx); ctx->NewDriverState |= ST_NEW_VERTEX_ARRAYS; ctx->Array.NewVertexElements = true; } } /** * Other than setting the new VAO, this returns a VAO reference to * the previously-bound VAO and the previous _VPModeInputFilter value through * parameters. The caller must call _mesa_restore_draw_vao to ensure * reference counting is done properly and the affected states are restored. * * \param ctx GL context * \param vao VAO to set. * \param vp_input_filter Mask of enabled vertex attribs. * Possible values that can also be OR'd with each other: * - VERT_BIT_FF_ALL * - VERT_BIT_MAT_ALL * - VERT_BIT_ALL * - VERT_BIT_SELECT_RESULT_OFFSET * \param old_vao Previous bound VAO. * \param old_vp_input_filter Previous value of vp_input_filter. */ void _mesa_save_and_set_draw_vao(struct gl_context *ctx, struct gl_vertex_array_object *vao, GLbitfield vp_input_filter, struct gl_vertex_array_object **old_vao, GLbitfield *old_vp_input_filter) { *old_vao = ctx->Array._DrawVAO; *old_vp_input_filter = ctx->VertexProgram._VPModeInputFilter; ctx->Array._DrawVAO = NULL; ctx->VertexProgram._VPModeInputFilter = vp_input_filter; _mesa_set_draw_vao(ctx, vao); } void _mesa_restore_draw_vao(struct gl_context *ctx, struct gl_vertex_array_object *saved, GLbitfield saved_vp_input_filter) { /* Restore states. */ _mesa_reference_vao(ctx, &ctx->Array._DrawVAO, NULL); ctx->Array._DrawVAO = saved; ctx->VertexProgram._VPModeInputFilter = saved_vp_input_filter; /* Update states. */ ctx->NewDriverState |= ST_NEW_VERTEX_ARRAYS; ctx->Array.NewVertexElements = true; /* Restore original states. */ _mesa_update_edgeflag_state_vao(ctx); } /** * Is 'mode' a valid value for glBegin(), glDrawArrays(), glDrawElements(), * etc? Also, do additional checking related to transformation feedback. * Note: this function cannot be called during glNewList(GL_COMPILE) because * this code depends on current transform feedback state. * Also, do additional checking related to tessellation shaders. */ static GLenum valid_prim_mode_custom(struct gl_context *ctx, GLenum mode, GLbitfield valid_prim_mask) { #if MESA_DEBUG ASSERTED unsigned mask = ctx->ValidPrimMask; ASSERTED unsigned mask_indexed = ctx->ValidPrimMaskIndexed; ASSERTED bool drawpix_valid = ctx->DrawPixValid; _mesa_update_valid_to_render_state(ctx); assert(mask == ctx->ValidPrimMask && mask_indexed == ctx->ValidPrimMaskIndexed && drawpix_valid == ctx->DrawPixValid); #endif /* All primitive type enums are less than 32, so we can use the shift. */ if (mode >= 32 || !((1u << mode) & valid_prim_mask)) { /* If the primitive type is not in SupportedPrimMask, set GL_INVALID_ENUM, * else set DrawGLError (e.g. GL_INVALID_OPERATION). */ return mode >= 32 || !((1u << mode) & ctx->SupportedPrimMask) ? GL_INVALID_ENUM : ctx->DrawGLError; } return GL_NO_ERROR; } GLenum _mesa_valid_prim_mode(struct gl_context *ctx, GLenum mode) { return valid_prim_mode_custom(ctx, mode, ctx->ValidPrimMask); } static GLenum valid_prim_mode_indexed(struct gl_context *ctx, GLenum mode) { return valid_prim_mode_custom(ctx, mode, ctx->ValidPrimMaskIndexed); } /** * Verify that the element type is valid. * * Generates \c GL_INVALID_ENUM and returns \c false if it is not. */ static GLenum valid_elements_type(struct gl_context *ctx, GLenum type) { return _mesa_is_index_type_valid(type) ? GL_NO_ERROR : GL_INVALID_ENUM; } static inline bool indices_aligned(unsigned index_size_shift, const GLvoid *indices) { /* Require that indices are aligned to the element size. GL doesn't specify * an error for this, but the ES 3.0 spec says: * * "Clients must align data elements consistently with the requirements * of the client platform, with an additional base-level requirement * that an offset within a buffer to a datum comprising N basic machine * units be a multiple of N" * * This is only required by index buffers, not user indices. */ return ((uintptr_t)indices & ((1 << index_size_shift) - 1)) == 0; } static GLenum validate_DrawElements_common(struct gl_context *ctx, GLenum mode, GLsizei count, GLsizei numInstances, GLenum type) { if (count < 0 || numInstances < 0) return GL_INVALID_VALUE; GLenum error = valid_prim_mode_indexed(ctx, mode); if (error) return error; return valid_elements_type(ctx, type); } /** * Error checking for glDrawElements(). Includes parameter checking * and VBO bounds checking. * \return GL_TRUE if OK to render, GL_FALSE if error found */ static GLboolean _mesa_validate_DrawElements(struct gl_context *ctx, GLenum mode, GLsizei count, GLenum type) { GLenum error = validate_DrawElements_common(ctx, mode, count, 1, type); if (error) _mesa_error(ctx, error, "glDrawElements"); return !error; } /** * Error checking for glMultiDrawElements(). Includes parameter checking * and VBO bounds checking. * \return GL_TRUE if OK to render, GL_FALSE if error found */ static GLboolean _mesa_validate_MultiDrawElements(struct gl_context *ctx, GLenum mode, const GLsizei *count, GLenum type, const GLvoid * const *indices, GLsizei primcount, struct gl_buffer_object *index_bo) { GLenum error; /* * Section 2.3.1 (Errors) of the OpenGL 4.5 (Core Profile) spec says: * * "If a negative number is provided where an argument of type sizei or * sizeiptr is specified, an INVALID_VALUE error is generated." * * and in the same section: * * "In other cases, there are no side effects unless otherwise noted; * the command which generates the error is ignored so that it has no * effect on GL state or framebuffer contents." * * Hence, check both primcount and all the count[i]. */ if (primcount < 0) { error = GL_INVALID_VALUE; } else { error = valid_prim_mode_indexed(ctx, mode); if (!error) { error = valid_elements_type(ctx, type); if (!error) { for (int i = 0; i < primcount; i++) { if (count[i] < 0) { error = GL_INVALID_VALUE; break; } } } } } if (error) _mesa_error(ctx, error, "glMultiDrawElements"); /* Not using a VBO for indices, so avoid NULL pointer derefs later. */ if (!index_bo) { for (int i = 0; i < primcount; i++) { if (!indices[i]) return GL_FALSE; } } return !error; } /** * Error checking for glDrawRangeElements(). Includes parameter checking * and VBO bounds checking. * \return GL_TRUE if OK to render, GL_FALSE if error found */ static GLboolean _mesa_validate_DrawRangeElements(struct gl_context *ctx, GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type) { GLenum error; if (end < start) { error = GL_INVALID_VALUE; } else { error = validate_DrawElements_common(ctx, mode, count, 1, type); } if (error) _mesa_error(ctx, error, "glDrawRangeElements"); return !error; } static bool need_xfb_remaining_prims_check(const struct gl_context *ctx) { /* 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)..." */ return _mesa_is_gles3(ctx) && _mesa_is_xfb_active_and_unpaused(ctx) && !_mesa_has_OES_geometry_shader(ctx) && !_mesa_has_OES_tessellation_shader(ctx); } /** * Figure out the number of transform feedback primitives that will be output * considering the drawing mode, number of vertices, and instance count, * assuming that no geometry shading is done and primitive restart is not * used. * * This is used by driver back-ends in implementing the PRIMITIVES_GENERATED * and TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN queries. It is also used to * pre-validate draw calls in GLES3 (where draw calls only succeed if there is * enough room in the transform feedback buffer for the result). */ static size_t count_tessellated_primitives(GLenum mode, GLuint count, GLuint num_instances) { size_t num_primitives; switch (mode) { case GL_POINTS: num_primitives = count; break; case GL_LINE_STRIP: num_primitives = count >= 2 ? count - 1 : 0; break; case GL_LINE_LOOP: num_primitives = count >= 2 ? count : 0; break; case GL_LINES: num_primitives = count / 2; break; case GL_TRIANGLE_STRIP: case GL_TRIANGLE_FAN: case GL_POLYGON: num_primitives = count >= 3 ? count - 2 : 0; break; case GL_TRIANGLES: num_primitives = count / 3; break; case GL_QUAD_STRIP: num_primitives = count >= 4 ? ((count / 2) - 1) * 2 : 0; break; case GL_QUADS: num_primitives = (count / 4) * 2; break; case GL_LINES_ADJACENCY: num_primitives = count / 4; break; case GL_LINE_STRIP_ADJACENCY: num_primitives = count >= 4 ? count - 3 : 0; break; case GL_TRIANGLES_ADJACENCY: num_primitives = count / 6; break; case GL_TRIANGLE_STRIP_ADJACENCY: num_primitives = count >= 6 ? (count - 4) / 2 : 0; break; default: assert(!"Unexpected primitive type in count_tessellated_primitives"); num_primitives = 0; break; } return num_primitives * num_instances; } static GLenum validate_draw_arrays(struct gl_context *ctx, GLenum mode, GLsizei count, GLsizei numInstances) { if (count < 0 || numInstances < 0) return GL_INVALID_VALUE; GLenum error = _mesa_valid_prim_mode(ctx, mode); if (error) return error; if (need_xfb_remaining_prims_check(ctx)) { struct gl_transform_feedback_object *xfb_obj = ctx->TransformFeedback.CurrentObject; size_t prim_count = count_tessellated_primitives(mode, count, numInstances); if (xfb_obj->GlesRemainingPrims < prim_count) return GL_INVALID_OPERATION; xfb_obj->GlesRemainingPrims -= prim_count; } return GL_NO_ERROR; } /** * 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 */ static GLboolean _mesa_validate_DrawArrays(struct gl_context *ctx, GLenum mode, GLsizei count) { GLenum error = validate_draw_arrays(ctx, mode, count, 1); if (error) _mesa_error(ctx, error, "glDrawArrays"); return !error; } static GLboolean _mesa_validate_DrawArraysInstanced(struct gl_context *ctx, GLenum mode, GLint first, GLsizei count, GLsizei numInstances) { GLenum error; if (first < 0) { error = GL_INVALID_VALUE; } else { error = validate_draw_arrays(ctx, mode, count, numInstances); } if (error) _mesa_error(ctx, error, "glDrawArraysInstanced"); return !error; } /** * Called to error check the function parameters. * * Note that glMultiDrawArrays is not part of GLES, so there's limited scope * for sharing code with the validation of glDrawArrays. */ static bool _mesa_validate_MultiDrawArrays(struct gl_context *ctx, GLenum mode, const GLsizei *count, GLsizei primcount) { GLenum error; if (primcount < 0) { error = GL_INVALID_VALUE; } else { error = _mesa_valid_prim_mode(ctx, mode); if (!error) { for (int i = 0; i < primcount; ++i) { if (count[i] < 0) { error = GL_INVALID_VALUE; break; } } if (!error) { if (need_xfb_remaining_prims_check(ctx)) { struct gl_transform_feedback_object *xfb_obj = ctx->TransformFeedback.CurrentObject; size_t xfb_prim_count = 0; for (int i = 0; i < primcount; ++i) { xfb_prim_count += count_tessellated_primitives(mode, count[i], 1); } if (xfb_obj->GlesRemainingPrims < xfb_prim_count) { error = GL_INVALID_OPERATION; } else { xfb_obj->GlesRemainingPrims -= xfb_prim_count; } } } } } if (error) _mesa_error(ctx, error, "glMultiDrawArrays"); return !error; } static GLboolean _mesa_validate_DrawElementsInstanced(struct gl_context *ctx, GLenum mode, GLsizei count, GLenum type, GLsizei numInstances) { GLenum error = validate_DrawElements_common(ctx, mode, count, numInstances, type); if (error) _mesa_error(ctx, error, "glDrawElementsInstanced"); return !error; } static GLboolean _mesa_validate_DrawTransformFeedback(struct gl_context *ctx, GLenum mode, struct gl_transform_feedback_object *obj, GLuint stream, GLsizei numInstances) { GLenum error; /* 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 || !obj->EverBound || stream >= ctx->Const.MaxVertexStreams || numInstances < 0) { error = GL_INVALID_VALUE; } else { error = _mesa_valid_prim_mode(ctx, mode); if (!error) { if (!obj->EndedAnytime) error = GL_INVALID_OPERATION; } } if (error) _mesa_error(ctx, error, "glDrawTransformFeedback*"); return !error; } static GLenum valid_draw_indirect(struct gl_context *ctx, GLenum mode, const GLvoid *indirect, GLsizei size) { 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->API != API_OPENGL_COMPAT && ctx->Array.VAO == ctx->Array.DefaultVAO) return GL_INVALID_OPERATION; /* 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) return GL_INVALID_OPERATION; GLenum error = _mesa_valid_prim_mode(ctx, mode); if (error) return error; /* OpenGL ES 3.1 specification, section 10.5: * * "An INVALID_OPERATION error is generated if * transform feedback is active and not paused." * * The OES_geometry_shader spec says: * * On p. 250 in the errors section for the DrawArraysIndirect command, * and on p. 254 in the errors section for the DrawElementsIndirect * command, delete the errors which state: * * "An INVALID_OPERATION error is generated if transform feedback is * active and not paused." * * (thus allowing transform feedback to work with indirect draw commands). */ if (_mesa_is_gles31(ctx) && !ctx->Extensions.OES_geometry_shader && _mesa_is_xfb_active_and_unpaused(ctx)) return GL_INVALID_OPERATION; /* 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)) return GL_INVALID_VALUE; if (!ctx->DrawIndirectBuffer) return GL_INVALID_OPERATION; if (_mesa_check_disallowed_mapping(ctx->DrawIndirectBuffer)) return GL_INVALID_OPERATION; /* 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) return GL_INVALID_OPERATION; return GL_NO_ERROR; } static inline GLenum valid_draw_indirect_elements(struct gl_context *ctx, GLenum mode, GLenum type, const GLvoid *indirect, GLsizeiptr size) { GLenum error = valid_elements_type(ctx, type); if (error) return error; /* * Unlike regular DrawElementsInstancedBaseVertex commands, the indices * may not come from a client array and must come from an index buffer. * If no element array buffer is bound, an INVALID_OPERATION error is * generated. */ if (!ctx->Array.VAO->IndexBufferObj) return GL_INVALID_OPERATION; return valid_draw_indirect(ctx, mode, indirect, size); } static GLboolean _mesa_valid_draw_indirect_multi(struct gl_context *ctx, GLsizei primcount, GLsizei stride, const char *name) { /* From the ARB_multi_draw_indirect specification: * "INVALID_VALUE is generated by MultiDrawArraysIndirect or * MultiDrawElementsIndirect if is negative." * * " must be positive, otherwise an INVALID_VALUE error will * be generated." */ if (primcount < 0) { _mesa_error(ctx, GL_INVALID_VALUE, "%s(primcount < 0)", name); return GL_FALSE; } /* From the ARB_multi_draw_indirect specification: * " must be a multiple of four, otherwise an INVALID_VALUE * error is generated." */ if (stride % 4) { _mesa_error(ctx, GL_INVALID_VALUE, "%s(stride %% 4)", name); return GL_FALSE; } return GL_TRUE; } static GLboolean _mesa_validate_DrawArraysIndirect(struct gl_context *ctx, GLenum mode, const GLvoid *indirect) { const unsigned drawArraysNumParams = 4; GLenum error = valid_draw_indirect(ctx, mode, indirect, drawArraysNumParams * sizeof(GLuint)); if (error) _mesa_error(ctx, error, "glDrawArraysIndirect"); return !error; } static GLboolean _mesa_validate_DrawElementsIndirect(struct gl_context *ctx, GLenum mode, GLenum type, const GLvoid *indirect) { const unsigned drawElementsNumParams = 5; GLenum error = valid_draw_indirect_elements(ctx, mode, type, indirect, drawElementsNumParams * sizeof(GLuint)); if (error) _mesa_error(ctx, error, "glDrawElementsIndirect"); return !error; } static GLboolean _mesa_validate_MultiDrawArraysIndirect(struct gl_context *ctx, GLenum mode, const GLvoid *indirect, GLsizei primcount, GLsizei stride) { GLsizeiptr size = 0; const unsigned drawArraysNumParams = 4; /* caller has converted stride==0 to drawArraysNumParams * sizeof(GLuint) */ assert(stride != 0); if (!_mesa_valid_draw_indirect_multi(ctx, primcount, stride, "glMultiDrawArraysIndirect")) return GL_FALSE; /* number of bytes of the indirect buffer which will be read */ size = primcount ? (primcount - 1) * stride + drawArraysNumParams * sizeof(GLuint) : 0; GLenum error = valid_draw_indirect(ctx, mode, indirect, size); if (error) _mesa_error(ctx, error, "glMultiDrawArraysIndirect"); return !error; } static GLboolean _mesa_validate_MultiDrawElementsIndirect(struct gl_context *ctx, GLenum mode, GLenum type, const GLvoid *indirect, GLsizei primcount, GLsizei stride) { GLsizeiptr size = 0; const unsigned drawElementsNumParams = 5; /* caller has converted stride==0 to drawElementsNumParams * sizeof(GLuint) */ assert(stride != 0); if (!_mesa_valid_draw_indirect_multi(ctx, primcount, stride, "glMultiDrawElementsIndirect")) return GL_FALSE; /* number of bytes of the indirect buffer which will be read */ size = primcount ? (primcount - 1) * stride + drawElementsNumParams * sizeof(GLuint) : 0; GLenum error = valid_draw_indirect_elements(ctx, mode, type, indirect, size); if (error) _mesa_error(ctx, error, "glMultiDrawElementsIndirect"); return !error; } static GLenum valid_draw_indirect_parameters(struct gl_context *ctx, GLintptr drawcount) { /* From the ARB_indirect_parameters specification: * "INVALID_VALUE is generated by MultiDrawArraysIndirectCountARB or * MultiDrawElementsIndirectCountARB if is not a multiple of * four." */ if (drawcount & 3) return GL_INVALID_VALUE; /* From the ARB_indirect_parameters specification: * "INVALID_OPERATION is generated by MultiDrawArraysIndirectCountARB or * MultiDrawElementsIndirectCountARB if no buffer is bound to the * PARAMETER_BUFFER_ARB binding point." */ if (!ctx->ParameterBuffer) return GL_INVALID_OPERATION; if (_mesa_check_disallowed_mapping(ctx->ParameterBuffer)) return GL_INVALID_OPERATION; /* From the ARB_indirect_parameters specification: * "INVALID_OPERATION is generated by MultiDrawArraysIndirectCountARB or * MultiDrawElementsIndirectCountARB if reading a typed value * from the buffer bound to the PARAMETER_BUFFER_ARB target at the offset * specified by would result in an out-of-bounds access." */ if (ctx->ParameterBuffer->Size < drawcount + sizeof(GLsizei)) return GL_INVALID_OPERATION; return GL_NO_ERROR; } static GLboolean _mesa_validate_MultiDrawArraysIndirectCount(struct gl_context *ctx, GLenum mode, GLintptr indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride) { GLsizeiptr size = 0; const unsigned drawArraysNumParams = 4; /* caller has converted stride==0 to drawArraysNumParams * sizeof(GLuint) */ assert(stride != 0); if (!_mesa_valid_draw_indirect_multi(ctx, maxdrawcount, stride, "glMultiDrawArraysIndirectCountARB")) return GL_FALSE; /* number of bytes of the indirect buffer which will be read */ size = maxdrawcount ? (maxdrawcount - 1) * stride + drawArraysNumParams * sizeof(GLuint) : 0; GLenum error = valid_draw_indirect(ctx, mode, (void *)indirect, size); if (!error) error = valid_draw_indirect_parameters(ctx, drawcount); if (error) _mesa_error(ctx, error, "glMultiDrawArraysIndirectCountARB"); return !error; } static GLboolean _mesa_validate_MultiDrawElementsIndirectCount(struct gl_context *ctx, GLenum mode, GLenum type, GLintptr indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride) { GLsizeiptr size = 0; const unsigned drawElementsNumParams = 5; /* caller has converted stride==0 to drawElementsNumParams * sizeof(GLuint) */ assert(stride != 0); if (!_mesa_valid_draw_indirect_multi(ctx, maxdrawcount, stride, "glMultiDrawElementsIndirectCountARB")) return GL_FALSE; /* number of bytes of the indirect buffer which will be read */ size = maxdrawcount ? (maxdrawcount - 1) * stride + drawElementsNumParams * sizeof(GLuint) : 0; GLenum error = valid_draw_indirect_elements(ctx, mode, type, (void *)indirect, size); if (!error) error = valid_draw_indirect_parameters(ctx, drawcount); if (error) _mesa_error(ctx, error, "glMultiDrawElementsIndirectCountARB"); return !error; } static inline struct pipe_draw_start_count_bias * get_temp_draws(struct gl_context *ctx, unsigned primcount) { if (primcount > ctx->num_tmp_draws) { struct pipe_draw_start_count_bias *tmp = realloc(ctx->tmp_draws, primcount * sizeof(ctx->tmp_draws[0])); if (tmp) { ctx->tmp_draws = tmp; ctx->num_tmp_draws = primcount; } else { _mesa_error(ctx, GL_OUT_OF_MEMORY, "can't alloc tmp_draws"); free(ctx->tmp_draws); /* realloc doesn't free on failure */ ctx->tmp_draws = NULL; ctx->num_tmp_draws = 0; } } return ctx->tmp_draws; } /** * Check that element 'j' of the array has reasonable data. * Map VBO if needed. * For debugging purposes; not normally used. */ static void check_array_data(struct gl_context *ctx, struct gl_vertex_array_object *vao, GLuint attrib, GLuint j) { const struct gl_array_attributes *array = &vao->VertexAttrib[attrib]; if (vao->Enabled & VERT_BIT(attrib)) { const struct gl_vertex_buffer_binding *binding = &vao->BufferBinding[array->BufferBindingIndex]; struct gl_buffer_object *bo = binding->BufferObj; const void *data = array->Ptr; if (bo) { data = ADD_POINTERS(_mesa_vertex_attrib_address(array, binding), bo->Mappings[MAP_INTERNAL].Pointer); } switch (array->Format.User.Type) { case GL_FLOAT: { GLfloat *f = (GLfloat *) ((GLubyte *) data + binding->Stride * j); GLint k; for (k = 0; k < array->Format.User.Size; k++) { if (util_is_inf_or_nan(f[k]) || f[k] >= 1.0e20F || f[k] <= -1.0e10F) { printf("Bad array data:\n"); printf(" Element[%u].%u = %f\n", j, k, f[k]); printf(" Array %u at %p\n", attrib, (void *) array); printf(" Type 0x%x, Size %d, Stride %d\n", array->Format.User.Type, array->Format.User.Size, binding->Stride); printf(" Address/offset %p in Buffer Object %u\n", array->Ptr, bo ? bo->Name : 0); f[k] = 1.0F; /* XXX replace the bad value! */ } /*assert(!util_is_inf_or_nan(f[k])); */ } } break; default: ; } } } /** * Examine the array's data for NaNs, etc. * For debug purposes; not normally used. */ static void check_draw_elements_data(struct gl_context *ctx, GLsizei count, GLenum elemType, const void *elements, GLint basevertex) { struct gl_vertex_array_object *vao = ctx->Array.VAO; GLint i; GLuint k; _mesa_vao_map(ctx, vao, GL_MAP_READ_BIT); if (vao->IndexBufferObj) elements = ADD_POINTERS(vao->IndexBufferObj->Mappings[MAP_INTERNAL].Pointer, elements); for (i = 0; i < count; i++) { GLuint j; /* j = element[i] */ switch (elemType) { case GL_UNSIGNED_BYTE: j = ((const GLubyte *) elements)[i]; break; case GL_UNSIGNED_SHORT: j = ((const GLushort *) elements)[i]; break; case GL_UNSIGNED_INT: j = ((const GLuint *) elements)[i]; break; default: unreachable("Unexpected index buffer type"); } /* check element j of each enabled array */ for (k = 0; k < VERT_ATTRIB_MAX; k++) { check_array_data(ctx, vao, k, j); } } _mesa_vao_unmap(ctx, vao); } /** * Check array data, looking for NaNs, etc. */ static void check_draw_arrays_data(struct gl_context *ctx, GLint start, GLsizei count) { /* TO DO */ } /** * Print info/data for glDrawArrays(), for debugging. */ static void print_draw_arrays(struct gl_context *ctx, GLenum mode, GLint start, GLsizei count) { struct gl_vertex_array_object *vao = ctx->Array.VAO; printf("_mesa_DrawArrays(mode 0x%x, start %d, count %d):\n", mode, start, count); _mesa_vao_map_arrays(ctx, vao, GL_MAP_READ_BIT); GLbitfield mask = vao->Enabled; while (mask) { const gl_vert_attrib i = u_bit_scan(&mask); const struct gl_array_attributes *array = &vao->VertexAttrib[i]; const struct gl_vertex_buffer_binding *binding = &vao->BufferBinding[array->BufferBindingIndex]; struct gl_buffer_object *bufObj = binding->BufferObj; printf("attr %s: size %d stride %d " "ptr %p Bufobj %u\n", gl_vert_attrib_name((gl_vert_attrib) i), array->Format.User.Size, binding->Stride, array->Ptr, bufObj ? bufObj->Name : 0); if (bufObj) { GLubyte *p = bufObj->Mappings[MAP_INTERNAL].Pointer; int offset = (int) (GLintptr) _mesa_vertex_attrib_address(array, binding); unsigned multiplier; switch (array->Format.User.Type) { case GL_DOUBLE: case GL_INT64_ARB: case GL_UNSIGNED_INT64_ARB: multiplier = 2; break; default: multiplier = 1; } float *f = (float *) (p + offset); int *k = (int *) f; int i = 0; int n = (count - 1) * (binding->Stride / (4 * multiplier)) + array->Format.User.Size; if (n > 32) n = 32; printf(" Data at offset %d:\n", offset); do { if (multiplier == 2) printf(" double[%d] = 0x%016llx %lf\n", i, ((unsigned long long *) k)[i], ((double *) f)[i]); else printf(" float[%d] = 0x%08x %f\n", i, k[i], f[i]); i++; } while (i < n); } } _mesa_vao_unmap_arrays(ctx, vao); } /** * Helper function called by the other DrawArrays() functions below. * This is where we handle primitive restart for drawing non-indexed * arrays. If primitive restart is enabled, it typically means * splitting one DrawArrays() into two. */ static void _mesa_draw_arrays(struct gl_context *ctx, GLenum mode, GLint start, GLsizei count, GLuint numInstances, GLuint baseInstance) { /* Viewperf has many draws with count=0. Discarding them is faster than * processing them. */ if (!count || !numInstances) return; /* OpenGL 4.5 says that primitive restart is ignored with non-indexed * draws. */ struct pipe_draw_info info; struct pipe_draw_start_count_bias draw; info.mode = mode; info.index_size = 0; /* Packed section begin. */ info.primitive_restart = false; info.has_user_indices = false; info.index_bounds_valid = true; info.increment_draw_id = false; info.was_line_loop = false; info.take_index_buffer_ownership = false; info.index_bias_varies = false; /* Packed section end. */ info.start_instance = baseInstance; info.instance_count = numInstances; info.view_mask = 0; info.min_index = start; info.max_index = start + count - 1; draw.start = start; draw.count = count; st_prepare_draw(ctx, ST_PIPELINE_RENDER_STATE_MASK); ctx->Driver.DrawGallium(ctx, &info, ctx->DrawID, NULL, &draw, 1); if (MESA_DEBUG_FLAGS & DEBUG_ALWAYS_FLUSH) { _mesa_flush(ctx); } } /** * Execute a glRectf() function. */ void GLAPIENTRY _mesa_Rectf(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2) { GET_CURRENT_CONTEXT(ctx); ASSERT_OUTSIDE_BEGIN_END(ctx); CALL_Begin(ctx->Dispatch.Current, (GL_QUADS)); /* Begin can change Dispatch.Current. */ struct _glapi_table *dispatch = ctx->Dispatch.Current; CALL_Vertex2f(dispatch, (x1, y1)); CALL_Vertex2f(dispatch, (x2, y1)); CALL_Vertex2f(dispatch, (x2, y2)); CALL_Vertex2f(dispatch, (x1, y2)); CALL_End(dispatch, ()); } void GLAPIENTRY _mesa_Rectd(GLdouble x1, GLdouble y1, GLdouble x2, GLdouble y2) { _mesa_Rectf((GLfloat) x1, (GLfloat) y1, (GLfloat) x2, (GLfloat) y2); } void GLAPIENTRY _mesa_Rectdv(const GLdouble *v1, const GLdouble *v2) { _mesa_Rectf((GLfloat) v1[0], (GLfloat) v1[1], (GLfloat) v2[0], (GLfloat) v2[1]); } void GLAPIENTRY _mesa_Rectfv(const GLfloat *v1, const GLfloat *v2) { _mesa_Rectf(v1[0], v1[1], v2[0], v2[1]); } void GLAPIENTRY _mesa_Recti(GLint x1, GLint y1, GLint x2, GLint y2) { _mesa_Rectf((GLfloat) x1, (GLfloat) y1, (GLfloat) x2, (GLfloat) y2); } void GLAPIENTRY _mesa_Rectiv(const GLint *v1, const GLint *v2) { _mesa_Rectf((GLfloat) v1[0], (GLfloat) v1[1], (GLfloat) v2[0], (GLfloat) v2[1]); } void GLAPIENTRY _mesa_Rects(GLshort x1, GLshort y1, GLshort x2, GLshort y2) { _mesa_Rectf((GLfloat) x1, (GLfloat) y1, (GLfloat) x2, (GLfloat) y2); } void GLAPIENTRY _mesa_Rectsv(const GLshort *v1, const GLshort *v2) { _mesa_Rectf((GLfloat) v1[0], (GLfloat) v1[1], (GLfloat) v2[0], (GLfloat) v2[1]); } void GLAPIENTRY _mesa_EvalMesh1(GLenum mode, GLint i1, GLint i2) { GET_CURRENT_CONTEXT(ctx); GLint i; GLfloat u, du; GLenum prim; switch (mode) { case GL_POINT: prim = GL_POINTS; break; case GL_LINE: prim = GL_LINE_STRIP; break; default: _mesa_error(ctx, GL_INVALID_ENUM, "glEvalMesh1(mode)"); return; } /* No effect if vertex maps disabled. */ if (!ctx->Eval.Map1Vertex4 && !ctx->Eval.Map1Vertex3) return; du = ctx->Eval.MapGrid1du; u = ctx->Eval.MapGrid1u1 + i1 * du; CALL_Begin(ctx->Dispatch.Current, (prim)); /* Begin can change Dispatch.Current. */ struct _glapi_table *dispatch = ctx->Dispatch.Current; for (i = i1; i <= i2; i++, u += du) { CALL_EvalCoord1f(dispatch, (u)); } CALL_End(dispatch, ()); } void GLAPIENTRY _mesa_EvalMesh2(GLenum mode, GLint i1, GLint i2, GLint j1, GLint j2) { GET_CURRENT_CONTEXT(ctx); GLfloat u, du, v, dv, v1, u1; GLint i, j; switch (mode) { case GL_POINT: case GL_LINE: case GL_FILL: break; default: _mesa_error(ctx, GL_INVALID_ENUM, "glEvalMesh2(mode)"); return; } /* No effect if vertex maps disabled. */ if (!ctx->Eval.Map2Vertex4 && !ctx->Eval.Map2Vertex3) return; du = ctx->Eval.MapGrid2du; dv = ctx->Eval.MapGrid2dv; v1 = ctx->Eval.MapGrid2v1 + j1 * dv; u1 = ctx->Eval.MapGrid2u1 + i1 * du; struct _glapi_table *dispatch; switch (mode) { case GL_POINT: CALL_Begin(ctx->Dispatch.Current, (GL_POINTS)); /* Begin can change Dispatch.Current. */ dispatch = ctx->Dispatch.Current; for (v = v1, j = j1; j <= j2; j++, v += dv) { for (u = u1, i = i1; i <= i2; i++, u += du) { CALL_EvalCoord2f(dispatch, (u, v)); } } CALL_End(dispatch, ()); break; case GL_LINE: for (v = v1, j = j1; j <= j2; j++, v += dv) { CALL_Begin(ctx->Dispatch.Current, (GL_LINE_STRIP)); /* Begin can change Dispatch.Current. */ dispatch = ctx->Dispatch.Current; for (u = u1, i = i1; i <= i2; i++, u += du) { CALL_EvalCoord2f(dispatch, (u, v)); } CALL_End(dispatch, ()); } for (u = u1, i = i1; i <= i2; i++, u += du) { CALL_Begin(ctx->Dispatch.Current, (GL_LINE_STRIP)); /* Begin can change Dispatch.Current. */ dispatch = ctx->Dispatch.Current; for (v = v1, j = j1; j <= j2; j++, v += dv) { CALL_EvalCoord2f(dispatch, (u, v)); } CALL_End(dispatch, ()); } break; case GL_FILL: for (v = v1, j = j1; j < j2; j++, v += dv) { CALL_Begin(ctx->Dispatch.Current, (GL_TRIANGLE_STRIP)); /* Begin can change Dispatch.Current. */ dispatch = ctx->Dispatch.Current; for (u = u1, i = i1; i <= i2; i++, u += du) { CALL_EvalCoord2f(dispatch, (u, v)); CALL_EvalCoord2f(dispatch, (u, v + dv)); } CALL_End(dispatch, ()); } break; } } /** * Called from glDrawArrays when in immediate mode (not display list mode). */ void GLAPIENTRY _mesa_DrawArrays(GLenum mode, GLint start, GLsizei count) { GET_CURRENT_CONTEXT(ctx); FLUSH_FOR_DRAW(ctx); _mesa_set_varying_vp_inputs(ctx, ctx->VertexProgram._VPModeInputFilter & ctx->Array._DrawVAO->_EnabledWithMapMode); if (ctx->NewState) _mesa_update_state(ctx); if (!_mesa_is_no_error_enabled(ctx) && !_mesa_validate_DrawArrays(ctx, mode, count)) return; if (0) check_draw_arrays_data(ctx, start, count); _mesa_draw_arrays(ctx, mode, start, count, 1, 0); if (0) print_draw_arrays(ctx, mode, start, count); } /** * Called from glDrawArraysInstanced when in immediate mode (not * display list mode). */ void GLAPIENTRY _mesa_DrawArraysInstanced(GLenum mode, GLint start, GLsizei count, GLsizei numInstances) { _mesa_DrawArraysInstancedBaseInstance(mode, start, count, numInstances, 0); } /** * Called from glDrawArraysInstancedBaseInstance when in immediate mode. */ void GLAPIENTRY _mesa_DrawArraysInstancedBaseInstance(GLenum mode, GLint first, GLsizei count, GLsizei numInstances, GLuint baseInstance) { GET_CURRENT_CONTEXT(ctx); FLUSH_FOR_DRAW(ctx); _mesa_set_varying_vp_inputs(ctx, ctx->VertexProgram._VPModeInputFilter & ctx->Array._DrawVAO->_EnabledWithMapMode); if (ctx->NewState) _mesa_update_state(ctx); if (!_mesa_is_no_error_enabled(ctx) && !_mesa_validate_DrawArraysInstanced(ctx, mode, first, count, numInstances)) return; if (0) check_draw_arrays_data(ctx, first, count); _mesa_draw_arrays(ctx, mode, first, count, numInstances, baseInstance); if (0) print_draw_arrays(ctx, mode, first, count); } /** * Called from glMultiDrawArrays when in immediate mode. */ void GLAPIENTRY _mesa_MultiDrawArrays(GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount) { GET_CURRENT_CONTEXT(ctx); FLUSH_FOR_DRAW(ctx); _mesa_set_varying_vp_inputs(ctx, ctx->VertexProgram._VPModeInputFilter & ctx->Array._DrawVAO->_EnabledWithMapMode); if (ctx->NewState) _mesa_update_state(ctx); if (!_mesa_is_no_error_enabled(ctx) && !_mesa_validate_MultiDrawArrays(ctx, mode, count, primcount)) return; if (primcount == 0) return; struct pipe_draw_info info; struct pipe_draw_start_count_bias *draw = get_temp_draws(ctx, primcount); if (!draw) return; info.mode = mode; info.index_size = 0; /* Packed section begin. */ info.primitive_restart = false; info.has_user_indices = false; info.index_bounds_valid = false; info.increment_draw_id = primcount > 1; info.was_line_loop = false; info.take_index_buffer_ownership = false; info.index_bias_varies = false; /* Packed section end. */ info.start_instance = 0; info.instance_count = 1; info.view_mask = 0; for (int i = 0; i < primcount; i++) { draw[i].start = first[i]; draw[i].count = count[i]; } st_prepare_draw(ctx, ST_PIPELINE_RENDER_STATE_MASK); ctx->Driver.DrawGallium(ctx, &info, 0, NULL, draw, primcount); if (MESA_DEBUG_FLAGS & DEBUG_ALWAYS_FLUSH) _mesa_flush(ctx); } /** * Map GL_ELEMENT_ARRAY_BUFFER and print contents. * For debugging. */ #if 0 static void dump_element_buffer(struct gl_context *ctx, GLenum type) { const GLvoid *map = ctx->Driver.MapBufferRange(ctx, 0, ctx->Array.VAO->IndexBufferObj->Size, GL_MAP_READ_BIT, ctx->Array.VAO->IndexBufferObj, MAP_INTERNAL); switch (type) { case GL_UNSIGNED_BYTE: { const GLubyte *us = (const GLubyte *) map; GLint i; for (i = 0; i < ctx->Array.VAO->IndexBufferObj->Size; i++) { printf("%02x ", us[i]); if (i % 32 == 31) printf("\n"); } printf("\n"); } break; case GL_UNSIGNED_SHORT: { const GLushort *us = (const GLushort *) map; GLint i; for (i = 0; i < ctx->Array.VAO->IndexBufferObj->Size / 2; i++) { printf("%04x ", us[i]); if (i % 16 == 15) printf("\n"); } printf("\n"); } break; case GL_UNSIGNED_INT: { const GLuint *us = (const GLuint *) map; GLint i; for (i = 0; i < ctx->Array.VAO->IndexBufferObj->Size / 4; i++) { printf("%08x ", us[i]); if (i % 8 == 7) printf("\n"); } printf("\n"); } break; default: ; } ctx->Driver.UnmapBuffer(ctx, ctx->Array.VAO->IndexBufferObj, MAP_INTERNAL); } #endif static bool validate_index_bounds(struct gl_context *ctx, struct pipe_draw_info *info, const struct pipe_draw_start_count_bias *draws, unsigned num_draws) { assert(info->index_size); /* Get index bounds for user buffers. */ if (!info->index_bounds_valid && ctx->st->draw_needs_minmax_index) { /* Return if this fails, which means all draws have count == 0. */ if (!vbo_get_minmax_indices_gallium(ctx, info, draws, num_draws)) return false; info->index_bounds_valid = true; } return true; } /** * Inner support for both _mesa_DrawElements and _mesa_DrawRangeElements. * Do the rendering for a glDrawElements or glDrawRangeElements call after * we've validated buffer bounds, etc. */ static void _mesa_validated_drawrangeelements(struct gl_context *ctx, struct gl_buffer_object *index_bo, GLenum mode, bool index_bounds_valid, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid * indices, GLint basevertex, GLuint numInstances, GLuint baseInstance) { /* Viewperf has many draws with count=0. Discarding them is faster than * processing them. */ if (!count || !numInstances) return; if (!index_bounds_valid) { assert(start == 0u); assert(end == ~0u); } unsigned index_size_shift = _mesa_get_index_size_shift(type); if (index_bo) { if (!indices_aligned(index_size_shift, indices)) return; if (unlikely(index_bo->Size < (uintptr_t)indices || !index_bo->buffer)) { #ifndef NDEBUG _mesa_warning(ctx, "Invalid indices offset 0x%" PRIxPTR " (indices buffer size is %ld bytes)" " or unallocated buffer (%u). Draw skipped.", (uintptr_t)indices, (long)index_bo->Size, !!index_bo->buffer); #endif return; } } st_prepare_draw(ctx, ST_PIPELINE_RENDER_STATE_MASK); /* Fast path for a very common DrawElements case: * - there are no user indices here (always true with glthread) * - DrawGallium is st_draw_gallium (regular render mode, almost always * true), which only calls cso_context::draw_vbo * - the threaded context is enabled while u_vbuf is bypassed (cso_context * always calls tc_draw_vbo, which is always true with glthread if all * vertex formats are also supported by the driver) * - DrawID is 0 (true if glthread isn't unrolling an indirect multi draw, * which is almost always true) */ struct st_context *st = st_context(ctx); if (index_bo && ctx->Driver.DrawGallium == st_draw_gallium && st->cso_context->draw_vbo == tc_draw_vbo && ctx->DrawID == 0) { assert(!st->draw_needs_minmax_index); struct pipe_resource *index_buffer = _mesa_get_bufferobj_reference(ctx, index_bo); struct tc_draw_single *draw = tc_add_draw_single_call(st->pipe, index_buffer); bool primitive_restart = ctx->Array._PrimitiveRestart[index_size_shift]; /* This must be set exactly like u_threaded_context sets it, not like * it would be set for draw_vbo. */ draw->info.mode = mode; draw->info.index_size = 1 << index_size_shift; draw->info.view_mask = 0; /* Packed section begin. */ draw->info.primitive_restart = primitive_restart; draw->info.has_user_indices = false; draw->info.index_bounds_valid = false; draw->info.increment_draw_id = false; draw->info.take_index_buffer_ownership = false; draw->info.index_bias_varies = false; draw->info.was_line_loop = false; draw->info._pad = 0; /* Packed section end. */ draw->info.start_instance = baseInstance; draw->info.instance_count = numInstances; draw->info.restart_index = primitive_restart ? ctx->Array._RestartIndex[index_size_shift] : 0; draw->info.index.resource = index_buffer; /* u_threaded_context stores start/count in min/max_index for single draws. */ draw->info.min_index = (uintptr_t)indices >> index_size_shift; draw->info.max_index = count; draw->index_bias = basevertex; return; } struct pipe_draw_info info; struct pipe_draw_start_count_bias draw; info.mode = mode; info.index_size = 1 << index_size_shift; /* Packed section begin. */ info.primitive_restart = ctx->Array._PrimitiveRestart[index_size_shift]; info.has_user_indices = index_bo == NULL; info.index_bounds_valid = index_bounds_valid; info.increment_draw_id = false; info.was_line_loop = false; info.take_index_buffer_ownership = false; info.index_bias_varies = false; /* Packed section end. */ info.start_instance = baseInstance; info.instance_count = numInstances; info.view_mask = 0; info.restart_index = ctx->Array._RestartIndex[index_size_shift]; if (info.has_user_indices) { info.index.user = indices; draw.start = 0; } else { draw.start = (uintptr_t)indices >> index_size_shift; if (ctx->pipe->draw_vbo == tc_draw_vbo) { /* Fast path for u_threaded_context to eliminate atomics. */ info.index.resource = _mesa_get_bufferobj_reference(ctx, index_bo); info.take_index_buffer_ownership = true; } else { info.index.resource = index_bo->buffer; } } draw.index_bias = basevertex; info.min_index = start; info.max_index = end; draw.count = count; if (!validate_index_bounds(ctx, &info, &draw, 1)) return; ctx->Driver.DrawGallium(ctx, &info, ctx->DrawID, NULL, &draw, 1); if (MESA_DEBUG_FLAGS & DEBUG_ALWAYS_FLUSH) { _mesa_flush(ctx); } } /** * Called by glDrawRangeElementsBaseVertex() in immediate mode. */ void GLAPIENTRY _mesa_DrawRangeElementsBaseVertex(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid * indices, GLint basevertex) { static GLuint warnCount = 0; bool index_bounds_valid = true; /* This is only useful to catch invalid values in the "end" parameter * like ~0. */ GLuint max_element = 2 * 1000 * 1000 * 1000; /* just a big number */ GET_CURRENT_CONTEXT(ctx); FLUSH_FOR_DRAW(ctx); _mesa_set_varying_vp_inputs(ctx, ctx->VertexProgram._VPModeInputFilter & ctx->Array._DrawVAO->_EnabledWithMapMode); if (ctx->NewState) _mesa_update_state(ctx); if (!_mesa_is_no_error_enabled(ctx) && !_mesa_validate_DrawRangeElements(ctx, mode, start, end, count, type)) return; if ((int) end + basevertex < 0 || start + basevertex >= max_element) { /* The application requested we draw using a range of indices that's * outside the bounds of the current VBO. This is invalid and appears * to give undefined results. The safest thing to do is to simply * ignore the range, in case the application botched their range tracking * but did provide valid indices. Also issue a warning indicating that * the application is broken. */ if (warnCount++ < 10) { _mesa_warning(ctx, "glDrawRangeElements(start %u, end %u, " "basevertex %d, count %d, type 0x%x, indices=%p):\n" "\trange is outside VBO bounds (max=%u); ignoring.\n" "\tThis should be fixed in the application.", start, end, basevertex, count, type, indices, max_element - 1); } index_bounds_valid = false; } /* NOTE: It's important that 'end' is a reasonable value. * in _tnl_draw_prims(), we use end to determine how many vertices * to transform. If it's too large, we can unnecessarily split prims * or we can read/write out of memory in several different places! */ /* Catch/fix some potential user errors */ if (type == GL_UNSIGNED_BYTE) { start = MIN2(start, 0xff); end = MIN2(end, 0xff); } else if (type == GL_UNSIGNED_SHORT) { start = MIN2(start, 0xffff); end = MIN2(end, 0xffff); } if (0) { printf("glDraw[Range]Elements{,BaseVertex}" "(start %u, end %u, type 0x%x, count %d) ElemBuf %u, " "base %d\n", start, end, type, count, ctx->Array.VAO->IndexBufferObj ? ctx->Array.VAO->IndexBufferObj->Name : 0, basevertex); } if ((int) start + basevertex < 0 || end + basevertex >= max_element) index_bounds_valid = false; #if 0 check_draw_elements_data(ctx, count, type, indices, basevertex); #else (void) check_draw_elements_data; #endif if (!index_bounds_valid) { start = 0; end = ~0; } _mesa_validated_drawrangeelements(ctx, ctx->Array.VAO->IndexBufferObj, mode, index_bounds_valid, start, end, count, type, indices, basevertex, 1, 0); } /** * Called by glDrawRangeElements() in immediate mode. */ void GLAPIENTRY _mesa_DrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid * indices) { _mesa_DrawRangeElementsBaseVertex(mode, start, end, count, type, indices, 0); } /** * Called by glDrawElements() in immediate mode. */ void GLAPIENTRY _mesa_DrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid * indices) { _mesa_DrawElementsBaseVertex(mode, count, type, indices, 0); } /** * Called by glDrawElementsBaseVertex() in immediate mode. */ void GLAPIENTRY _mesa_DrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, const GLvoid * indices, GLint basevertex) { GET_CURRENT_CONTEXT(ctx); FLUSH_FOR_DRAW(ctx); _mesa_set_varying_vp_inputs(ctx, ctx->VertexProgram._VPModeInputFilter & ctx->Array._DrawVAO->_EnabledWithMapMode); if (ctx->NewState) _mesa_update_state(ctx); if (!_mesa_is_no_error_enabled(ctx) && !_mesa_validate_DrawElements(ctx, mode, count, type)) return; _mesa_validated_drawrangeelements(ctx, ctx->Array.VAO->IndexBufferObj, mode, false, 0, ~0, count, type, indices, basevertex, 1, 0); } /** * Called by glDrawElementsInstanced() in immediate mode. */ void GLAPIENTRY _mesa_DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, const GLvoid * indices, GLsizei numInstances) { _mesa_DrawElementsInstancedBaseVertexBaseInstance(mode, count, type, indices, numInstances, 0, 0); } /** * Called by glDrawElementsInstancedBaseVertex() in immediate mode. */ void GLAPIENTRY _mesa_DrawElementsInstancedBaseVertex(GLenum mode, GLsizei count, GLenum type, const GLvoid * indices, GLsizei numInstances, GLint basevertex) { _mesa_DrawElementsInstancedBaseVertexBaseInstance(mode, count, type, indices, numInstances, basevertex, 0); } /** * Called by glDrawElementsInstancedBaseInstance() in immediate mode. */ void GLAPIENTRY _mesa_DrawElementsInstancedBaseInstance(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLsizei numInstances, GLuint baseInstance) { _mesa_DrawElementsInstancedBaseVertexBaseInstance(mode, count, type, indices, numInstances, 0, baseInstance); } /** * Called by glDrawElementsInstancedBaseVertexBaseInstance() in immediate mode. */ void GLAPIENTRY _mesa_DrawElementsInstancedBaseVertexBaseInstance(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLsizei numInstances, GLint basevertex, GLuint baseInstance) { GET_CURRENT_CONTEXT(ctx); FLUSH_FOR_DRAW(ctx); _mesa_set_varying_vp_inputs(ctx, ctx->VertexProgram._VPModeInputFilter & ctx->Array._DrawVAO->_EnabledWithMapMode); if (ctx->NewState) _mesa_update_state(ctx); if (!_mesa_is_no_error_enabled(ctx) && !_mesa_validate_DrawElementsInstanced(ctx, mode, count, type, numInstances)) return; _mesa_validated_drawrangeelements(ctx, ctx->Array.VAO->IndexBufferObj, mode, false, 0, ~0, count, type, indices, basevertex, numInstances, baseInstance); } /** * Same as glDrawElementsInstancedBaseVertexBaseInstance, but the index * buffer is set by the indexBuf parameter instead of using the bound * GL_ELEMENT_ARRAY_BUFFER if indexBuf != NULL. */ void GLAPIENTRY _mesa_DrawElementsUserBuf(const GLvoid *ptr) { GET_CURRENT_CONTEXT(ctx); FLUSH_FOR_DRAW(ctx); _mesa_set_varying_vp_inputs(ctx, ctx->VertexProgram._VPModeInputFilter & ctx->Array._DrawVAO->_EnabledWithMapMode); if (ctx->NewState) _mesa_update_state(ctx); const struct marshal_cmd_DrawElementsUserBuf *cmd = (const struct marshal_cmd_DrawElementsUserBuf *)ptr; const GLenum mode = cmd->mode; const GLsizei count = cmd->count; const GLenum type = _mesa_decode_index_type(cmd->type); const GLsizei instance_count = cmd->instance_count; if (!_mesa_is_no_error_enabled(ctx) && !_mesa_validate_DrawElementsInstanced(ctx, mode, count, type, instance_count)) return; struct gl_buffer_object *index_bo = cmd->index_buffer ? cmd->index_buffer : ctx->Array.VAO->IndexBufferObj; const GLvoid *indices = cmd->indices; const GLint basevertex = cmd->basevertex; const GLuint baseinstance = cmd->baseinstance; ctx->DrawID = cmd->drawid; _mesa_validated_drawrangeelements(ctx, index_bo, mode, false, 0, ~0, count, type, indices, basevertex, instance_count, baseinstance); ctx->DrawID = 0; } /** * Same as glDrawElementsInstancedBaseVertexBaseInstance, but the index * buffer is set by the indexBuf parameter instead of using the bound * GL_ELEMENT_ARRAY_BUFFER if indexBuf != NULL. */ void GLAPIENTRY _mesa_DrawElementsUserBufPacked(const GLvoid *ptr) { GET_CURRENT_CONTEXT(ctx); FLUSH_FOR_DRAW(ctx); _mesa_set_varying_vp_inputs(ctx, ctx->VertexProgram._VPModeInputFilter & ctx->Array._DrawVAO->_EnabledWithMapMode); if (ctx->NewState) _mesa_update_state(ctx); const struct marshal_cmd_DrawElementsUserBufPacked *cmd = (const struct marshal_cmd_DrawElementsUserBufPacked *)ptr; const GLenum mode = cmd->mode; const GLsizei count = cmd->count; const GLenum type = _mesa_decode_index_type(cmd->type); if (!_mesa_is_no_error_enabled(ctx) && !_mesa_validate_DrawElements(ctx, mode, count, type)) return; struct gl_buffer_object *index_bo = cmd->index_buffer ? cmd->index_buffer : ctx->Array.VAO->IndexBufferObj; const GLvoid *indices = (void*)(uintptr_t)cmd->indices; _mesa_validated_drawrangeelements(ctx, index_bo, mode, false, 0, ~0, count, type, indices, 0, 1, 0); } /** * Inner support for both _mesa_MultiDrawElements() and * _mesa_MultiDrawRangeElements(). * This does the actual rendering after we've checked array indexes, etc. */ static void _mesa_validated_multidrawelements(struct gl_context *ctx, struct gl_buffer_object *index_bo, GLenum mode, const GLsizei *count, GLenum type, const GLvoid * const *indices, GLsizei primcount, const GLint *basevertex) { uintptr_t min_index_ptr, max_index_ptr; bool fallback = false; int i; if (primcount == 0) return; unsigned index_size_shift = _mesa_get_index_size_shift(type); min_index_ptr = (uintptr_t) indices[0]; max_index_ptr = 0; for (i = 0; i < primcount; i++) { if (count[i]) { min_index_ptr = MIN2(min_index_ptr, (uintptr_t) indices[i]); max_index_ptr = MAX2(max_index_ptr, (uintptr_t) indices[i] + (count[i] << index_size_shift)); } } /* Check if we can handle this thing as a bunch of index offsets from the * same index pointer. If we can't, then we have to fall back to doing * a draw_prims per primitive. * Check that the difference between each prim's indexes is a multiple of * the index/element size. */ if (index_size_shift) { for (i = 0; i < primcount; i++) { if (count[i] && (((uintptr_t)indices[i] - min_index_ptr) & ((1 << index_size_shift) - 1)) != 0) { fallback = true; break; } } } struct pipe_draw_info info; info.mode = mode; info.index_size = 1 << index_size_shift; /* Packed section begin. */ info.primitive_restart = ctx->Array._PrimitiveRestart[index_size_shift]; info.has_user_indices = index_bo == NULL; info.index_bounds_valid = false; info.increment_draw_id = primcount > 1; info.was_line_loop = false; info.take_index_buffer_ownership = false; info.index_bias_varies = !!basevertex; /* Packed section end. */ info.start_instance = 0; info.instance_count = 1; info.view_mask = 0; info.restart_index = ctx->Array._RestartIndex[index_size_shift]; if (info.has_user_indices) { info.index.user = (void*)min_index_ptr; } else { if (ctx->pipe->draw_vbo == tc_draw_vbo) { /* Fast path for u_threaded_context to eliminate atomics. */ info.index.resource = _mesa_get_bufferobj_reference(ctx, index_bo); info.take_index_buffer_ownership = true; } else { info.index.resource = index_bo->buffer; } /* No index buffer storage allocated - nothing to do. */ if (!info.index.resource) return; } if (!fallback && (!info.has_user_indices || /* "max_index_ptr - min_index_ptr >> index_size_shift" is stored * in draw[i].start. The driver will multiply it later by index_size * so make sure the final value won't overflow. * * For real index buffers, gallium doesn't support index buffer offsets * greater than UINT32_MAX bytes. */ max_index_ptr - min_index_ptr <= UINT32_MAX)) { struct pipe_draw_start_count_bias *draw = get_temp_draws(ctx, primcount); if (!draw) return; if (info.has_user_indices) { for (int i = 0; i < primcount; i++) { draw[i].start = ((uintptr_t)indices[i] - min_index_ptr) >> index_size_shift; draw[i].count = count[i]; draw[i].index_bias = basevertex ? basevertex[i] : 0; } } else { for (int i = 0; i < primcount; i++) { draw[i].start = (uintptr_t)indices[i] >> index_size_shift; draw[i].count = indices_aligned(index_size_shift, indices[i]) ? count[i] : 0; draw[i].index_bias = basevertex ? basevertex[i] : 0; } } st_prepare_draw(ctx, ST_PIPELINE_RENDER_STATE_MASK); if (!validate_index_bounds(ctx, &info, draw, primcount)) return; ctx->Driver.DrawGallium(ctx, &info, 0, NULL, draw, primcount); } else { /* draw[i].start would overflow. Draw one at a time. */ assert(info.has_user_indices); info.increment_draw_id = false; st_prepare_draw(ctx, ST_PIPELINE_RENDER_STATE_MASK); for (int i = 0; i < primcount; i++) { struct pipe_draw_start_count_bias draw; if (!count[i]) continue; /* Reset these, because the callee can change them. */ info.index_bounds_valid = false; info.index.user = indices[i]; draw.start = 0; draw.index_bias = basevertex ? basevertex[i] : 0; draw.count = count[i]; if (!draw.count || !validate_index_bounds(ctx, &info, &draw, 1)) continue; ctx->Driver.DrawGallium(ctx, &info, i, NULL, &draw, 1); } } if (MESA_DEBUG_FLAGS & DEBUG_ALWAYS_FLUSH) { _mesa_flush(ctx); } } void GLAPIENTRY _mesa_MultiDrawElements(GLenum mode, const GLsizei *count, GLenum type, const GLvoid * const *indices, GLsizei primcount) { GET_CURRENT_CONTEXT(ctx); FLUSH_FOR_DRAW(ctx); _mesa_set_varying_vp_inputs(ctx, ctx->VertexProgram._VPModeInputFilter & ctx->Array._DrawVAO->_EnabledWithMapMode); if (ctx->NewState) _mesa_update_state(ctx); struct gl_buffer_object *index_bo = ctx->Array.VAO->IndexBufferObj; if (!_mesa_is_no_error_enabled(ctx) && !_mesa_validate_MultiDrawElements(ctx, mode, count, type, indices, primcount, index_bo)) return; _mesa_validated_multidrawelements(ctx, index_bo, mode, count, type, indices, primcount, NULL); } void GLAPIENTRY _mesa_MultiDrawElementsBaseVertex(GLenum mode, const GLsizei *count, GLenum type, const GLvoid * const *indices, GLsizei primcount, const GLsizei *basevertex) { GET_CURRENT_CONTEXT(ctx); FLUSH_FOR_DRAW(ctx); _mesa_set_varying_vp_inputs(ctx, ctx->VertexProgram._VPModeInputFilter & ctx->Array._DrawVAO->_EnabledWithMapMode); if (ctx->NewState) _mesa_update_state(ctx); struct gl_buffer_object *index_bo = ctx->Array.VAO->IndexBufferObj; if (!_mesa_is_no_error_enabled(ctx) && !_mesa_validate_MultiDrawElements(ctx, mode, count, type, indices, primcount, index_bo)) return; _mesa_validated_multidrawelements(ctx, index_bo, mode, count, type, indices, primcount, basevertex); } /** * Same as glMultiDrawElementsBaseVertex, but the index buffer is set by * the indexBuf parameter instead of using the bound GL_ELEMENT_ARRAY_BUFFER * if indexBuf != NULL. */ void GLAPIENTRY _mesa_MultiDrawElementsUserBuf(GLintptr indexBuf, GLenum mode, const GLsizei *count, GLenum type, const GLvoid * const * indices, GLsizei primcount, const GLint * basevertex) { GET_CURRENT_CONTEXT(ctx); FLUSH_FOR_DRAW(ctx); _mesa_set_varying_vp_inputs(ctx, ctx->VertexProgram._VPModeInputFilter & ctx->Array._DrawVAO->_EnabledWithMapMode); if (ctx->NewState) _mesa_update_state(ctx); struct gl_buffer_object *index_bo = indexBuf ? (struct gl_buffer_object*)indexBuf : ctx->Array.VAO->IndexBufferObj; if (!_mesa_is_no_error_enabled(ctx) && !_mesa_validate_MultiDrawElements(ctx, mode, count, type, indices, primcount, index_bo)) return; _mesa_validated_multidrawelements(ctx, index_bo, mode, count, type, indices, primcount, basevertex); } /** * Like DrawArrays, but take the count from a transform feedback object. * \param mode GL_POINTS, GL_LINES, GL_TRIANGLE_STRIP, etc. * \param name the transform feedback object * User still has to setup of the vertex attribute info with * glVertexPointer, glColorPointer, etc. * Part of GL_ARB_transform_feedback2. */ void GLAPIENTRY _mesa_DrawTransformFeedback(GLenum mode, GLuint name) { _mesa_DrawTransformFeedbackStreamInstanced(mode, name, 0, 1); } void GLAPIENTRY _mesa_DrawTransformFeedbackStream(GLenum mode, GLuint name, GLuint stream) { _mesa_DrawTransformFeedbackStreamInstanced(mode, name, stream, 1); } void GLAPIENTRY _mesa_DrawTransformFeedbackInstanced(GLenum mode, GLuint name, GLsizei primcount) { _mesa_DrawTransformFeedbackStreamInstanced(mode, name, 0, primcount); } void GLAPIENTRY _mesa_DrawTransformFeedbackStreamInstanced(GLenum mode, GLuint name, GLuint stream, GLsizei primcount) { GET_CURRENT_CONTEXT(ctx); struct gl_transform_feedback_object *obj = _mesa_lookup_transform_feedback_object(ctx, name); FLUSH_FOR_DRAW(ctx); _mesa_set_varying_vp_inputs(ctx, ctx->VertexProgram._VPModeInputFilter & ctx->Array._DrawVAO->_EnabledWithMapMode); if (ctx->NewState) _mesa_update_state(ctx); if (!_mesa_is_no_error_enabled(ctx) && !_mesa_validate_DrawTransformFeedback(ctx, mode, obj, stream, primcount)) return; st_prepare_draw(ctx, ST_PIPELINE_RENDER_STATE_MASK); struct pipe_draw_indirect_info indirect; memset(&indirect, 0, sizeof(indirect)); indirect.count_from_stream_output = obj->draw_count[stream]; if (indirect.count_from_stream_output == NULL) return; struct pipe_draw_start_count_bias draw = {0}; struct pipe_draw_info info; util_draw_init_info(&info); info.max_index = ~0u; /* so that u_vbuf can tell that it's unknown */ info.mode = mode; info.instance_count = primcount; ctx->Driver.DrawGallium(ctx, &info, 0, &indirect, &draw, 1); if (MESA_DEBUG_FLAGS & DEBUG_ALWAYS_FLUSH) { _mesa_flush(ctx); } } /** * Like [Multi]DrawArrays/Elements, but they take most arguments from * a buffer object. */ void GLAPIENTRY _mesa_DrawArraysIndirect(GLenum mode, const GLvoid *indirect) { GET_CURRENT_CONTEXT(ctx); /* From the ARB_draw_indirect spec: * * "Initially zero is bound to DRAW_INDIRECT_BUFFER. In the * compatibility profile, this indicates that DrawArraysIndirect and * DrawElementsIndirect are to source their arguments directly from the * pointer passed as their parameters." */ if (_mesa_is_desktop_gl_compat(ctx) && !ctx->DrawIndirectBuffer) { DrawArraysIndirectCommand *cmd = (DrawArraysIndirectCommand *) indirect; _mesa_DrawArraysInstancedBaseInstance(mode, cmd->first, cmd->count, cmd->primCount, cmd->baseInstance); return; } FLUSH_FOR_DRAW(ctx); _mesa_set_varying_vp_inputs(ctx, ctx->VertexProgram._VPModeInputFilter & ctx->Array._DrawVAO->_EnabledWithMapMode); if (ctx->NewState) _mesa_update_state(ctx); if (!_mesa_is_no_error_enabled(ctx) && !_mesa_validate_DrawArraysIndirect(ctx, mode, indirect)) return; st_indirect_draw_vbo(ctx, mode, 0, (GLintptr)indirect, 0, 1, 16); } void GLAPIENTRY _mesa_DrawElementsIndirect(GLenum mode, GLenum type, const GLvoid *indirect) { GET_CURRENT_CONTEXT(ctx); /* From the ARB_draw_indirect spec: * * "Initially zero is bound to DRAW_INDIRECT_BUFFER. In the * compatibility profile, this indicates that DrawArraysIndirect and * DrawElementsIndirect are to source their arguments directly from the * pointer passed as their parameters." */ if (_mesa_is_desktop_gl_compat(ctx) && !ctx->DrawIndirectBuffer) { /* * Unlike regular DrawElementsInstancedBaseVertex commands, the indices * may not come from a client array and must come from an index buffer. * If no element array buffer is bound, an INVALID_OPERATION error is * generated. */ if (!ctx->Array.VAO->IndexBufferObj) { _mesa_error(ctx, GL_INVALID_OPERATION, "glDrawElementsIndirect(no buffer bound " "to GL_ELEMENT_ARRAY_BUFFER)"); } else { DrawElementsIndirectCommand *cmd = (DrawElementsIndirectCommand *) indirect; /* Convert offset to pointer */ void *offset = (void *) (uintptr_t)((cmd->firstIndex * _mesa_sizeof_type(type)) & 0xffffffffUL); _mesa_DrawElementsInstancedBaseVertexBaseInstance(mode, cmd->count, type, offset, cmd->primCount, cmd->baseVertex, cmd->baseInstance); } return; } FLUSH_FOR_DRAW(ctx); _mesa_set_varying_vp_inputs(ctx, ctx->VertexProgram._VPModeInputFilter & ctx->Array._DrawVAO->_EnabledWithMapMode); if (ctx->NewState) _mesa_update_state(ctx); if (!_mesa_is_no_error_enabled(ctx) && !_mesa_validate_DrawElementsIndirect(ctx, mode, type, indirect)) return; st_indirect_draw_vbo(ctx, mode, type, (GLintptr)indirect, 0, 1, 20); } void GLAPIENTRY _mesa_MultiDrawArraysIndirect(GLenum mode, const GLvoid *indirect, GLsizei primcount, GLsizei stride) { GET_CURRENT_CONTEXT(ctx); /* If is zero, the array elements are treated as tightly packed. */ if (stride == 0) stride = sizeof(DrawArraysIndirectCommand); FLUSH_FOR_DRAW(ctx); _mesa_set_varying_vp_inputs(ctx, ctx->VertexProgram._VPModeInputFilter & ctx->Array._DrawVAO->_EnabledWithMapMode); if (ctx->NewState) _mesa_update_state(ctx); /* From the ARB_draw_indirect spec: * * "Initially zero is bound to DRAW_INDIRECT_BUFFER. In the * compatibility profile, this indicates that DrawArraysIndirect and * DrawElementsIndirect are to source their arguments directly from the * pointer passed as their parameters." */ if (_mesa_is_desktop_gl_compat(ctx) && !ctx->DrawIndirectBuffer) { if (!_mesa_is_no_error_enabled(ctx) && (!_mesa_valid_draw_indirect_multi(ctx, primcount, stride, "glMultiDrawArraysIndirect") || !_mesa_validate_DrawArrays(ctx, mode, 1))) return; struct pipe_draw_info info; info.mode = mode; info.index_size = 0; info.view_mask = 0; /* Packed section begin. */ info.primitive_restart = false; info.has_user_indices = false; info.index_bounds_valid = false; info.increment_draw_id = primcount > 1; info.was_line_loop = false; info.take_index_buffer_ownership = false; info.index_bias_varies = false; /* Packed section end. */ st_prepare_draw(ctx, ST_PIPELINE_RENDER_STATE_MASK); const uint8_t *ptr = (const uint8_t *) indirect; for (unsigned i = 0; i < primcount; i++) { DrawArraysIndirectCommand *cmd = (DrawArraysIndirectCommand *) ptr; info.start_instance = cmd->baseInstance; info.instance_count = cmd->primCount; struct pipe_draw_start_count_bias draw; draw.start = cmd->first; draw.count = cmd->count; if (!draw.count) continue; ctx->Driver.DrawGallium(ctx, &info, i, NULL, &draw, 1); ptr += stride; } return; } if (!_mesa_is_no_error_enabled(ctx) && !_mesa_validate_MultiDrawArraysIndirect(ctx, mode, indirect, primcount, stride)) return; st_indirect_draw_vbo(ctx, mode, 0, (GLintptr)indirect, 0, primcount, stride); } void GLAPIENTRY _mesa_MultiDrawElementsIndirect(GLenum mode, GLenum type, const GLvoid *indirect, GLsizei primcount, GLsizei stride) { GET_CURRENT_CONTEXT(ctx); FLUSH_FOR_DRAW(ctx); _mesa_set_varying_vp_inputs(ctx, ctx->VertexProgram._VPModeInputFilter & ctx->Array._DrawVAO->_EnabledWithMapMode); if (ctx->NewState) _mesa_update_state(ctx); /* If is zero, the array elements are treated as tightly packed. */ if (stride == 0) stride = sizeof(DrawElementsIndirectCommand); /* From the ARB_draw_indirect spec: * * "Initially zero is bound to DRAW_INDIRECT_BUFFER. In the * compatibility profile, this indicates that DrawArraysIndirect and * DrawElementsIndirect are to source their arguments directly from the * pointer passed as their parameters." */ if (_mesa_is_desktop_gl_compat(ctx) && !ctx->DrawIndirectBuffer) { /* * Unlike regular DrawElementsInstancedBaseVertex commands, the indices * may not come from a client array and must come from an index buffer. * If no element array buffer is bound, an INVALID_OPERATION error is * generated. */ if (!ctx->Array.VAO->IndexBufferObj) { _mesa_error(ctx, GL_INVALID_OPERATION, "glMultiDrawElementsIndirect(no buffer bound " "to GL_ELEMENT_ARRAY_BUFFER)"); return; } if (!_mesa_is_no_error_enabled(ctx) && (!_mesa_valid_draw_indirect_multi(ctx, primcount, stride, "glMultiDrawArraysIndirect") || !_mesa_validate_DrawElements(ctx, mode, 1, type))) return; unsigned index_size_shift = _mesa_get_index_size_shift(type); struct pipe_draw_info info; info.mode = mode; info.index_size = 1 << index_size_shift; info.view_mask = 0; /* Packed section begin. */ info.primitive_restart = ctx->Array._PrimitiveRestart[index_size_shift]; info.has_user_indices = false; info.index_bounds_valid = false; info.increment_draw_id = primcount > 1; info.was_line_loop = false; info.take_index_buffer_ownership = false; info.index_bias_varies = false; /* Packed section end. */ info.restart_index = ctx->Array._RestartIndex[index_size_shift]; struct gl_buffer_object *index_bo = ctx->Array.VAO->IndexBufferObj; if (ctx->pipe->draw_vbo == tc_draw_vbo) { /* Fast path for u_threaded_context to eliminate atomics. */ info.index.resource = _mesa_get_bufferobj_reference(ctx, index_bo); info.take_index_buffer_ownership = true; /* Increase refcount so be able to use take_index_buffer_ownership with * multiple draws. */ if (primcount > 1 && info.index.resource) p_atomic_add(&info.index.resource->reference.count, primcount - 1); } else { info.index.resource = index_bo->buffer; } /* No index buffer storage allocated - nothing to do. */ if (!info.index.resource) return; st_prepare_draw(ctx, ST_PIPELINE_RENDER_STATE_MASK); const uint8_t *ptr = (const uint8_t *) indirect; for (unsigned i = 0; i < primcount; i++) { DrawElementsIndirectCommand *cmd = (DrawElementsIndirectCommand*)ptr; info.start_instance = cmd->baseInstance; info.instance_count = cmd->primCount; struct pipe_draw_start_count_bias draw; draw.start = cmd->firstIndex; draw.count = cmd->count; draw.index_bias = cmd->baseVertex; if (!draw.count || !validate_index_bounds(ctx, &info, &draw, 1)) continue; ctx->Driver.DrawGallium(ctx, &info, i, NULL, &draw, 1); ptr += stride; } return; } if (!_mesa_is_no_error_enabled(ctx) && !_mesa_validate_MultiDrawElementsIndirect(ctx, mode, type, indirect, primcount, stride)) return; st_indirect_draw_vbo(ctx, mode, type, (GLintptr)indirect, 0, primcount, stride); } void GLAPIENTRY _mesa_MultiDrawArraysIndirectCountARB(GLenum mode, GLintptr indirect, GLintptr drawcount_offset, GLsizei maxdrawcount, GLsizei stride) { GET_CURRENT_CONTEXT(ctx); FLUSH_FOR_DRAW(ctx); /* If is zero, the array elements are treated as tightly packed. */ if (stride == 0) stride = 4 * sizeof(GLuint); /* sizeof(DrawArraysIndirectCommand) */ _mesa_set_varying_vp_inputs(ctx, ctx->VertexProgram._VPModeInputFilter & ctx->Array._DrawVAO->_EnabledWithMapMode); if (ctx->NewState) _mesa_update_state(ctx); if (!_mesa_is_no_error_enabled(ctx) && !_mesa_validate_MultiDrawArraysIndirectCount(ctx, mode, indirect, drawcount_offset, maxdrawcount, stride)) return; st_indirect_draw_vbo(ctx, mode, 0, (GLintptr)indirect, drawcount_offset, maxdrawcount, stride); } void GLAPIENTRY _mesa_MultiDrawElementsIndirectCountARB(GLenum mode, GLenum type, GLintptr indirect, GLintptr drawcount_offset, GLsizei maxdrawcount, GLsizei stride) { GET_CURRENT_CONTEXT(ctx); FLUSH_FOR_DRAW(ctx); /* If is zero, the array elements are treated as tightly packed. */ if (stride == 0) stride = 5 * sizeof(GLuint); /* sizeof(DrawElementsIndirectCommand) */ _mesa_set_varying_vp_inputs(ctx, ctx->VertexProgram._VPModeInputFilter & ctx->Array._DrawVAO->_EnabledWithMapMode); if (ctx->NewState) _mesa_update_state(ctx); if (!_mesa_is_no_error_enabled(ctx) && !_mesa_validate_MultiDrawElementsIndirectCount(ctx, mode, type, indirect, drawcount_offset, maxdrawcount, stride)) return; st_indirect_draw_vbo(ctx, mode, type, (GLintptr)indirect, drawcount_offset, maxdrawcount, stride); } /* GL_IBM_multimode_draw_arrays */ void GLAPIENTRY _mesa_MultiModeDrawArraysIBM( const GLenum * mode, const GLint * first, const GLsizei * count, GLsizei primcount, GLint modestride ) { GET_CURRENT_CONTEXT(ctx); GLint i; for ( i = 0 ; i < primcount ; i++ ) { if ( count[i] > 0 ) { GLenum m = *((GLenum *) ((GLubyte *) mode + i * modestride)); CALL_DrawArrays(ctx->Dispatch.Current, ( m, first[i], count[i] )); } } } /* GL_IBM_multimode_draw_arrays */ void GLAPIENTRY _mesa_MultiModeDrawElementsIBM( const GLenum * mode, const GLsizei * count, GLenum type, const GLvoid * const * indices, GLsizei primcount, GLint modestride ) { GET_CURRENT_CONTEXT(ctx); GLint i; for ( i = 0 ; i < primcount ; i++ ) { if ( count[i] > 0 ) { GLenum m = *((GLenum *) ((GLubyte *) mode + i * modestride)); CALL_DrawElements(ctx->Dispatch.Current, ( m, count[i], type, indices[i] )); } } }