svga: Add GL4.1(compatibility profile) support in svga driver
[mesa.git] / src / gallium / drivers / svga / svga_pipe_draw.c
index 5ebd17cf0eabc5c50685467353883625beefdd9e..e6fabfc995e136332cf34f94f40048d522684f8e 100644 (file)
  **********************************************************/
 
 
+#include "util/u_draw.h"
+#include "util/format/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 "svga_context.h"
+#include "svga_draw_private.h"
+#include "svga_screen.h"
 #include "svga_draw.h"
 #include "svga_shader.h"
 #include "svga_surface.h"
 #include "svga_debug.h"
 #include "svga_resource_buffer.h"
 
-/* Returns TRUE if we are currently using flat shading.
- */
-static boolean
-is_using_flat_shading(const struct svga_context *svga)
-{
-   return
-      svga->state.hw_draw.fs ? svga->state.hw_draw.fs->uses_flat_interp : FALSE;
-}
-
 
 static enum pipe_error
 retry_draw_range_elements(struct svga_context *svga,
                           const struct pipe_draw_info *info,
                           unsigned count)
 {
-   enum pipe_error ret;
-
    SVGA_STATS_TIME_PUSH(svga_sws(svga), SVGA_STATS_TIME_DRAWELEMENTS);
 
-   ret = svga_hwtnl_draw_range_elements(svga->hwtnl, info, count);
-   if (ret != PIPE_OK) {
-      svga_context_flush(svga, NULL);
-      ret = svga_hwtnl_draw_range_elements(svga->hwtnl, info, count);
-   }
+   SVGA_RETRY(svga, svga_hwtnl_draw_range_elements(svga->hwtnl, info, count));
 
-   assert (ret == PIPE_OK);
    SVGA_STATS_TIME_POP(svga_sws(svga));
-   return ret;
+   return PIPE_OK;
 }
 
 
 static enum pipe_error
-retry_draw_arrays(struct svga_context *svga,
-                  enum pipe_prim_type prim, unsigned start, unsigned count,
-                  unsigned start_instance, unsigned instance_count)
+retry_draw_arrays( struct svga_context *svga,
+                   enum pipe_prim_type prim, unsigned start, unsigned count,
+                   unsigned start_instance, unsigned instance_count,
+                   ubyte vertices_per_patch)
 {
    enum pipe_error ret;
 
    SVGA_STATS_TIME_PUSH(svga_sws(svga), SVGA_STATS_TIME_DRAWARRAYS);
 
-   for (unsigned try = 0; try < 2; try++) {
-      ret = svga_hwtnl_draw_arrays(svga->hwtnl, prim, start, count,
-                                   start_instance, instance_count);
-      if (ret == PIPE_OK)
-         break;
-      svga_context_flush(svga, NULL);
-   }
-
+   SVGA_RETRY_OOM(svga, ret, svga_hwtnl_draw_arrays(svga->hwtnl, prim, start,
+                                                    count, start_instance,
+                                                    instance_count,
+                                                    vertices_per_patch));
    SVGA_STATS_TIME_POP(svga_sws(svga));
    return ret;
 }
 
 
