draw: inject frontface info into wireframe outputs
authorZack Rusin <zackr@vmware.com>
Wed, 31 Jul 2013 11:34:49 +0000 (07:34 -0400)
committerZack Rusin <zackr@vmware.com>
Sat, 3 Aug 2013 00:11:18 +0000 (20:11 -0400)
Draw module can decompose primitives into wireframe models, which
is a fancy word for 'lines', unfortunately that decomposition means
that we weren't able to preserve the original front-face info which
could be derived from the original primitives (lines don't have a
'face'). To fix it allow draw module to inject a fake face semantic
into outputs from which the backends can figure out the original
frontfacing info of the primitives.

Signed-off-by: Zack Rusin <zackr@vmware.com>
Reviewed-by: Roland Scheidegger <sroland@vmware.com>
Reviewed-by: Jose Fonseca <jfonseca@vmware.com>
14 files changed:
src/gallium/auxiliary/draw/draw_context.c
src/gallium/auxiliary/draw/draw_context.h
src/gallium/auxiliary/draw/draw_pipe.h
src/gallium/auxiliary/draw/draw_pipe_unfilled.c
src/gallium/drivers/i915/i915_state_derived.c
src/gallium/drivers/llvmpipe/lp_context.h
src/gallium/drivers/llvmpipe/lp_setup.c
src/gallium/drivers/llvmpipe/lp_setup_context.h
src/gallium/drivers/llvmpipe/lp_setup_line.c
src/gallium/drivers/llvmpipe/lp_setup_point.c
src/gallium/drivers/llvmpipe/lp_state_derived.c
src/gallium/drivers/r300/r300_state_derived.c
src/gallium/drivers/softpipe/sp_state_derived.c
src/gallium/drivers/svga/svga_swtnl_state.c

index 29bbc94d0ab4279bddd5227d9cc597d658e42b65..377b852f0520c790084b1745d4d987c6008fa193 100644 (file)
@@ -39,6 +39,7 @@
 #include "util/u_helpers.h"
 #include "util/u_prim.h"
 #include "draw_context.h"
+#include "draw_pipe.h"
 #include "draw_vs.h"
 #include "draw_gs.h"
 
@@ -540,6 +541,22 @@ draw_get_shader_info(const struct draw_context *draw)
    }
 }
 
+/**
+ * Prepare outputs slots from the draw module
+ *
+ * Certain parts of the draw module can emit additional
+ * outputs that can be quite useful to the backends, a good
+ * example of it is the process of decomposing primitives
+ * into wireframes (aka. lines) which normally would lose
+ * the face-side information, but using this method we can
+ * inject another shader output which passes the original
+ * face side information to the backend.
+ */
+void
+draw_prepare_shader_outputs(struct draw_context *draw)
+{
+   draw_unfilled_prepare_outputs(draw, draw->pipeline.unfilled);
+}
 
 /**
  * Ask the draw module for the location/slot of the given vertex attribute in
@@ -1007,3 +1024,29 @@ draw_stats_clipper_primitives(struct draw_context *draw,
       }
    }
 }
+
+
+/**
+ * Returns true if the draw module will inject the frontface
+ * info into the outputs.
+ *
+ * Given the specified primitive and rasterizer state
+ * the function will figure out if the draw module
+ * will inject the front-face information into shader
+ * outputs. This is done to preserve the front-facing
+ * info when decomposing primitives into wireframes.
+ */
+boolean
+draw_will_inject_frontface(const struct draw_context *draw)
+{
+   unsigned reduced_prim = u_reduced_prim(draw->pt.prim);
+   const struct pipe_rasterizer_state *rast = draw->rasterizer;
+
+   if (reduced_prim != PIPE_PRIM_TRIANGLES) {
+      return FALSE;
+   }
+
+   return (rast &&
+           (rast->fill_front != PIPE_POLYGON_MODE_FILL ||
+            rast->fill_back != PIPE_POLYGON_MODE_FILL));
+}
index c720806feca75cb51934d640bef3422b17e80e31..46d0de6f8538e9c8dba054ad8b1b55b3df6c6cb2 100644 (file)
@@ -126,10 +126,16 @@ draw_install_pstipple_stage(struct draw_context *draw, struct pipe_context *pipe
 struct tgsi_shader_info *
 draw_get_shader_info(const struct draw_context *draw);
 
+void
+draw_prepare_shader_outputs(struct draw_context *draw);
+
 int
 draw_find_shader_output(const struct draw_context *draw,
                         uint semantic_name, uint semantic_index);
 
+boolean
+draw_will_inject_frontface(const struct draw_context *draw);
+
 uint
 draw_num_shader_outputs(const struct draw_context *draw);
 
index 547e4bf4d058527ef3bad7cdc4b8b884d82ee4c0..70c286f433070e632781e3d87cfbd9e0bcf393d6 100644 (file)
@@ -103,6 +103,9 @@ void draw_pipe_passthrough_line(struct draw_stage *stage, struct prim_header *he
 void draw_pipe_passthrough_point(struct draw_stage *stage, struct prim_header *header);
 
 
+void draw_unfilled_prepare_outputs(struct draw_context *context,
+                                   struct draw_stage *stage);
+
 
 /**
  * Get a writeable copy of a vertex.
index d87741b91e7ce5c821c2e617a409a8076e7b1c5a..d8a603f61cb5d91a7cfdf90240ea246e0c5dcd1f 100644 (file)
@@ -47,6 +47,8 @@ struct unfilled_stage {
     * and PIPE_POLYGON_MODE_POINT,
     */
    unsigned mode[2];
+
+   int face_slot;
 };
 
 
@@ -55,8 +57,31 @@ static INLINE struct unfilled_stage *unfilled_stage( struct draw_stage *stage )
    return (struct unfilled_stage *)stage;
 }
 
