svga: implement user index buffers
[mesa.git] / src / gallium / drivers / svga / svga_pipe_draw.c
index 4e0c499dc3ec91fcec0fe5a847b9da2fd1e64b57..bbd443049507da7dcbaa0bb0b655c780fc7189e4 100644 (file)
  *
  **********************************************************/
 
-#include "svga_cmd.h"
 
-#include "pipe/p_inlines.h"
+#include "util/u_format.h"
+#include "util/u_helpers.h"
+#include "util/u_inlines.h"
 #include "util/u_prim.h"
+#include "util/u_prim_restart.h"
 #include "util/u_time.h"
+#include "util/u_upload_mgr.h"
 #include "indices/u_indices.h"
 
 #include "svga_hw_reg.h"
+#include "svga_cmd.h"
 #include "svga_context.h"
 #include "svga_screen.h"
 #include "svga_draw.h"
+#include "svga_shader.h"
 #include "svga_state.h"
+#include "svga_surface.h"
 #include "svga_swtnl.h"
 #include "svga_debug.h"
-
-
+#include "svga_resource_buffer.h"
 
 static enum pipe_error
 retry_draw_range_elements( struct svga_context *svga,
-                           struct pipe_buffer *index_buffer,
+                           struct pipe_resource *index_buffer,
                            unsigned index_size,
+                           int index_bias,
                            unsigned min_index,
                            unsigned max_index,
-                           unsigned prim, 
-                           unsigned start, 
+                           enum pipe_prim_type prim,
+                           unsigned start,
                            unsigned count,
+                           unsigned start_instance,
+                           unsigned instance_count,
                            boolean do_retry )
 {
-   enum pipe_error ret = 0;
+   enum pipe_error ret = PIPE_OK;
 
-   svga_hwtnl_set_unfilled( svga->hwtnl,
-                            svga->curr.rast->hw_unfilled );
-
-   svga_hwtnl_set_flatshade( svga->hwtnl,
-                             svga->curr.rast->templ.flatshade,
-                             svga->curr.rast->templ.flatshade_first );
+   SVGA_STATS_TIME_PUSH(svga_sws(svga), SVGA_STATS_TIME_DRAWELEMENTS);
 
+   svga_hwtnl_set_fillmode(svga->hwtnl, svga->curr.rast->hw_fillmode);
 
    ret = svga_update_state( svga, SVGA_STATE_HW_DRAW );
-   if (ret)
+   if (ret != PIPE_OK)
       goto retry;
 
+   /** determine if flatshade is to be used after svga_update_state()
+    *  in case the fragment shader is changed.
+    */
+   svga_hwtnl_set_flatshade(svga->hwtnl,
+                            svga->curr.rast->templ.flatshade ||
+                            svga->state.hw_draw.fs->uses_flat_interp,
+                            svga->curr.rast->templ.flatshade_first);
+
    ret = svga_hwtnl_draw_range_elements( svga->hwtnl,
-                                         index_buffer, index_size,
+                                         index_buffer, index_size, index_bias,
                                          min_index, max_index,
-                                         prim, start, count, 0 );
-   if (ret)
+                                         prim, start, count,
+                                         start_instance, instance_count);
+   if (ret != PIPE_OK)
       goto retry;
 
-   if (svga->curr.any_user_vertex_buffers) {
-      ret = svga_hwtnl_flush( svga->hwtnl );
-      if (ret)
-         goto retry;
-   }
-
-   return PIPE_OK;
+   goto done;
 
 retry:
    svga_context_flush( svga, NULL );
 
    if (do_retry)
    {
-      return retry_draw_range_elements( svga,
-                                        index_buffer, index_size,
-                                        min_index, max_index,
-                                        prim, start, count,
-                                        FALSE );
+      ret = retry_draw_range_elements(svga,
+                                      index_buffer, index_size, index_bias,
+                                      min_index, max_index,
+                                      prim, start, count,
+                                      start_instance, instance_count, FALSE);
    }
 
