Files
third_party_mesa3d/src/mesa/main/draw.c
Mike Blumenkrantz f5bd39e0e3 gallium: delete duplicated viewmask member in draw info
this was added for lavapipe, but it should have been in the
framebuffer state since it is a framebuffer state

now the GL multiview extensions are supported with viewmask
in the framebuffer struct, which means this is all redundant
and should be corrected/deleted

Reviewed-by: Marek Olšák <marek.olsak@amd.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/31590>
2024-10-15 14:01:42 +00:00

2698 lines
86 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**************************************************************************
*
* 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 <stdio.h>
#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 objects 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 <primcount> is negative."
*
* "<primcount> 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:
* "<stride> 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 <drawcount> 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 <sizei> typed value
* from the buffer bound to the PARAMETER_BUFFER_ARB target at the offset
* specified by <drawcount> 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.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;
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;
/* 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.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.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 <indirect> 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 <indirect> 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 <stride> 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 <indirect> 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;
/* 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 <stride> 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 <indirect> 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;
/* 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 <stride> 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 <stride> 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] ));
}
}
}