+static void
+inject_front_face_info(struct draw_stage *stage,
+                       struct prim_header *header)
+{
+   struct unfilled_stage *unfilled = unfilled_stage(stage);
+   unsigned ccw = header->det < 0.0;
+   boolean is_front_face = (
+      (stage->draw->rasterizer->front_ccw && ccw) ||
+      (!stage->draw->rasterizer->front_ccw && !ccw));
+   unsigned slot = unfilled->face_slot;
+   struct vertex_header *v0 = header->v[0];
+   struct vertex_header *v1 = header->v[1];
+   struct vertex_header *v2 = header->v[2];
+
+   /* In case the backend doesn't care about it */
+   if (slot < 0) {
+      return;
+   }
 
+   v0->data[slot][0] = is_front_face;
+   v1->data[slot][0] = is_front_face;
+   v2->data[slot][0] = is_front_face;
+}
 
+   
 static void point( struct draw_stage *stage,
                   struct vertex_header *v0 )
 {
@@ -83,6 +108,8 @@ static void points( struct draw_stage *stage,
    struct vertex_header *v1 = header->v[1];
    struct vertex_header *v2 = header->v[2];
 
+   inject_front_face_info(stage, header);
+
    if ((header->flags & DRAW_PIPE_EDGE_FLAG_0) && v0->edgeflag) point( stage, v0 );
    if ((header->flags & DRAW_PIPE_EDGE_FLAG_1) && v1->edgeflag) point( stage, v1 );
    if ((header->flags & DRAW_PIPE_EDGE_FLAG_2) && v2->edgeflag) point( stage, v2 );
@@ -99,6 +126,8 @@ static void lines( struct draw_stage *stage,
    if (header->flags & DRAW_PIPE_RESET_STIPPLE)
       stage->next->reset_stipple_counter( stage->next );
 
+   inject_front_face_info(stage, header);
+
    if ((header->flags & DRAW_PIPE_EDGE_FLAG_2) && v2->edgeflag) line( stage, v2, v0 );
    if ((header->flags & DRAW_PIPE_EDGE_FLAG_0) && v0->edgeflag) line( stage, v0, v1 );
    if ((header->flags & DRAW_PIPE_EDGE_FLAG_1) && v1->edgeflag) line( stage, v1, v2 );
@@ -192,6 +221,26 @@ static void unfilled_destroy( struct draw_stage *stage )
    FREE( stage );
 }
 
+/*
+ * Try to allocate an output slot which we can use
+ * to preserve the front face information.
+ */
+void
+draw_unfilled_prepare_outputs( struct draw_context *draw,
+                               struct draw_stage *stage )
+{
+   struct unfilled_stage *unfilled = unfilled_stage(stage);
+   const struct pipe_rasterizer_state *rast = draw ? draw->rasterizer : 0;
+   if (rast &&
+       (rast->fill_front != PIPE_POLYGON_MODE_FILL ||
+        rast->fill_back != PIPE_POLYGON_MODE_FILL)) {
+      unfilled->face_slot = draw_alloc_extra_vertex_attrib(
+         stage->draw, TGSI_SEMANTIC_FACE, 0);
+   } else {
+      unfilled->face_slot = -1;
+   }
+}
+
 
 /**
  * Create unfilled triangle stage.
index e01f16e715ce9035bed822439b6b8f5cf051b0c6..e1d18403d5d0105548b05c7c919a15605db0f37d 100644 (file)
@@ -67,6 +67,8 @@ static void calculate_vertex_layout(struct i915_context *i915)
    colors[0] = colors[1] = fog = needW = face = FALSE;
    memset(&vinfo, 0, sizeof(vinfo));
 
+   draw_prepare_shader_outputs(i915->draw);
+
    /* Determine which fragment program inputs are needed.  Setup HW vertex
     * layout below, in the HW-specific attribute order.
     */
index 9495e42feee008d3dd982dfcbc4be97760b1a391..fc948a727feafafc0d4dd15f447a8846057c1bbc 100644 (file)
@@ -122,6 +122,9 @@ struct llvmpipe_context {
    /** Which geometry shader output slot contains layer */
    int layer_slot;
 
+   /** A fake frontface output for unfilled primitives */
+   int face_slot;
+
    /**< minimum resolvable depth value, for polygon offset */   
    double mrd;
    
index 65f61edc36070eabc498f38ead0c369aaadeffc3..5fde01fa13b625dce2f4ec28538b551fb6c0ae8a 100644 (file)
@@ -1053,6 +1053,7 @@ lp_setup_update_state( struct lp_setup_context *setup,
       setup->psize = lp->psize_slot;
       setup->viewport_index_slot = lp->viewport_index_slot;
       setup->layer_slot = lp->layer_slot;
+      setup->face_slot = lp->face_slot;
 
       assert(lp->dirty == 0);
 
index 89c23bbb3ac9e1631a93778f34329b084b012f14..ea1d0d688d8569ee729f332c320aee41fc1fb481 100644 (file)
@@ -106,6 +106,7 @@ struct lp_setup_context
    float psize;
    unsigned viewport_index_slot;
    unsigned layer_slot;
+   unsigned face_slot;
 
    struct pipe_framebuffer_state fb;
    struct u_rect framebuffer;
index bfed59cba0aebe5638f0a53b6e952b65bf649b88..3b16163ba593a930796ed028dd5ef08346551515 100644 (file)
@@ -37,6 +37,7 @@
 #include "lp_state_fs.h"
 #include "lp_state_setup.h"
 #include "lp_context.h"
+#include "draw/draw_context.h"
 
 #define NUM_CHANNELS 4
 
@@ -45,6 +46,7 @@ struct lp_line_info {
    float dx;
    float dy;
    float oneoverarea;
+   boolean frontfacing;
 
    const float (*v1)[4];
    const float (*v2)[4];
@@ -214,7 +216,8 @@ static void setup_line_coefficients( struct lp_setup_context *setup,
       case LP_INTERP_FACING:
          for (i = 0; i < NUM_CHANNELS; i++)
             if (usage_mask & (1 << i))
-               constant_coef(setup, info, slot+1, 1.0, i);
+               constant_coef(setup, info, slot+1,
+                             info->frontfacing ? 1.0f : -1.0f, i);
          break;
 
       default:
@@ -613,15 +616,22 @@ try_setup_line( struct lp_setup_context *setup,
    plane[2].dcdx = y[2] - y[3];
    plane[3].dcdx = y[3] - y[0];
 
+   if (draw_will_inject_frontface(lp_context->draw) &&
+       setup->face_slot > 0) {
+      line->inputs.frontfacing = v1[setup->face_slot][0];
+   } else {
+      line->inputs.frontfacing = TRUE;
+   }
+   
 
    /* Setup parameter interpolants:
     */
    info.a0 = GET_A0(&line->inputs);
    info.dadx = GET_DADX(&line->inputs);
    info.dady = GET_DADY(&line->inputs);
+   info.frontfacing = line->inputs.frontfacing;
    setup_line_coefficients(setup, &info); 
 
-   line->inputs.frontfacing = TRUE;
    line->inputs.disable = FALSE;
    line->inputs.opaque = FALSE;
    line->inputs.layer = layer;
index 868a9a904b725cf3f06de24b2508b8b0736cea7a..cbcc8d4f2f3b407a11257949a735369bf1b5da01 100644 (file)
@@ -38,6 +38,7 @@
 #include "lp_state_setup.h"
 #include "lp_context.h"
 #include "tgsi/tgsi_scan.h"
+#include "draw/draw_context.h"
 
 #define NUM_CHANNELS 4
 
@@ -51,6 +52,8 @@ struct point_info {
    float (*a0)[4];
    float (*dadx)[4];
    float (*dady)[4];
+
+   boolean frontfacing;
 };   
 
 
@@ -276,7 +279,8 @@ setup_point_coefficients( struct lp_setup_context *setup,
       case LP_INTERP_FACING:
          for (i = 0; i < NUM_CHANNELS; i++)
             if (usage_mask & (1 << i))
-               constant_coef(setup, info, slot+1, 1.0, i);
+               constant_coef(setup, info, slot+1,
+                             info->frontfacing ? 1.0f : -1.0f, i);
          break;
 
       default:
@@ -384,6 +388,13 @@ try_setup_point( struct lp_setup_context *setup,
       lp_context->pipeline_statistics.c_primitives++;
    }
 
+   if (draw_will_inject_frontface(lp_context->draw) &&
+       setup->face_slot > 0) {
+      point->inputs.frontfacing = v0[setup->face_slot][0];
+   } else {
+      point->inputs.frontfacing = TRUE;
+   }
+
    info.v0 = v0;
    info.dx01 = 0;
    info.dx12 = fixed_width;
@@ -392,12 +403,12 @@ try_setup_point( struct lp_setup_context *setup,
    info.a0 = GET_A0(&point->inputs);
    info.dadx = GET_DADX(&point->inputs);
    info.dady = GET_DADY(&point->inputs);
+   info.frontfacing = point->inputs.frontfacing;
    
    /* Setup parameter interpolants:
     */
    setup_point_coefficients(setup, &info);
 
-   point->inputs.frontfacing = TRUE;
    point->inputs.disable = FALSE;
    point->inputs.opaque = FALSE;
    point->inputs.layer = layer;
index dc80358c7be6dd07f286b2a63f7a637166686c99..5a51b50821e5e7a3e31cd3496f95a5865d750978 100644 (file)
@@ -53,6 +53,8 @@ compute_vertex_info(struct llvmpipe_context *llvmpipe)
    int vs_index;
    uint i;
 
+   draw_prepare_shader_outputs(llvmpipe->draw);
+
    llvmpipe->color_slot[0] = -1;
    llvmpipe->color_slot[1] = -1;
    llvmpipe->bcolor_slot[0] = -1;
@@ -138,6 +140,13 @@ compute_vertex_info(struct llvmpipe_context *llvmpipe)
       llvmpipe->layer_slot = 0;
    }
 
+   /* Check for a fake front face for unfilled primitives*/
+   vs_index = draw_find_shader_output(llvmpipe->draw,
+                                      TGSI_SEMANTIC_FACE, 0);
+   if (vs_index >= 0) {
+      llvmpipe->face_slot = vinfo->num_attribs;
+      draw_emit_vertex_attr(vinfo, EMIT_4F, INTERP_CONSTANT, vs_index);
+   }
 
    draw_compute_vertex_size(vinfo);
    lp_setup_set_vertex_info(llvmpipe->setup, vinfo);
index 1013557bde9727c5295fe7a51d69fd4d397aa763..da86cd2aaa58676cd627c4ee4844b53ebbcd5b43 100644 (file)
@@ -1079,6 +1079,7 @@ void r300_update_derived_state(struct r300_context* r300)
 
         if (r300->draw) {
             memset(&r300->vertex_info, 0, sizeof(struct vertex_info));
+            draw_prepare_shader_outputs(r300->draw);
             r300_draw_emit_all_attribs(r300);
             draw_compute_vertex_size(&r300->vertex_info);
             r300_swtcl_vertex_psc(r300);
index 61153494c5b0b219de9fbc2163bf9a307a7ee218..9aa97d2d202956e364db77bf04deb4223846cd73 100644 (file)
@@ -65,6 +65,8 @@ softpipe_get_vertex_info(struct softpipe_context *softpipe)
 {
    struct vertex_info *vinfo = &softpipe->vertex_info;
 
+   draw_prepare_shader_outputs(softpipe->draw);
+
    if (vinfo->num_attribs == 0) {
       /* compute vertex layout now */
       const struct tgsi_shader_info *fsInfo = &softpipe->fs_variant->info;
index d744f18ee72fc3845a423ea49710448314a54047..d60af3fb8343c439497eaef073dc56c126baa9bf 100644 (file)
@@ -162,6 +162,7 @@ svga_swtnl_update_vdecl( struct svga_context *svga )
    memset(vinfo, 0, sizeof(*vinfo));
    memset(vdecl, 0, sizeof(vdecl));
 
+   draw_prepare_shader_outputs(draw);
    /* always add position */
    src = draw_find_shader_output(draw, TGSI_SEMANTIC_POSITION, 0);
    draw_emit_vertex_attr(vinfo, EMIT_4F, INTERP_LINEAR, src);