st/mesa: Implement primitive restart in software
authorJakob Bornecrantz <jakob@vmware.com>
Fri, 15 Apr 2011 20:13:04 +0000 (21:13 +0100)
committerBrian Paul <brianp@vmware.com>
Wed, 19 Oct 2011 16:10:12 +0000 (10:10 -0600)
src/mesa/state_tracker/st_context.h
src/mesa/state_tracker/st_draw.c
src/mesa/state_tracker/st_extensions.c

index 0a322022149a5cd2ebcbe7d7835c25a3adb53ffb..c60780989638843981814760a5ffb85a9781375d 100644 (file)
@@ -75,6 +75,7 @@ struct st_context
    struct draw_stage *feedback_stage;  /**< For GL_FEEDBACK rendermode */
    struct draw_stage *selection_stage;  /**< For GL_SELECT rendermode */
    struct draw_stage *rastpos_stage;  /**< For glRasterPos */
+   GLboolean sw_primitive_restart;
 
 
    /* On old libGL's for linux we need to invalidate the drawables
index 5748020842fd75a0d77ae7e801307831e8bdde91..f027aea150f936f13781337389613a13f602e061 100644 (file)
@@ -582,6 +582,118 @@ check_uniforms(struct gl_context *ctx)
    }
 }
 
+/** Helper code for primitive restart fallback */
+#define DO_DRAW(pipe, cur_start, cur_count) \
+   do { \
+      info.start = cur_start; \
+      info.count = cur_count; \
+      if (u_trim_pipe_prim(info.mode, &info.count)) \
+         pipe->draw_vbo(pipe, &info); \
+   } while(0)
+      
+/** More helper code for primitive restart fallback */
+#define PRIM_RESTART_LOOP(elements) \
+   do { \
+      for (i = start; i < end; i++) { \
+         if (elements[i] == info.restart_index) { \
+            if (cur_count > 0) { \
+               /* draw elts up to prev pos */ \
+               DO_DRAW(pipe, cur_start, cur_count); \
+            } \
+            /* begin new prim at next elt */ \
+            cur_start = i + 1; \
+            cur_count = 0; \
+         } \
+         else { \
+            cur_count++; \
+         } \
+      } \
+      if (cur_count > 0) { \
+         DO_DRAW(pipe, cur_start, cur_count); \
+      } \
+   } while (0)
+
+static void
+handle_fallback_primitive_restart(struct pipe_context *pipe,
+                                  struct pipe_index_buffer *ibuffer,
+                                  struct pipe_draw_info *orig_info)
+{
+   const unsigned start = orig_info->start;
+   const unsigned count = orig_info->count;
+   const unsigned end = start + count;
+   struct pipe_draw_info info = *orig_info;
+   struct pipe_transfer *transfer;
+   unsigned instance, i, cur_start, cur_count;
+   void *ptr;
+
+   info.primitive_restart = FALSE;
+
+   /* split the draw_arrays calls into two */
+   if (!info.indexed) {
+#if 0
+       /* handled by VBO */
+       if (info.restart_index >= info.min_index) {
+          info.count = MIN(info.restart_index-1, info.max_index) - info.start + 1;
+          if (u_trim_pipe_prim(info.mode, &info.count))
+             pipe->draw_vbo(pipe, &info);
+       }
+
+       if (info.restart_index <= info.max_index) {
+          info.start = MAX(info.min_index, info.restart_index + 1);
+          info.count = info.max_index - info.start + 1;
+          if (u_trim_pipe_prim(info.mode, &info.count))
+             pipe->draw_vbo(pipe, &info);
+       }
+#endif
+       if (u_trim_pipe_prim(info.mode, &info.count))
+          pipe->draw_vbo(pipe, &info);
+
+       return;
+   }
+
+   /* info.indexed == TRUE */
+   assert(ibuffer);
+   assert(ibuffer->buffer);
+
+   ptr = pipe_buffer_map(pipe, ibuffer->buffer, PIPE_TRANSFER_READ, &transfer);
+   if (!ptr)
+     return;
+   ptr = ADD_POINTERS(ptr, ibuffer->offset);
+
+   /* Need to loop over instances as well to preserve draw order */
+   for (instance = 0; instance < orig_info->instance_count; instance++) {
+      info.start_instance = instance + orig_info->start_instance;
+      info.instance_count = 1;
+      cur_start = start;
+      cur_count = 0;
+
+      switch (ibuffer->index_size) {
+      case 1:
+         {
+            const ubyte *elt_ub = (const ubyte *)ptr; 
+            PRIM_RESTART_LOOP(elt_ub);
+         }
+         break;
+      case 2:
+         {
+            const ushort *elt_us = (const ushort *)ptr;
+            PRIM_RESTART_LOOP(elt_us);
+         }
+         break;
+      case 4:
+         {
+            const uint *elt_ui = (const uint *)ptr;
+            PRIM_RESTART_LOOP(elt_ui);
+         }
+         break;
+      default:
+         assert(0 && "bad index_size in handle_fallback_primitive_restart()");
+      }
+   }
+
+   pipe_buffer_unmap(pipe, transfer);
+}
+
 
 /**
  * Translate OpenGL primtive type (GL_POINTS, GL_TRIANGLE_STRIP, etc) to
@@ -794,7 +906,22 @@ st_draw_vbo(struct gl_context *ctx,
          info.max_index = info.start + info.count - 1;
       }
 
-      if (u_trim_pipe_prim(info.mode, &info.count))
+      if (info.primitive_restart) {
+         /*
+          * Handle primitive restart for drivers that doesn't support it.
+          *
+          * The VBO module handles restart inside of draw_arrays for us,
+          * but we should still remove the primitive_restart flag on the
+          * info struct, the fallback function does this for us. Just
+          * remove the flag for all drivers in this case as well.
+          */
+         if (st->sw_primitive_restart || !info.indexed)
+            handle_fallback_primitive_restart(pipe, &ibuffer, &info);
+         else
+            /* don't trim, restarts might be inside index list */
+            pipe->draw_vbo(pipe, &info);
+      }
+      else if (u_trim_pipe_prim(info.mode, &info.count))
          pipe->draw_vbo(pipe, &info);
    }
 
index a1f029089d0e84b6373a8a6de8ed1ac02b7f7b30..37f36de93816c9d12c621767a6f58cc739d2f00a 100644 (file)
@@ -555,8 +555,9 @@ void st_init_extensions(struct st_context *st)
 #endif
    }
 
-   if (screen->get_param(screen, PIPE_CAP_PRIMITIVE_RESTART)) {
-      ctx->Extensions.NV_primitive_restart = GL_TRUE;
+   ctx->Extensions.NV_primitive_restart = GL_TRUE;
+   if (!screen->get_param(screen, PIPE_CAP_PRIMITIVE_RESTART)) {
+      st->sw_primitive_restart = GL_TRUE;
    }
 
    if (screen->get_param(screen, PIPE_CAP_DEPTH_CLAMP)) {