geometry shaders: make gs work with changable primitives and variable number of vertices
[mesa.git] / src / gallium / auxiliary / draw / draw_pt_fetch_shade_pipeline.c
index 932113783d09e57b58ede769e24c3fc600f279c7..afc146c602309a03d11f014bd86b1cb7fbfa7abc 100644 (file)
@@ -32,7 +32,7 @@
 #include "draw/draw_vertex.h"
 #include "draw/draw_pt.h"
 #include "draw/draw_vs.h"
-#include "translate/translate.h"
+#include "draw/draw_gs.h"
 
 
 struct fetch_pipeline_middle_end {
@@ -40,24 +40,29 @@ struct fetch_pipeline_middle_end {
    struct draw_context *draw;
 
    struct pt_emit *emit;
+   struct pt_so_emit *so_emit;
    struct pt_fetch *fetch;
    struct pt_post_vs *post_vs;
 
    unsigned vertex_data_offset;
    unsigned vertex_size;
-   unsigned prim;
+   unsigned input_prim;
+   unsigned output_prim;
    unsigned opt;
 };
 
 
 static void fetch_pipeline_prepare( struct draw_pt_middle_end *middle,
-                                    unsigned prim,
+                                    unsigned in_prim,
+                                    unsigned out_prim,
                                    unsigned opt,
                                     unsigned *max_vertices )
 {
    struct fetch_pipeline_middle_end *fpme = (struct fetch_pipeline_middle_end *)middle;
    struct draw_context *draw = fpme->draw;
    struct draw_vertex_shader *vs = draw->vs.vertex_shader;
+   unsigned i;
+   unsigned instance_id_index = ~0;
 
    /* Add one to num_outputs because the pipeline occasionally tags on
     * an additional texcoord, eg for AA lines.
@@ -65,7 +70,17 @@ static void fetch_pipeline_prepare( struct draw_pt_middle_end *middle,
    unsigned nr = MAX2( vs->info.num_inputs,
                       vs->info.num_outputs + 1 );
 
-   fpme->prim = prim;
+   /* Scan for instanceID system value.
+    */
+   for (i = 0; i < vs->info.num_inputs; i++) {
+      if (vs->info.input_semantic_name[i] == TGSI_SEMANTIC_INSTANCEID) {
+         instance_id_index = i;
+         break;
+      }
+   }
+
+   fpme->input_prim = in_prim;
+   fpme->output_prim = out_prim;
    fpme->opt = opt;
 
    /* Always leave room for the vertex header whether we need it or
@@ -78,20 +93,22 @@ static void fetch_pipeline_prepare( struct draw_pt_middle_end *middle,
 
    draw_pt_fetch_prepare( fpme->fetch, 
                           vs->info.num_inputs,
-                         fpme->vertex_size );
+                          fpme->vertex_size,
+                          instance_id_index );
    /* XXX: it's not really gl rasterization rules we care about here,
     * but gl vs dx9 clip spaces.
     */
    draw_pt_post_vs_prepare( fpme->post_vs,
                            (boolean)draw->bypass_clipping,
-                           (boolean)(draw->identity_viewport ||
-                           draw->rasterizer->bypass_vs_clip_and_viewport),
+                           (boolean)draw->identity_viewport,
                            (boolean)draw->rasterizer->gl_rasterization_rules,
-                           (draw->vs.edgeflag_output ? true : false) );    
+                           (draw->vs.edgeflag_output ? true : false) );
+
+   draw_pt_so_emit_prepare( fpme->so_emit, out_prim );
 
    if (!(opt & PT_PIPELINE)) {
-      draw_pt_emit_prepare( fpme->emit, 
-                           prim,
+      draw_pt_emit_prepare( fpme->emit,
+                           out_prim,
                             max_vertices );
 
       *max_vertices = MAX2( *max_vertices,
@@ -119,11 +136,18 @@ static void fetch_pipeline_run( struct draw_pt_middle_end *middle,
 {
    struct fetch_pipeline_middle_end *fpme = (struct fetch_pipeline_middle_end *)middle;
    struct draw_context *draw = fpme->draw;
-   struct draw_vertex_shader *shader = draw->vs.vertex_shader;
+   struct draw_vertex_shader *vshader = draw->vs.vertex_shader;
+   struct draw_geometry_shader *gshader = draw->gs.geometry_shader;
    unsigned opt = fpme->opt;
+   struct vertex_header *pipeline_verts;
    unsigned alloc_count = align( fetch_count, 4 );
 
-   struct vertex_header *pipeline_verts = 
+   if (draw->gs.geometry_shader &&
+       draw->gs.geometry_shader->max_output_vertices > fetch_count) {
+      alloc_count = align(draw->gs.geometry_shader->max_output_vertices, 4);
+   }
+
+   pipeline_verts =
       (struct vertex_header *)MALLOC(fpme->vertex_size * alloc_count);
 
    if (!pipeline_verts) {
@@ -141,21 +165,34 @@ static void fetch_pipeline_run( struct draw_pt_middle_end *middle,
                      (char *)pipeline_verts );
 
    /* Run the shader, note that this overwrites the data[] parts of
-    * the pipeline verts.  If there is no shader, eg if
-    * bypass_vs_clip_and_viewport, then the inputs == outputs, and are
-    * already in the correct place.
+    * the pipeline verts.
     */
    if (opt & PT_SHADE)
    {
-      shader->run_linear(shader, 
-                        (const float (*)[4])pipeline_verts->data,
-                        (      float (*)[4])pipeline_verts->data,
-                        (const float (*)[4])draw->pt.user.constants,
-                        fetch_count,
-                        fpme->vertex_size,
-                        fpme->vertex_size);
+      vshader->run_linear(vshader,
+                          (const float (*)[4])pipeline_verts->data,
+                          (      float (*)[4])pipeline_verts->data,
+                          draw->pt.user.vs_constants,
+                          fetch_count,
+                          fpme->vertex_size,
+                          fpme->vertex_size);
+      if (gshader)
+         fetch_count =
+            draw_geometry_shader_run(gshader,
+                                     (const float (*)[4])pipeline_verts->data,
+                                     (      float (*)[4])pipeline_verts->data,
+                                     draw->pt.user.gs_constants,
+                                     fetch_count,
+                                     fpme->vertex_size,
+                                     fpme->vertex_size);
    }
 
+   /* stream output needs to be done before clipping */
+   draw_pt_so_emit( fpme->so_emit,
+                   (const float (*)[4])pipeline_verts->data,
+                   fetch_count,
+                   fpme->vertex_size );
+
    if (draw_pt_post_vs_run( fpme->post_vs,
                            pipeline_verts,
                            fetch_count,
@@ -168,7 +205,7 @@ static void fetch_pipeline_run( struct draw_pt_middle_end *middle,
     */
    if (opt & PT_PIPELINE) {
       draw_pipeline_run( fpme->draw,
-                         fpme->prim,
+                         fpme->output_prim,
                          pipeline_verts,
                          fetch_count,
                          fpme->vertex_size,
@@ -196,10 +233,16 @@ static void fetch_pipeline_linear_run( struct draw_pt_middle_end *middle,
    struct fetch_pipeline_middle_end *fpme = (struct fetch_pipeline_middle_end *)middle;
    struct draw_context *draw = fpme->draw;
    struct draw_vertex_shader *shader = draw->vs.vertex_shader;
+   struct draw_geometry_shader *geometry_shader = draw->gs.geometry_shader;
    unsigned opt = fpme->opt;
+   struct vertex_header *pipeline_verts;
    unsigned alloc_count = align( count, 4 );
 
-   struct vertex_header *pipeline_verts =
+   if (geometry_shader && geometry_shader->max_output_vertices > count) {
+      alloc_count = align(geometry_shader->max_output_vertices, 4);
+   }
+
+   pipeline_verts =
       (struct vertex_header *)MALLOC(fpme->vertex_size * alloc_count);
 
    if (!pipeline_verts) {
@@ -217,21 +260,34 @@ static void fetch_pipeline_linear_run( struct draw_pt_middle_end *middle,
                              (char *)pipeline_verts );
 
    /* Run the shader, note that this overwrites the data[] parts of
-    * the pipeline verts.  If there is no shader, ie if
-    * bypass_vs_clip_and_viewport, then the inputs == outputs, and are
-    * already in the correct place.
+    * the pipeline verts.
     */
    if (opt & PT_SHADE)
    {
       shader->run_linear(shader,
                         (const float (*)[4])pipeline_verts->data,
                         (      float (*)[4])pipeline_verts->data,
-                        (const float (*)[4])draw->pt.user.constants,
+                         draw->pt.user.vs_constants,
                         count,
                         fpme->vertex_size,
                         fpme->vertex_size);
+
+      if (geometry_shader)
+         count = draw_geometry_shader_run(geometry_shader,
+                                          (const float (*)[4])pipeline_verts->data,
+                                          (      float (*)[4])pipeline_verts->data,
+                                          draw->pt.user.gs_constants,
+                                          count,
+                                          fpme->vertex_size,
+                                          fpme->vertex_size);
    }
 
+   /* stream output needs to be done before clipping */
+   draw_pt_so_emit( fpme->so_emit,
+                   (const float (*)[4])pipeline_verts->data,
+                   count,
+                   fpme->vertex_size );
+
    if (draw_pt_post_vs_run( fpme->post_vs,
                            pipeline_verts,
                            count,
@@ -244,7 +300,7 @@ static void fetch_pipeline_linear_run( struct draw_pt_middle_end *middle,
     */
    if (opt & PT_PIPELINE) {
       draw_pipeline_run_linear( fpme->draw,
-                                fpme->prim,
+                                fpme->output_prim,
                                 pipeline_verts,
                                 count,
                                 fpme->vertex_size);
@@ -270,13 +326,20 @@ static boolean fetch_pipeline_linear_run_elts( struct draw_pt_middle_end *middle
    struct fetch_pipeline_middle_end *fpme = (struct fetch_pipeline_middle_end *)middle;
    struct draw_context *draw = fpme->draw;
    struct draw_vertex_shader *shader = draw->vs.vertex_shader;
+   struct draw_geometry_shader *geometry_shader = draw->gs.geometry_shader;
    unsigned opt = fpme->opt;
+   struct vertex_header *pipeline_verts;
    unsigned alloc_count = align( count, 4 );
 
-   struct vertex_header *pipeline_verts =
+   if (draw->gs.geometry_shader &&
+       draw->gs.geometry_shader->max_output_vertices > count) {
+      alloc_count = align(draw->gs.geometry_shader->max_output_vertices, 4);
+   }
+
+   pipeline_verts =
       (struct vertex_header *)MALLOC(fpme->vertex_size * alloc_count);
 
-   if (!pipeline_verts) 
+   if (!pipeline_verts)
       return FALSE;
 
    /* Fetch into our vertex buffer
@@ -287,21 +350,34 @@ static boolean fetch_pipeline_linear_run_elts( struct draw_pt_middle_end *middle
                              (char *)pipeline_verts );
 
    /* Run the shader, note that this overwrites the data[] parts of
-    * the pipeline verts.  If there is no shader, ie if
-    * bypass_vs_clip_and_viewport, then the inputs == outputs, and are
-    * already in the correct place.
+    * the pipeline verts.
     */
    if (opt & PT_SHADE)
    {
       shader->run_linear(shader,
                         (const float (*)[4])pipeline_verts->data,
                         (      float (*)[4])pipeline_verts->data,
-                        (const float (*)[4])draw->pt.user.constants,
+                         draw->pt.user.vs_constants,
                         count,
                         fpme->vertex_size,
                         fpme->vertex_size);
+
+      if (geometry_shader)
+         count = draw_geometry_shader_run(geometry_shader,
+                                          (const float (*)[4])pipeline_verts->data,
+                                          (      float (*)[4])pipeline_verts->data,
+                                          draw->pt.user.gs_constants,
+                                          count,
+                                          fpme->vertex_size,
+                                          fpme->vertex_size);
    }
 
+   /* stream output needs to be done before clipping */
+   draw_pt_so_emit( fpme->so_emit,
+                   (const float (*)[4])pipeline_verts->data,
+                   count,
+                   fpme->vertex_size );
+
    if (draw_pt_post_vs_run( fpme->post_vs,
                            pipeline_verts,
                            count,
@@ -314,7 +390,7 @@ static boolean fetch_pipeline_linear_run_elts( struct draw_pt_middle_end *middle
     */
    if (opt & PT_PIPELINE) {
       draw_pipeline_run( fpme->draw,
-                         fpme->prim,
+                         fpme->output_prim,
                          pipeline_verts,
                          count,
                          fpme->vertex_size,
@@ -351,6 +427,9 @@ static void fetch_pipeline_destroy( struct draw_pt_middle_end *middle )
    if (fpme->emit)
       draw_pt_emit_destroy( fpme->emit );
 
+   if (fpme->so_emit)
+      draw_pt_so_emit_destroy( fpme->so_emit );
+
    if (fpme->post_vs)
       draw_pt_post_vs_destroy( fpme->post_vs );
 
@@ -385,6 +464,10 @@ struct draw_pt_middle_end *draw_pt_fetch_pipeline_or_emit( struct draw_context *
    if (!fpme->emit) 
       goto fail;
 
+   fpme->so_emit = draw_pt_so_emit_create( draw );
+   if (!fpme->so_emit)
+      goto fail;
+
    return &fpme->base;
 
  fail: