vbo: support for primitive restart
authorBrian Paul <brianp@vmware.com>
Fri, 22 Oct 2010 01:03:38 +0000 (19:03 -0600)
committerBrian Paul <brianp@vmware.com>
Fri, 22 Oct 2010 01:03:38 +0000 (19:03 -0600)
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
src/mesa/vbo/vbo_exec_array.c
src/mesa/vbo/vbo_save_api.c

index 80ca1d866e7fc9029ede31cc079d196c996d8e58..1ef491741930fa3dc3fc30682c8459aea318c2f1 100644 (file)
@@ -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_);
index f46ba636fe76fdac438e9e8ce8bc88966c3c530e..dd36cc32a706fa1e1a4f1c57509960ce8348bdb4 100644 (file)
@@ -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++) {
index 8d66e14ab3151a6a7afb95ecacd9fdf6dfb5bbe7..19c4b15d5fb8b25d5bd89427868d0fb842aaa9e9 100644 (file)
@@ -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;