i965/fs: Swap if/else conditions in SEL peephole.
[mesa.git] / src / gallium / auxiliary / draw / draw_pipe_clip.c
index b01e51994cba9eaa22b7cd09f8cc522760a0e74a..d8b56de4d2ff576a545c672637a10f6f7e1e7cc4 100644 (file)
@@ -1,6 +1,6 @@
 /**************************************************************************
  * 
- * Copyright 2007 Tungsten Graphics, Inc., Cedar Park, Texas.
+ * Copyright 2007 VMware, Inc.
  * All Rights Reserved.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
@@ -18,7 +18,7 @@
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
- * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
+ * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -28,7 +28,7 @@
 /**
  * \brief  Clipping stage
  *
- * \author  Keith Whitwell <keith@tungstengraphics.com>
+ * \author  Keith Whitwell <keithw@vmware.com>
  */
 
 
@@ -79,6 +79,21 @@ static INLINE struct clip_stage *clip_stage( struct draw_stage *stage )
    return (struct clip_stage *)stage;
 }
 
+static INLINE unsigned
+draw_viewport_index(struct draw_context *draw,
+                    const struct vertex_header *leading_vertex)
+{
+   if (draw_current_shader_uses_viewport_index(draw)) {
+      unsigned viewport_index_output =
+         draw_current_shader_viewport_index_output(draw);
+      unsigned viewport_index =
+         *((unsigned*)leading_vertex->data[viewport_index_output]);
+      return draw_clamp_viewport_idx(viewport_index);
+   } else {
+      return 0;
+   }
+}
+
 
 #define LINTERP(T, OUT, IN) ((OUT) + (T) * ((IN) - (OUT)))
 
@@ -89,7 +104,7 @@ static void interp_attr( float dst[4],
                         float t,
                         const float in[4],
                         const float out[4] )
