From be45255ab1f63c10fefcf2f399ac7d1c9294cf6a Mon Sep 17 00:00:00 2001 From: Brian Paul Date: Thu, 21 Oct 2010 19:03:38 -0600 Subject: [PATCH] vbo: support for primitive restart We handle splitting of glDrawArrays() calls into two primitives here so that drivers don't have to worry about it. --- src/mesa/vbo/vbo_exec_api.c | 23 ++++ src/mesa/vbo/vbo_exec_array.c | 236 +++++++++++++++++++++------------- src/mesa/vbo/vbo_save_api.c | 13 ++ 3 files changed, 186 insertions(+), 86 deletions(-) diff --git a/src/mesa/vbo/vbo_exec_api.c b/src/mesa/vbo/vbo_exec_api.c index 80ca1d866e7..1ef49174193 100644 --- a/src/mesa/vbo/vbo_exec_api.c +++ b/src/mesa/vbo/vbo_exec_api.c @@ -568,6 +568,28 @@ static void GLAPIENTRY vbo_exec_End( void ) } +/** + * Called via glPrimitiveRestartNV() + */ +static void GLAPIENTRY +vbo_exec_PrimitiveRestartNV(void) +{ + GLenum curPrim; + GET_CURRENT_CONTEXT( ctx ); + + curPrim = ctx->Driver.CurrentExecPrimitive; + + if (curPrim == PRIM_OUTSIDE_BEGIN_END) { + _mesa_error( ctx, GL_INVALID_OPERATION, "glPrimitiveRestartNV" ); + } + else { + vbo_exec_End(); + vbo_exec_Begin(curPrim); + } +} + + + static void vbo_exec_vtxfmt_init( struct vbo_exec_context *exec ) { GLvertexformat *vfmt = &exec->vtxfmt; @@ -576,6 +598,7 @@ static void vbo_exec_vtxfmt_init( struct vbo_exec_context *exec ) vfmt->Begin = vbo_exec_Begin; vfmt->End = vbo_exec_End; + vfmt->PrimitiveRestartNV = vbo_exec_PrimitiveRestartNV; _MESA_INIT_DLIST_VTXFMT(vfmt, _mesa_); _MESA_INIT_EVAL_VTXFMT(vfmt, vbo_exec_); diff --git a/src/mesa/vbo/vbo_exec_array.c b/src/mesa/vbo/vbo_exec_array.c index f46ba636fe7..dd36cc32a70 100644 --- a/src/mesa/vbo/vbo_exec_array.c +++ b/src/mesa/vbo/vbo_exec_array.c @@ -41,6 +41,8 @@ /** * Compute min and max elements by scanning the index buffer for * glDraw[Range]Elements() calls. + * If primitive restart is enabled, we need to ignore restart + * indexes when computing min/max. */ void vbo_get_minmax_index(struct gl_context *ctx, @@ -48,9 +50,11 @@ vbo_get_minmax_index(struct gl_context *ctx, const struct _mesa_index_buffer *ib, GLuint *min_index, GLuint *max_index) { - GLuint i; - GLuint count = prim->count; + const GLboolean restart = ctx->Array.PrimitiveRestart; + const GLuint restartIndex = ctx->Array.RestartIndex; + const GLuint count = prim->count; const void *indices; + GLuint i; if (_mesa_is_bufferobj(ib->obj)) { const GLvoid *map = @@ -64,11 +68,21 @@ vbo_get_minmax_index(struct gl_context *ctx, switch (ib->type) { case GL_UNSIGNED_INT: { const GLuint *ui_indices = (const GLuint *)indices; - GLuint max_ui = ui_indices[count-1]; - GLuint min_ui = ui_indices[0]; - for (i = 0; i < count; i++) { - if (ui_indices[i] > max_ui) max_ui = ui_indices[i]; - if (ui_indices[i] < min_ui) min_ui = ui_indices[i]; + GLuint max_ui = 0; + GLuint min_ui = ~0U; + if (restart) { + for (i = 0; i < count; i++) { + if (ui_indices[i] != restartIndex) { + if (ui_indices[i] > max_ui) max_ui = ui_indices[i]; + if (ui_indices[i] < min_ui) min_ui = ui_indices[i]; + } + } + } + else { + for (i = 0; i < count; i++) { + if (ui_indices[i] > max_ui) max_ui = ui_indices[i]; + if (ui_indices[i] < min_ui) min_ui = ui_indices[i]; + } } *min_index = min_ui; *max_index = max_ui; @@ -76,11 +90,21 @@ vbo_get_minmax_index(struct gl_context *ctx, } case GL_UNSIGNED_SHORT: { const GLushort *us_indices = (const GLushort *)indices; - GLuint max_us = us_indices[count-1]; - GLuint min_us = us_indices[0]; - for (i = 0; i < count; i++) { - if (us_indices[i] > max_us) max_us = us_indices[i]; - if (us_indices[i] < min_us) min_us = us_indices[i]; + GLuint max_us = 0; + GLuint min_us = ~0U; + if (restart) { + for (i = 0; i < count; i++) { + if (us_indices[i] != restartIndex) { + if (us_indices[i] > max_us) max_us = us_indices[i]; + if (us_indices[i] < min_us) min_us = us_indices[i]; + } + } + } + else { + for (i = 0; i < count; i++) { + if (us_indices[i] > max_us) max_us = us_indices[i]; + if (us_indices[i] < min_us) min_us = us_indices[i]; + } } *min_index = min_us; *max_index = max_us; @@ -88,11 +112,21 @@ vbo_get_minmax_index(struct gl_context *ctx, } case GL_UNSIGNED_BYTE: { const GLubyte *ub_indices = (const GLubyte *)indices; - GLuint max_ub = ub_indices[count-1]; - GLuint min_ub = ub_indices[0]; - for (i = 0; i < count; i++) { - if (ub_indices[i] > max_ub) max_ub = ub_indices[i]; - if (ub_indices[i] < min_ub) min_ub = ub_indices[i]; + GLuint max_ub = 0; + GLuint min_ub = ~0U; + if (restart) { + for (i = 0; i < count; i++) { + if (ub_indices[i] != restartIndex) { + if (ub_indices[i] > max_ub) max_ub = ub_indices[i]; + if (ub_indices[i] < min_ub) min_ub = ub_indices[i]; + } + } + } + else { + for (i = 0; i < count; i++) { + if (ub_indices[i] > max_ub) max_ub = ub_indices[i]; + if (ub_indices[i] < min_ub) min_ub = ub_indices[i]; + } } *min_index = min_ub; *max_index = max_ub; @@ -254,9 +288,11 @@ check_draw_arrays_data(struct gl_context *ctx, GLint start, GLsizei count) * Print info/data for glDrawArrays(), for debugging. */ static void -print_draw_arrays(struct gl_context *ctx, struct vbo_exec_context *exec, +print_draw_arrays(struct gl_context *ctx, GLenum mode, GLint start, GLsizei count) { + struct vbo_context *vbo = vbo_context(ctx); + struct vbo_exec_context *exec = &vbo->exec; int i; printf("vbo_exec_DrawArrays(mode 0x%x, start %d, count %d):\n", @@ -471,6 +507,90 @@ bind_arrays(struct gl_context *ctx) } +/** + * 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 +vbo_draw_arrays(struct gl_context *ctx, GLenum mode, GLint start, + GLsizei count, GLuint numInstances) +{ + struct vbo_context *vbo = vbo_context(ctx); + struct vbo_exec_context *exec = &vbo->exec; + struct _mesa_prim prim[2]; + + bind_arrays(ctx); + + /* Again... because we may have changed the bitmask of per-vertex varying + * attributes. If we regenerate the fixed-function vertex program now + * we may be able to prune down the number of vertex attributes which we + * need in the shader. + */ + if (ctx->NewState) + _mesa_update_state(ctx); + + prim[0].begin = 1; + prim[0].end = 1; + prim[0].weak = 0; + prim[0].pad = 0; + prim[0].mode = mode; + prim[0].start = 0; /* filled in below */ + prim[0].count = 0; /* filled in below */ + prim[0].indexed = 0; + prim[0].basevertex = 0; + prim[0].num_instances = numInstances; + + /* Implement the primitive restart index */ + if (ctx->Array.PrimitiveRestart && ctx->Array.RestartIndex < count) { + GLuint primCount = 0; + + if (ctx->Array.RestartIndex == start) { + /* special case: RestartIndex at beginning */ + if (count > 1) { + prim[0].start = start + 1; + prim[0].count = count - 1; + primCount = 1; + } + } + else if (ctx->Array.RestartIndex == start + count - 1) { + /* special case: RestartIndex at end */ + if (count > 1) { + prim[0].start = start; + prim[0].count = count - 1; + primCount = 1; + } + } + else { + /* general case: RestartIndex in middle, split into two prims */ + prim[0].start = start; + prim[0].count = ctx->Array.RestartIndex - start; + + prim[1] = prim[0]; + prim[1].start = ctx->Array.RestartIndex + 1; + prim[1].count = count - prim[1].start; + + primCount = 2; + } + + if (primCount > 0) { + /* draw one or two prims */ + vbo->draw_prims(ctx, exec->array.inputs, prim, primCount, NULL, + GL_TRUE, start, start + count - 1); + } + } + else { + /* no prim restart */ + prim[0].start = start; + prim[0].count = count; + + vbo->draw_prims(ctx, exec->array.inputs, prim, 1, NULL, + GL_TRUE, start, start + count - 1); + } +} + + /** * Called from glDrawArrays when in immediate mode (not display list mode). @@ -479,9 +599,6 @@ static void GLAPIENTRY vbo_exec_DrawArrays(GLenum mode, GLint start, GLsizei count) { GET_CURRENT_CONTEXT(ctx); - struct vbo_context *vbo = vbo_context(ctx); - struct vbo_exec_context *exec = &vbo->exec; - struct _mesa_prim prim[1]; if (MESA_VERBOSE & VERBOSE_DRAW) _mesa_debug(ctx, "glDrawArrays(%s, %d, %d)\n", @@ -496,41 +613,13 @@ vbo_exec_DrawArrays(GLenum mode, GLint start, GLsizei count) return; } -#if 0 - check_draw_arrays_data(ctx, start, count); -#else - (void) check_draw_arrays_data; -#endif - - bind_arrays( ctx ); - - /* Again... because we may have changed the bitmask of per-vertex varying - * attributes. If we regenerate the fixed-function vertex program now - * we may be able to prune down the number of vertex attributes which we - * need in the shader. - */ - if (ctx->NewState) - _mesa_update_state( ctx ); - - prim[0].begin = 1; - prim[0].end = 1; - prim[0].weak = 0; - prim[0].pad = 0; - prim[0].mode = mode; - prim[0].start = start; - prim[0].count = count; - prim[0].indexed = 0; - prim[0].basevertex = 0; - prim[0].num_instances = 1; + if (0) + check_draw_arrays_data(ctx, start, count); - vbo->draw_prims( ctx, exec->array.inputs, prim, 1, NULL, - GL_TRUE, start, start + count - 1 ); + vbo_draw_arrays(ctx, mode, start, count, 1); -#if 0 - print_draw_arrays(ctx, exec, mode, start, count); -#else - (void) print_draw_arrays; -#endif + if (0) + print_draw_arrays(ctx, mode, start, count); } @@ -543,9 +632,6 @@ vbo_exec_DrawArraysInstanced(GLenum mode, GLint start, GLsizei count, GLsizei primcount) { GET_CURRENT_CONTEXT(ctx); - struct vbo_context *vbo = vbo_context(ctx); - struct vbo_exec_context *exec = &vbo->exec; - struct _mesa_prim prim[1]; if (MESA_VERBOSE & VERBOSE_DRAW) _mesa_debug(ctx, "glDrawArraysInstanced(%s, %d, %d, %d)\n", @@ -560,37 +646,13 @@ vbo_exec_DrawArraysInstanced(GLenum mode, GLint start, GLsizei count, return; } -#if 0 /* debug */ - check_draw_arrays_data(ctx, start, count); -#endif + if (0) + check_draw_arrays_data(ctx, start, count); - bind_arrays( ctx ); + vbo_draw_arrays(ctx, mode, start, count, primcount); - /* Again... because we may have changed the bitmask of per-vertex varying - * attributes. If we regenerate the fixed-function vertex program now - * we may be able to prune down the number of vertex attributes which we - * need in the shader. - */ - if (ctx->NewState) - _mesa_update_state( ctx ); - - prim[0].begin = 1; - prim[0].end = 1; - prim[0].weak = 0; - prim[0].pad = 0; - prim[0].mode = mode; - prim[0].start = start; - prim[0].count = count; - prim[0].indexed = 0; - prim[0].basevertex = 0; - prim[0].num_instances = primcount; - - vbo->draw_prims( ctx, exec->array.inputs, prim, 1, NULL, - GL_TRUE, start, start + count - 1 ); - -#if 0 /* debug */ - print_draw_arrays(ctx, exec, mode, start, count); -#endif + if (0) + print_draw_arrays(ctx, mode, start, count); } @@ -1003,6 +1065,8 @@ vbo_validated_multidrawelements(struct gl_context *ctx, GLenum mode, /* 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_type_size != 1) { for (i = 0; i < primcount; i++) { diff --git a/src/mesa/vbo/vbo_save_api.c b/src/mesa/vbo/vbo_save_api.c index 8d66e14ab31..19c4b15d5fb 100644 --- a/src/mesa/vbo/vbo_save_api.c +++ b/src/mesa/vbo/vbo_save_api.c @@ -896,6 +896,18 @@ static void GLAPIENTRY _save_Begin( GLenum mode ) } +static void GLAPIENTRY _save_PrimitiveRestartNV( void ) +{ + GLenum curPrim; + GET_CURRENT_CONTEXT( ctx ); + + curPrim = ctx->Driver.CurrentSavePrimitive; + + _save_End(); + _save_Begin(curPrim); +} + + /* Unlike the functions above, these are to be hooked into the vtxfmt * maintained in ctx->ListState, active when the list is known or * suspected to be outside any begin/end primitive. @@ -1003,6 +1015,7 @@ static void _save_vtxfmt_init( struct gl_context *ctx ) vfmt->Color4fv = _save_Color4fv; vfmt->EdgeFlag = _save_EdgeFlag; vfmt->End = _save_End; + vfmt->PrimitiveRestartNV = _save_PrimitiveRestartNV; vfmt->FogCoordfEXT = _save_FogCoordfEXT; vfmt->FogCoordfvEXT = _save_FogCoordfvEXT; vfmt->Indexf = _save_Indexf; -- 2.30.2