+done:
+   SVGA_STATS_TIME_POP(svga_sws(svga));
    return ret;
 }
 
 
 static enum pipe_error
 retry_draw_arrays( struct svga_context *svga,
-                   unsigned prim, 
-                   unsigned start, 
-                   unsigned count,
+                   enum pipe_prim_type prim, unsigned start, unsigned count,
+                   unsigned start_instance, unsigned instance_count,
                    boolean do_retry )
 {
    enum pipe_error ret;
 
-   svga_hwtnl_set_unfilled( svga->hwtnl,
-                            svga->curr.rast->hw_unfilled );
+   SVGA_STATS_TIME_PUSH(svga_sws(svga), SVGA_STATS_TIME_DRAWARRAYS);
 
-   svga_hwtnl_set_flatshade( svga->hwtnl,
-                             svga->curr.rast->templ.flatshade,
-                             svga->curr.rast->templ.flatshade_first );
+   svga_hwtnl_set_fillmode(svga->hwtnl, svga->curr.rast->hw_fillmode);
 
    ret = svga_update_state( svga, SVGA_STATE_HW_DRAW );
-   if (ret)
+   if (ret != PIPE_OK)
       goto retry;
 
-   ret = svga_hwtnl_draw_arrays( svga->hwtnl, prim,
-                                 start, count );
-   if (ret)
+   /** determine if flatshade is to be used after svga_update_state()
+    *  in case the fragment shader is changed.
+    */
+   svga_hwtnl_set_flatshade(svga->hwtnl,
+                            svga->curr.rast->templ.flatshade ||
+                            svga->state.hw_draw.fs->uses_flat_interp,
+                            svga->curr.rast->templ.flatshade_first);
+
+   ret = svga_hwtnl_draw_arrays(svga->hwtnl, prim, start, count,
+                                start_instance, instance_count);
+   if (ret != PIPE_OK)
       goto retry;
 
-   if (svga->curr.any_user_vertex_buffers) {
-      ret = svga_hwtnl_flush( svga->hwtnl );
-      if (ret)
-         goto retry;
-   }
-
-   return 0;
+   goto done;
 
 retry:
-   if (ret == PIPE_ERROR_OUT_OF_MEMORY && do_retry) 
+   if (ret == PIPE_ERROR_OUT_OF_MEMORY && do_retry)
    {
       svga_context_flush( svga, NULL );
 
-      return retry_draw_arrays( svga,
-                                prim,
-                                start,
-                                count,
-                                FALSE );
+      ret = retry_draw_arrays(svga, prim, start, count,
+                              start_instance, instance_count,
+                              FALSE);
    }
 
+done:
+   SVGA_STATS_TIME_POP(svga_sws(svga));
    return ret;
 }
 
 
+/**
+ * Determine if we need to implement primitive restart with a fallback
+ * path which breaks the original primitive into sub-primitive at the
+ * restart indexes.
+ */
+static boolean
+need_fallback_prim_restart(const struct svga_context *svga,
+                           const struct pipe_draw_info *info)
+{
+   if (info->primitive_restart && info->indexed) {
+      if (!svga_have_vgpu10(svga))
+         return TRUE;
+      else if (!svga->state.sw.need_swtnl) {
+         if (svga->curr.ib.index_size == 1)
+            return TRUE; /* no device support for 1-byte indexes */
+         else if (svga->curr.ib.index_size == 2)
+            return info->restart_index != 0xffff;
+         else
+            return info->restart_index != 0xffffffff;
+      }
+   }
 
+   return FALSE;
+}
 
 
 static void