+/**
+ * Auto draw (get vertex count from a transform feedback result).
+ */
+static enum pipe_error
+retry_draw_auto(struct svga_context *svga,
+                const struct pipe_draw_info *info)
+{
+   assert(svga_have_sm5(svga));
+   assert(info->count_from_stream_output);
+   assert(info->instance_count == 1);
+   /* SO drawing implies core profile and none of these prim types */
+   assert(info->mode != PIPE_PRIM_QUADS &&
+          info->mode != PIPE_PRIM_QUAD_STRIP &&
+          info->mode != PIPE_PRIM_POLYGON);
+
+   if (info->mode == PIPE_PRIM_LINE_LOOP) {
+      /* XXX need to do a fallback */
+      assert(!"draw auto fallback not supported yet");
+      return PIPE_OK;
+   }
+   else {
+      SVGA3dPrimitiveRange range;
+      unsigned hw_count;
+
+      range.primType = svga_translate_prim(info->mode, 12, &hw_count,
+                                           info->vertices_per_patch);
+      range.primitiveCount = 0;
+      range.indexArray.surfaceId = SVGA3D_INVALID_ID;
+      range.indexArray.offset = 0;
+      range.indexArray.stride = 0;
+      range.indexWidth = 0;
+      range.indexBias = 0;
+
+      SVGA_RETRY(svga, svga_hwtnl_prim
+                 (svga->hwtnl, &range,
+                  0,    /* vertex count comes from SO buffer */
+                  0,    /* don't know min index */
+                  ~0u,  /* don't know max index */
+                  NULL, /* no index buffer */
+                  0,    /* start instance */
+                  1,    /* only 1 instance supported */
+                  NULL, /* indirect drawing info */
+                  info->count_from_stream_output));
+
+      return PIPE_OK;
+   }
+}
+
+
+/**
+ * Indirect draw (get vertex count, start index, etc. from a buffer object.
+ */
+static enum pipe_error
+retry_draw_indirect(struct svga_context *svga,
+                    const struct pipe_draw_info *info)
+{
+   assert(svga_have_sm5(svga));
+   assert(info->indirect);
+   /* indirect drawing implies core profile and none of these prim types */
+   assert(info->mode != PIPE_PRIM_QUADS &&
+          info->mode != PIPE_PRIM_QUAD_STRIP &&
+          info->mode != PIPE_PRIM_POLYGON);
+
+   if (info->mode == PIPE_PRIM_LINE_LOOP) {
+      /* need to do a fallback */
+      util_draw_indirect(&svga->pipe, info);
+      return PIPE_OK;
+   }
+   else {
+      SVGA3dPrimitiveRange range;
+      unsigned hw_count;
+
+      range.primType = svga_translate_prim(info->mode, 12, &hw_count,
+                                           info->vertices_per_patch);
+      range.primitiveCount = 0;  /* specified in indirect buffer */
+      range.indexArray.surfaceId = SVGA3D_INVALID_ID;
+      range.indexArray.offset = 0;
+      range.indexArray.stride = 0;
+      range.indexWidth = info->index_size;
+      range.indexBias = 0; /* specified in indirect buffer */
+
+      SVGA_RETRY(svga, svga_hwtnl_prim
+                 (svga->hwtnl, &range,
+                  0,   /* vertex count is in indirect buffer */
+                  0,   /* don't know min index */
+                  ~0u, /* don't know max index */
+                  info->index.resource,
+                  info->start_instance,
+                  0,   /* don't know instance count */
+                  info->indirect,
+                  NULL)); /* SO vertex count */
+
+      return PIPE_OK;
+   }
+}
+
+
 /**
  * Determine if we need to implement primitive restart with a fallback
  * path which breaks the original primitive into sub-primitive at the
@@ -116,6 +198,21 @@ need_fallback_prim_restart(const struct svga_context *svga,
 }
 
 
+/**
+ * A helper function to return the vertex count from the primitive count
+ * returned from the stream output statistics query for the specified stream.
+ */
+static unsigned
+get_vcount_from_stream_output(struct svga_context *svga,
+                              const struct pipe_draw_info *info,
+                              unsigned stream)
+{
+   unsigned primcount;
+   primcount = svga_get_primcount_from_stream_output(svga, stream);
+   return u_vertices_for_prims(info->mode, primcount);
+}
+
+
 static void
 svga_draw_vbo(struct pipe_context *pipe, const struct pipe_draw_info *info)
 {
@@ -147,6 +244,18 @@ svga_draw_vbo(struct pipe_context *pipe, const struct pipe_draw_info *info)
       svga->dirty |= SVGA_NEW_REDUCED_PRIMITIVE;
    }
 
+   if (svga->curr.vertices_per_patch != info->vertices_per_patch) {
+      svga->curr.vertices_per_patch = info->vertices_per_patch;
+
+      /* If input patch size changes, we need to notifiy the TCS
+       * code to reevaluate the shader variant since the
+       * vertices per patch count is a constant in the control
+       * point count declaration.
+       */
+      if (svga->curr.tcs || svga->curr.tes)
+         svga->dirty |= SVGA_NEW_TCS_PARAM;
+   }
+
    if (need_fallback_prim_restart(svga, info)) {
       enum pipe_error r;
       r = util_draw_vbo_without_prim_restart(pipe, info);
@@ -155,7 +264,8 @@ svga_draw_vbo(struct pipe_context *pipe, const struct pipe_draw_info *info)
       goto done;
    }
 
-   if (!u_trim_pipe_prim(info->mode, &count))
+   if (!info->indirect && !info->count_from_stream_output &&
+       !u_trim_pipe_prim(info->mode, &count))
       goto done;
 
    needed_swtnl = svga->state.sw.need_swtnl;
@@ -189,20 +299,53 @@ svga_draw_vbo(struct pipe_context *pipe, const struct pipe_draw_info *info)
       }
       svga_hwtnl_set_fillmode(svga->hwtnl, svga->curr.rast->hw_fillmode);
 
+      svga_update_state_retry(svga, SVGA_STATE_HW_DRAW);
+
       /** 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 ||
-                               is_using_flat_shading(svga),
+                               svga_is_using_flat_shading(svga),
                                svga->curr.rast->templ.flatshade_first);
 
-      if (info->index_size) {
+      if (info->count_from_stream_output) {
+         unsigned stream = 0;
+         assert(count == 0);
+
+         /* If the vertex count is from the stream output of a non-zero stream
+          * or the draw info specifies instancing, we will need a workaround
+          * since the draw_auto command does not support stream instancing.
+          * The workaround requires querying the vertex count from the
+          * stream output statistics query for the specified stream and then
+          * fallback to the regular draw function.
+          */
+
+         /* Check the stream index of the specified stream output target */
+         for (unsigned i = 0; i < ARRAY_SIZE(svga->so_targets); i++) {
+            if (svga->vcount_so_targets[i] == info->count_from_stream_output) {
+               stream = (svga->vcount_buffer_stream >> (i * 4)) & 0xf;
+               break;
+            }
+         }
+         if (info->instance_count > 1 || stream > 0) {
+            count = get_vcount_from_stream_output(svga, info, stream);
+         }
+      }
+
+      if (info->count_from_stream_output && count == 0) {
+         ret = retry_draw_auto(svga, info);
+      }
+      else if (info->indirect) {
+         ret = retry_draw_indirect(svga, info);
+      }
+      else if (info->index_size) {
          ret = retry_draw_range_elements(svga, info, count);
       }
       else {
          ret = retry_draw_arrays(svga, info->mode, info->start, count,
-                                 info->start_instance, info->instance_count);
+                                 info->start_instance, info->instance_count,
+                                 info->vertices_per_patch);
       }
    }