i965/fs: Swap if/else conditions in SEL peephole.
[mesa.git] / src / gallium / auxiliary / draw / draw_pipe_clip.c
index 2d36eb3ddca079274665f6d4ed3c11897d344a62..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>
  */
 
 
 #include "draw_vs.h"
 #include "draw_pipe.h"
 #include "draw_fs.h"
+#include "draw_gs.h"
+
+
+/** Set to 1 to enable printing of coords before/after clipping */
+#define DEBUG_CLIP 0
 
 
 #ifndef IS_NEGATIVE
@@ -74,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)))
 
@@ -84,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] );
@@ -107,17 +127,16 @@ 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;
@@ -141,8 +160,10 @@ static void interp( const struct clip_stage *clip,
     */
    {
       const float *pos = dst->pre_clip_pos;
-      const float *scale = clip->stage.draw->viewport.scale;
-      const float *trans = clip->stage.draw->viewport.translate;
+      const float *scale =
+         clip->stage.draw->viewports[viewport_index].scale;
+      const float *trans =
+         clip->stage.draw->viewports[viewport_index].translate;
       const float oow = 1.0f / pos[3];
 
       dst->data[pos_attr][0] = pos[0] * oow * scale[0] + trans[0];
@@ -163,12 +184,17 @@ static void interp( const struct clip_stage *clip,
    {
       int k;
       t_nopersp = t;
-      for (k = 0; k < 2; k++)
-         if (in->data[pos_attr][k] != out->data[pos_attr][k]) {
-            t_nopersp = (dst->data[pos_attr][k] - out->data[pos_attr][k]) /
-               (in->data[pos_attr][k] - out->data[pos_attr][k]);
+      /* find either in.x != out.x or in.y != out.y */
+      for (k = 0; k < 2; k++) {
+         if (in->clip[k] != out->clip[k]) {
+            /* do divide by W, then compute linear interpolation factor */
+            float in_coord = in->clip[k] / in->clip[3];
+            float out_coord = out->clip[k] / out->clip[3];
+            float dst_coord = dst->clip[k] / dst->clip[3];
+            t_nopersp = (dst_coord - out_coord) / (in_coord - out_coord);
             break;
          }
+      }
    }
 
    /* Other attributes
@@ -183,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
@@ -197,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;
@@ -218,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 */
@@ -230,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;
       }
@@ -237,13 +302,17 @@ static void emit_poly( struct draw_stage *stage,
       if (i == n - 1 && edgeflags[i])
          header.flags |= edge_last;
 
-      if (0) {
-         const struct draw_vertex_shader *vs = stage->draw->vs.vertex_shader;
+      if (DEBUG_CLIP) {
          uint j, k;
          debug_printf("Clipped tri: (flat-shade-first = %d)\n",
                       stage->draw->rasterizer->flatshade_first);
          for (j = 0; j < 3; j++) {
-            for (k = 0; k < vs->info.num_outputs; k++) {
+            debug_printf("  Vert %d: clip: %f %f %f %f\n", j,
+                         header.v[j]->clip[0],
+                         header.v[j]->clip[1],
+                         header.v[j]->clip[2],
+                         header.v[j]->clip[3]);
+            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],
@@ -252,7 +321,6 @@ static void emit_poly( struct draw_stage *stage,
             }
          }
       }
-
       stage->next->tri( stage->next, &header );
    }
 }
@@ -310,11 +378,24 @@ 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;
+      const float *v2 = header->v[2]->clip;
+      debug_printf("Clip triangle:\n");
+      debug_printf(" %f, %f, %f, %f\n", v0[0], v0[1], v0[2], v0[3]);
+      debug_printf(" %f, %f, %f, %f\n", v1[0], v1[1], v1[2], v1[3]);
+      debug_printf(" %f, %f, %f, %f\n", v2[0], v2[1], v2[2], v2[3]);
+   }
+
    /*
     * Note: at this point we can't just use the per-vertex edge flags.
     * We have to observe the edge flag bits set in header->flags which
@@ -338,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;
@@ -350,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)
@@ -379,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
@@ -400,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:
                */
@@ -473,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);
@@ -496,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 {
@@ -505,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 {
@@ -518,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 );
@@ -546,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 );
@@ -564,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.
  */
@@ -571,9 +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_fragment_shader *fs = stage->draw->fs.fragment_shader;
-   uint i;
+   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
@@ -586,6 +760,9 @@ clip_init_state( struct draw_stage *stage )
     * two outputs for one input, so we tuck the information in a
     * specific array.  Second if they don't have qualifiers, the
     * default value has to be picked from the global shade mode.
+    *
+    * Of course, if we don't have a fragment shader in the first
+    * place, defaults should be used.
     */
 
    /* First pick up the interpolation mode for
@@ -595,10 +772,12 @@ clip_init_state( struct draw_stage *stage )
    indexed_interp[0] = indexed_interp[1] = stage->draw->rasterizer->flatshade ?
       TGSI_INTERPOLATE_CONSTANT : TGSI_INTERPOLATE_PERSPECTIVE;
 
-   for (i = 0; i < fs->info.num_inputs; i++) {
-      if (fs->info.input_semantic_name[i] == TGSI_SEMANTIC_COLOR) {
-         if (fs->info.input_interpolate[i] != TGSI_INTERPOLATE_COLOR)
-            indexed_interp[fs->info.input_semantic_index[i]] = fs->info.input_interpolate[i];
+   if (fs) {
+      for (i = 0; i < fs->info.num_inputs; i++) {
+         if (fs->info.input_semantic_name[i] == TGSI_SEMANTIC_COLOR) {
+            if (fs->info.input_interpolate[i] != TGSI_INTERPOLATE_COLOR)
+               indexed_interp[fs->info.input_semantic_index[i]] = fs->info.input_interpolate[i];
+         }
       }
    }
 
@@ -611,40 +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;
-         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;
@@ -701,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;