-svga_draw_range_elements( struct pipe_context *pipe,
-                          struct pipe_buffer *index_buffer,
-                          unsigned index_size,
-                          unsigned min_index,
-                          unsigned max_index,
-                          unsigned prim, unsigned start, unsigned count)
+svga_draw_vbo(struct pipe_context *pipe, const struct pipe_draw_info *info)
 {
    struct svga_context *svga = svga_context( pipe );
-   unsigned reduced_prim = u_reduced_prim(prim);
+   unsigned reduced_prim = u_reduced_prim( info->mode );
+   unsigned count = info->count;
    enum pipe_error ret = 0;
+   boolean needed_swtnl;
+
+   SVGA_STATS_TIME_PUSH(svga_sws(svga), SVGA_STATS_TIME_DRAWVBO);
+
+   svga->hud.num_draw_calls++;  /* for SVGA_QUERY_NUM_DRAW_CALLS */
+
+   if (u_reduced_prim(info->mode) == PIPE_PRIM_TRIANGLES &&
+       svga->curr.rast->templ.cull_face == PIPE_FACE_FRONT_AND_BACK)
+      goto done;
 
-   if (!u_trim_pipe_prim( prim, &count ))
+   /* Upload a user index buffer. */
+   struct pipe_index_buffer ibuffer_saved = {};
+   if (info->indexed && svga->curr.ib.user_buffer &&
+       !util_save_and_upload_index_buffer(pipe, info, &svga->curr.ib,
+                                          &ibuffer_saved)) {
       return;
+   }
 
    /*
     * Mark currently bound target surfaces as dirty
@@ -176,83 +216,84 @@ svga_draw_range_elements( struct pipe_context *pipe,
       svga->curr.reduced_prim = reduced_prim;
       svga->dirty |= SVGA_NEW_REDUCED_PRIMITIVE;
    }
-   
+
+   if (need_fallback_prim_restart(svga, info)) {
+      enum pipe_error r;
+      r = util_draw_vbo_without_prim_restart(pipe, &svga->curr.ib, info);
+      assert(r == PIPE_OK);
+      (void) r;
+      goto done;
+   }
+
+   if (!u_trim_pipe_prim( info->mode, &count ))
+      goto done;
+
+   needed_swtnl = svga->state.sw.need_swtnl;
+
    svga_update_state_retry( svga, SVGA_STATE_NEED_SWTNL );
 
-#ifdef DEBUG
-   if (svga->curr.vs->base.id == svga->debug.disable_shader ||
-       svga->curr.fs->base.id == svga->debug.disable_shader)
-      return;
-#endif
+   if (svga->state.sw.need_swtnl) {
+      svga->hud.num_fallbacks++;  /* for SVGA_QUERY_NUM_FALLBACKS */
+      if (!needed_swtnl) {
+         /*
+          * We're switching from HW to SW TNL.  SW TNL will require mapping all
+          * currently bound vertex buffers, some of which may already be
+          * referenced in the current command buffer as result of previous HW
+          * TNL. So flush now, to prevent the context to flush while a referred
+          * vertex buffer is mapped.
+          */
+
+         svga_context_flush(svga, NULL);
+      }
 
-   if (svga->state.sw.need_swtnl)
-   {
-      ret = svga_swtnl_draw_range_elements( svga, 
-                                            index_buffer, 
-                                            index_size,
-                                            min_index, max_index,
-                                            prim,
-                                            start, count );
+      /* Avoid leaking the previous hwtnl bias to swtnl */
+      svga_hwtnl_set_index_bias( svga->hwtnl, 0 );
+      ret = svga_swtnl_draw_vbo( svga, info );
    }
    else {
-      if (index_buffer) {
+      if (info->indexed && svga->curr.ib.buffer) {
+         unsigned offset;
+
+         assert(svga->curr.ib.offset % svga->curr.ib.index_size == 0);
+         offset = svga->curr.ib.offset / svga->curr.ib.index_size;
+
          ret = retry_draw_range_elements( svga,
-                                          index_buffer,
-                                          index_size,
-                                          min_index,
-                                          max_index,
-                                          prim,
-                                          start,
+                                          svga->curr.ib.buffer,
+                                          svga->curr.ib.index_size,
+                                          info->index_bias,
+                                          info->min_index,
+                                          info->max_index,
+                                          info->mode,
+                                          info->start + offset,
                                           count,
+                                          info->start_instance,
+                                          info->instance_count,
                                           TRUE );
       }
       else {
-         ret = retry_draw_arrays( svga, 
-                                  prim, 
-                                  start, 
-                                  count,
-                                  TRUE );
+         ret = retry_draw_arrays(svga, info->mode, info->start, count,
+                                 info->start_instance, info->instance_count,
+                                 TRUE);
       }
    }
 
-   if (SVGA_DEBUG & DEBUG_FLUSH) {
-      static unsigned id;
-      debug_printf("%s %d\n", __FUNCTION__, id++);
-      if (id > 1300)
-         util_time_sleep( 2000 );
+   /* XXX: Silence warnings, do something sensible here? */
+   (void)ret;
 
+   if (SVGA_DEBUG & DEBUG_FLUSH) {
       svga_hwtnl_flush_retry( svga );
       svga_context_flush(svga, NULL);
    }
-}
-
 
-static void
-svga_draw_elements( struct pipe_context *pipe,
-                    struct pipe_buffer *index_buffer,
-                    unsigned index_size,
-                    unsigned prim, unsigned start, unsigned count)
-{
-   svga_draw_range_elements( pipe, index_buffer,
-                             index_size,
-                             0, 0xffffffff,
-                             prim, start, count );
-}
+done:
+   if (info->indexed && ibuffer_saved.user_buffer)
+      pipe->set_index_buffer(pipe, &ibuffer_saved);
 
-static void
-svga_draw_arrays( struct pipe_context *pipe,
-                  unsigned prim, unsigned start, unsigned count)
-{
-   svga_draw_range_elements(pipe, NULL, 0, 
-                            start, start + count - 1, 
-                            prim, 
-                            start, count);
+   SVGA_STATS_TIME_POP(svga_sws(svga));
 }
 
 
 void svga_init_draw_functions( struct svga_context *svga )
 {
-   svga->pipe.draw_arrays = svga_draw_arrays;
-   svga->pipe.draw_elements = svga_draw_elements;
-   svga->pipe.draw_range_elements = svga_draw_range_elements;
+   svga->pipe.draw_vbo = svga_draw_vbo;
 }