-{  
+{
    dst[0] = LINTERP( t, out[0], in[0] );
    dst[1] = LINTERP( t, out[1], in[1] );
    dst[2] = LINTERP( t, out[2], in[2] );
@@ -112,23 +127,20 @@ static void copy_flat( struct draw_stage *stage,
    }
 }
 
-
-
 /* Interpolate between two vertices to produce a third.  
  */
 static void interp( const struct clip_stage *clip,
                    struct vertex_header *dst,
                    float t,
                    const struct vertex_header *out, 
-                   const struct vertex_header *in )
+                   const struct vertex_header *in,
+                    unsigned viewport_index )
 {
-   const unsigned nr_attrs = draw_current_shader_outputs(clip->stage.draw);
+   const unsigned nr_attrs = draw_num_shader_outputs(clip->stage.draw);
    const unsigned pos_attr = draw_current_shader_position_output(clip->stage.draw);
    const unsigned clip_attr = draw_current_shader_clipvertex_output(clip->stage.draw);
    unsigned j;
    float t_nopersp;
-   unsigned viewport_index_output =
-      draw_current_shader_viewport_index_output(clip->stage.draw);
 
    /* Vertex header.
     */
@@ -147,9 +159,6 @@ static void interp( const struct clip_stage *clip,
     * new window coordinates:
     */
    {
-      int viewport_index = 
-         draw_current_shader_uses_viewport_index(clip->stage.draw) ?
-         *((unsigned*)in->data[viewport_index_output]) : 0;
       const float *pos = dst->pre_clip_pos;
       const float *scale =
          clip->stage.draw->viewports[viewport_index].scale;
@@ -200,6 +209,29 @@ static void interp( const struct clip_stage *clip,
    }
 }
 
+/**
+ * Checks whether the specifed triangle is empty and if it is returns
+ * true, otherwise returns false.
+ * Triangle is considered null/empty if it's area is qual to zero.
+ */
+static INLINE boolean
+is_tri_null(struct draw_context *draw, const struct prim_header *header)
+{
+   const unsigned pos_attr = draw_current_shader_position_output(draw);
+   float x1 = header->v[1]->data[pos_attr][0] - header->v[0]->data[pos_attr][0];
+   float y1 = header->v[1]->data[pos_attr][1] - header->v[0]->data[pos_attr][1];
+   float z1 = header->v[1]->data[pos_attr][2] - header->v[0]->data[pos_attr][2];
+
+   float x2 = header->v[2]->data[pos_attr][0] - header->v[0]->data[pos_attr][0];
+   float y2 = header->v[2]->data[pos_attr][1] - header->v[0]->data[pos_attr][1];
+   float z2 = header->v[2]->data[pos_attr][2] - header->v[0]->data[pos_attr][2];
+
+   float vx = y1 * z2 - z1 * y2;
+   float vy = x1 * z2 - z1 * x2;
+   float vz = x1 * y2 - y1 * x2;
+
+   return (vx*vx  + vy*vy + vz*vz) == 0.f;
+}
 
 /**
  * Emit a post-clip polygon to the next pipeline stage.  The polygon
@@ -214,6 +246,8 @@ static void emit_poly( struct draw_stage *stage,
    struct prim_header header;
    unsigned i;
    ushort edge_first, edge_middle, edge_last;
+   boolean last_tri_was_null = FALSE;
+   boolean tri_was_not_null = FALSE;
 
    if (stage->draw->rasterizer->flatshade_first) {
       edge_first  = DRAW_PIPE_EDGE_FLAG_0;
@@ -235,6 +269,7 @@ static void emit_poly( struct draw_stage *stage,
    header.pad = 0;
 
    for (i = 2; i < n; i++, header.flags = edge_middle) {
+      boolean tri_null;
       /* order the triangle verts to respect the provoking vertex mode */
       if (stage->draw->rasterizer->flatshade_first) {
          header.v[0] = inlist[0];  /* the provoking vertex */
@@ -247,6 +282,19 @@ static void emit_poly( struct draw_stage *stage,
          header.v[2] = inlist[0];  /* the provoking vertex */
       }
 
+      tri_null = is_tri_null(stage->draw, &header);
+      /* If we generated a triangle with an area, aka. non-null triangle,
+       * or if the previous triangle was also null then skip all subsequent
+       * null triangles */
+      if ((tri_was_not_null && tri_null) || (last_tri_was_null && tri_null)) {
+         last_tri_was_null = tri_null;
+         continue;
+      }
+      last_tri_was_null = tri_null;
+      if (!tri_null) {
+         tri_was_not_null = TRUE;
+      }
+
       if (!edgeflags[i-1]) {
          header.flags &= ~edge_middle;
       }
@@ -255,7 +303,6 @@ static void emit_poly( struct draw_stage *stage,
          header.flags |= edge_last;
 
       if (DEBUG_CLIP) {
-         const struct draw_vertex_shader *vs = stage->draw->vs.vertex_shader;
          uint j, k;
          debug_printf("Clipped tri: (flat-shade-first = %d)\n",
                       stage->draw->rasterizer->flatshade_first);
@@ -265,7 +312,7 @@ static void emit_poly( struct draw_stage *stage,
                          header.v[j]->clip[1],
                          header.v[j]->clip[2],
                          header.v[j]->clip[3]);
-            for (k = 0; k < vs->info.num_outputs; k++) {
+            for (k = 0; k < draw_num_shader_outputs(stage->draw); k++) {
                debug_printf("  Vert %d: Attr %d:  %f %f %f %f\n", j, k,
                             header.v[j]->data[k][0],
                             header.v[j]->data[k][1],
@@ -274,7 +321,6 @@ static void emit_poly( struct draw_stage *stage,
             }
          }
       }
-
       stage->next->tri( stage->next, &header );
    }
 }
@@ -332,11 +378,14 @@ do_clip_tri( struct draw_stage *stage,
    boolean bEdges[MAX_CLIPPED_VERTICES];
    boolean *inEdges = aEdges;
    boolean *outEdges = bEdges;
+   int viewport_index = 0;
 
    inlist[0] = header->v[0];
    inlist[1] = header->v[1];
    inlist[2] = header->v[2];
 
+   viewport_index = draw_viewport_index(clipper->stage.draw, inlist[0]);
+
    if (DEBUG_CLIP) {
       const float *v0 = header->v[0]->clip;
       const float *v1 = header->v[1]->clip;
@@ -370,6 +419,9 @@ do_clip_tri( struct draw_stage *stage,
       dp_prev = getclipdist(clipper, vert_prev, plane_idx);
       clipmask &= ~(1<<plane_idx);
 
+      if (util_is_inf_or_nan(dp_prev))
+         return; //discard nan
+
       assert(n < MAX_CLIPPED_VERTICES);
       if (n >= MAX_CLIPPED_VERTICES)
          return;
@@ -382,6 +434,9 @@ do_clip_tri( struct draw_stage *stage,
 
          float dp = getclipdist(clipper, vert, plane_idx);
 
+         if (util_is_inf_or_nan(dp))
+            return; //discard nan
+
         if (!IS_NEGATIVE(dp_prev)) {
             assert(outcount < MAX_CLIPPED_VERTICES);
             if (outcount >= MAX_CLIPPED_VERTICES)
@@ -411,7 +466,7 @@ do_clip_tri( struct draw_stage *stage,
                * know dp != dp_prev from DIFFERENT_SIGNS, above.
                */
               float t = dp / (dp - dp_prev);
-              interp( clipper, new_vert, t, vert, vert_prev );
+              interp( clipper, new_vert, t, vert, vert_prev, viewport_index );
               
               /* Whether or not to set edge flag for the new vert depends
                 * on whether it's a user-defined clipping plane.  We're
@@ -432,7 +487,7 @@ do_clip_tri( struct draw_stage *stage,
               /* Coming back in.
                */
               float t = dp_prev / (dp_prev - dp);
-              interp( clipper, new_vert, t, vert_prev, vert );
+              interp( clipper, new_vert, t, vert_prev, vert, viewport_index );
 
               /* Copy starting vert's edgeflag:
                */
@@ -505,12 +560,16 @@ do_clip_line( struct draw_stage *stage,
    float t0 = 0.0F;
    float t1 = 0.0F;
    struct prim_header newprim;
+   int viewport_index = draw_viewport_index(clipper->stage.draw, v0);
 
    while (clipmask) {
       const unsigned plane_idx = ffs(clipmask)-1;
       const float dp0 = getclipdist(clipper, v0, plane_idx);
       const float dp1 = getclipdist(clipper, v1, plane_idx);
 
+      if (util_is_inf_or_nan(dp0) || util_is_inf_or_nan(dp1))
+         return; //discard nan
+
       if (dp1 < 0.0F) {
         float t = dp1 / (dp1 - dp0);
          t1 = MAX2(t1, t);
@@ -528,8 +587,13 @@ do_clip_line( struct draw_stage *stage,
    }
 
    if (v0->clipmask) {
-      interp( clipper, stage->tmp[0], t0, v0, v1 );
-      copy_flat(stage, stage->tmp[0], v0);
+      interp( clipper, stage->tmp[0], t0, v0, v1, viewport_index );
+      if (stage->draw->rasterizer->flatshade_first) {
+         copy_flat(stage, stage->tmp[0], v0);  /* copy v0 color to tmp[0] */
+      }
+      else {
+         copy_flat(stage, stage->tmp[0], v1);  /* copy v1 color to tmp[0] */
+      }
       newprim.v[0] = stage->tmp[0];
    }
    else {
@@ -537,7 +601,13 @@ do_clip_line( struct draw_stage *stage,
    }
 
    if (v1->clipmask) {
-      interp( clipper, stage->tmp[1], t1, v1, v0 );
+      interp( clipper, stage->tmp[1], t1, v1, v0, viewport_index );
+      if (stage->draw->rasterizer->flatshade_first) {
+         copy_flat(stage, stage->tmp[1], v0);  /* copy v0 color to tmp[1] */
+      }
+      else {
+         copy_flat(stage, stage->tmp[1], v1);  /* copy v1 color to tmp[1] */
+      }
       newprim.v[1] = stage->tmp[1];
    }
    else {
@@ -550,20 +620,63 @@ do_clip_line( struct draw_stage *stage,
 
 static void
 clip_point( struct draw_stage *stage, 
-           struct prim_header *header )
+            struct prim_header *header )
 {
-   if (header->v[0]->clipmask == 0) 
+   if (header->v[0]->clipmask == 0)
       stage->next->point( stage->next, header );
 }
 
 
+/*
+ * Clip points but ignore the first 4 (xy) clip planes.
+ * (Because the generated clip mask is completely unaffacted by guard band,
+ * we still need to manually evaluate the x/y planes if they are outside
+ * the guard band and not just outside the vp.)
+ */
+static void
+clip_point_guard_xy( struct draw_stage *stage,
+                     struct prim_header *header )
+{
+   unsigned clipmask = header->v[0]->clipmask;
+   if ((clipmask & 0xffffffff) == 0)
+      stage->next->point(stage->next, header);
+   else if ((clipmask & 0xfffffff0) == 0) {
+      while (clipmask) {
+         const unsigned plane_idx = ffs(clipmask)-1;
+         clipmask &= ~(1 << plane_idx);  /* turn off this plane's bit */
+         /* TODO: this should really do proper guardband clipping,
+          * currently just throw out infs/nans.
+          * Also note that vertices with negative w values MUST be tossed
+          * out (not sure if proper guardband clipping would do this
+          * automatically). These would usually be captured by depth clip
+          * too but this can be disabled.
+          */
+         if (header->v[0]->clip[3] <= 0.0f ||
+             util_is_inf_or_nan(header->v[0]->clip[0]) ||
+             util_is_inf_or_nan(header->v[0]->clip[1]))
+            return;
+      }
+      stage->next->point(stage->next, header);
+   }
+}
+
+
+static void
+clip_first_point( struct draw_stage *stage,
+                  struct prim_header *header )
+{
+   stage->point = stage->draw->guard_band_points_xy ? clip_point_guard_xy : clip_point;
+   stage->point(stage, header);
+}
+
+
 static void
 clip_line( struct draw_stage *stage,
           struct prim_header *header )
 {
    unsigned clipmask = (header->v[0]->clipmask | 
                         header->v[1]->clipmask);
-   
+
    if (clipmask == 0) {
       /* no clipping needed */
       stage->next->line( stage->next, header );
@@ -578,12 +691,12 @@ clip_line( struct draw_stage *stage,
 
 static void
 clip_tri( struct draw_stage *stage,
-         struct prim_header *header )
+          struct prim_header *header )
 {
    unsigned clipmask = (header->v[0]->clipmask | 
                         header->v[1]->clipmask | 
                         header->v[2]->clipmask);
-   
+
    if (clipmask == 0) {
       /* no clipping needed */
       stage->next->tri( stage->next, header );
@@ -596,6 +709,35 @@ clip_tri( struct draw_stage *stage,
 }
 
 
+static int
+find_interp(const struct draw_fragment_shader *fs, int *indexed_interp,
+            uint semantic_name, uint semantic_index)
+{
+   int interp;
+   /* If it's gl_{Front,Back}{,Secondary}Color, pick up the mode
+    * from the array we've filled before. */
+   if (semantic_name == TGSI_SEMANTIC_COLOR ||
+       semantic_name == TGSI_SEMANTIC_BCOLOR) {
+      interp = indexed_interp[semantic_index];
+   } else {
+      /* Otherwise, search in the FS inputs, with a decent default
+       * if we don't find it.
+       */
+      uint j;
+      interp = TGSI_INTERPOLATE_PERSPECTIVE;
+      if (fs) {
+         for (j = 0; j < fs->info.num_inputs; j++) {
+            if (semantic_name == fs->info.input_semantic_name[j] &&
+                semantic_index == fs->info.input_semantic_index[j]) {
+               interp = fs->info.input_interpolate[j];
+               break;
+            }
+         }
+      }
+   }
+   return interp;
+}
+
 /* Update state.  Could further delay this until we hit the first
  * primitive that really requires clipping.
  */
@@ -603,11 +745,9 @@ static void
 clip_init_state( struct draw_stage *stage )
 {
    struct clip_stage *clipper = clip_stage( stage );
-   const struct draw_vertex_shader *vs = stage->draw->vs.vertex_shader;
-   const struct draw_geometry_shader *gs = stage->draw->gs.geometry_shader;
    const struct draw_fragment_shader *fs = stage->draw->fs.fragment_shader;
-   uint i;
-   const struct tgsi_shader_info *vs_info = gs ? &gs->info : &vs->info;
+   uint i, j;
+   const struct tgsi_shader_info *info = draw_get_shader_info(stage->draw);
 
    /* We need to know for each attribute what kind of interpolation is
     * done on it (flat, smooth or noperspective).  But the information
@@ -650,42 +790,36 @@ clip_init_state( struct draw_stage *stage )
 
    clipper->num_flat_attribs = 0;
    memset(clipper->noperspective_attribs, 0, sizeof(clipper->noperspective_attribs));
-   for (i = 0; i < vs_info->num_outputs; i++) {
-      /* Find the interpolation mode for a specific attribute
-       */
-      int interp;
-
-      /* If it's gl_{Front,Back}{,Secondary}Color, pick up the mode
-       * from the array we've filled before. */
-      if (vs_info->output_semantic_name[i] == TGSI_SEMANTIC_COLOR ||
-          vs_info->output_semantic_name[i] == TGSI_SEMANTIC_BCOLOR) {
-         interp = indexed_interp[vs_info->output_semantic_index[i]];
-      } else {
-         /* Otherwise, search in the FS inputs, with a decent default
-          * if we don't find it.
-          */
-         uint j;
-         interp = TGSI_INTERPOLATE_PERSPECTIVE;
-         if (fs) {
-            for (j = 0; j < fs->info.num_inputs; j++) {
-               if (vs_info->output_semantic_name[i] == fs->info.input_semantic_name[j] &&
-                   vs_info->output_semantic_index[i] == fs->info.input_semantic_index[j]) {
-                  interp = fs->info.input_interpolate[j];
-                  break;
-               }
-            }
-         }
-      }
-
+   for (i = 0; i < info->num_outputs; i++) {
+      /* Find the interpolation mode for a specific attribute */
+      int interp = find_interp(fs, indexed_interp,
+                               info->output_semantic_name[i],
+                               info->output_semantic_index[i]);
       /* If it's flat, add it to the flat vector.  Otherwise update
        * the noperspective mask.
        */
+
       if (interp == TGSI_INTERPOLATE_CONSTANT) {
          clipper->flat_attribs[clipper->num_flat_attribs] = i;
          clipper->num_flat_attribs++;
       } else
          clipper->noperspective_attribs[i] = interp == TGSI_INTERPOLATE_LINEAR;
    }
+   /* Search the extra vertex attributes */
+   for (j = 0; j < stage->draw->extra_shader_outputs.num; j++) {
+      /* Find the interpolation mode for a specific attribute */
+      int interp = find_interp(fs, indexed_interp,
+                               stage->draw->extra_shader_outputs.semantic_name[j],
+                               stage->draw->extra_shader_outputs.semantic_index[j]);
+      /* If it's flat, add it to the flat vector.  Otherwise update
+       * the noperspective mask.
+       */
+      if (interp == TGSI_INTERPOLATE_CONSTANT) {
+         clipper->flat_attribs[clipper->num_flat_attribs] = i + j;
+         clipper->num_flat_attribs++;
+      } else
+         clipper->noperspective_attribs[i + j] = interp == TGSI_INTERPOLATE_LINEAR;
+   }
    
    stage->tri = clip_tri;
    stage->line = clip_line;
@@ -742,7 +876,7 @@ struct draw_stage *draw_clip_stage( struct draw_context *draw )
 
    clipper->stage.draw = draw;
    clipper->stage.name = "clipper";
-   clipper->stage.point = clip_point;
+   clipper->stage.point = clip_first_point;
    clipper->stage.line = clip_first_line;
    clipper->stage.tri = clip_first_tri;
    clipper->stage.flush = clip_flush;