draw: fix clipping of layer/vp index outputs
authorRoland Scheidegger <sroland@vmware.com>
Fri, 4 Dec 2015 02:42:02 +0000 (03:42 +0100)
committerRoland Scheidegger <sroland@vmware.com>
Fri, 4 Dec 2015 02:42:19 +0000 (03:42 +0100)
This was just plain broken. It used always the value from v0 (for vp_index)
but would pass the value from the provoking vertex to later stages - but only
if there was a corresponding fs input, otherwise the layer/vp index would get
lost completely (as it would try to interpolate the (unsigned) values as
floats).
So, make it obey provoking vertex rules (drivers relying on draw will need to
do the same). And make sure that the default interpolation mode (when no
corresponding fs input is found) for them is constant.
Also, change the code a bit so constant inputs aren't interpolated then
copied over later.

Fixes the new piglit test gl-layer-render-clipped.

v2: more consistent whitespaces fixes for function defs, and more tab killing
(overall still not quite right however).

Reviewed-by: Brian Paul <brianp@vmware.com>
Reviewed-by: Jose Fonseca <jfonseca@vmware.com>
src/gallium/auxiliary/draw/draw_pipe_clip.c

index c22758bc702e218c53cef3236c8e89236a96f646..f2b56b017dded5b04b11382d5112ca67e9f99dd6 100644 (file)
 struct clip_stage {
    struct draw_stage stage;      /**< base class */
 
-   /* List of the attributes to be flatshaded. */
-   uint num_flat_attribs;
-   uint flat_attribs[PIPE_MAX_SHADER_OUTPUTS];
-
-   /* Mask of attributes in noperspective mode */
-   boolean noperspective_attribs[PIPE_MAX_SHADER_OUTPUTS];
+   unsigned pos_attr;
+
+   /* List of the attributes to be constant interpolated. */
+   uint num_const_attribs;
+   uint8_t const_attribs[PIPE_MAX_SHADER_OUTPUTS];
+   /* List of the attributes to be linear interpolated. */
+   uint num_linear_attribs;
+   uint8_t linear_attribs[PIPE_MAX_SHADER_OUTPUTS];
+   /* List of the attributes to be perspective interpolated. */
+   uint num_perspect_attribs;
+   uint8_t perspect_attribs[PIPE_MAX_SHADER_OUTPUTS];
 
    float (*plane)[4];
 };
 
 
 /** Cast wrapper */
-static inline struct clip_stage *clip_stage( struct draw_stage *stage )
+static inline struct clip_stage *clip_stage(struct draw_stage *stage)
 {
    return (struct clip_stage *)stage;
 }
@@ -96,10 +101,10 @@ draw_viewport_index(struct draw_context *draw,
 
 /* All attributes are float[4], so this is easy:
  */
-static void interp_attr( float dst[4],
-                        float t,
-                        const float in[4],
-                        const float out[4] )
+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] );
@@ -111,30 +116,28 @@ static void interp_attr( float dst[4],
 /**
  * Copy flat shaded attributes src vertex to dst vertex.
  */
-static void copy_flat( struct draw_stage *stage,
-                       struct vertex_header *dst,
-                       const struct vertex_header *src )
+static void copy_flat(struct draw_stage *stage,
+                      struct vertex_header *dst,
+                      const struct vertex_header *src)
 {
    const struct clip_stage *clipper = clip_stage(stage);
    uint i;
-   for (i = 0; i < clipper->num_flat_attribs; i++) {
-      const uint attr = clipper->flat_attribs[i];
+   for (i = 0; i < clipper->num_const_attribs; i++) {
+      const uint attr = clipper->const_attribs[i];
       COPY_4FV(dst->data[attr], src->data[attr]);
    }
 }
 
 /* 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,
-                    unsigned viewport_index )
+static void interp(const struct clip_stage *clip,
+                   struct vertex_header *dst,
+                   float t,
+                   const struct vertex_header *out,
+                   const struct vertex_header *in,
+                   unsigned viewport_index)
 {
-   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);
+   const unsigned pos_attr = clip->pos_attr;
    unsigned j;
    float t_nopersp;
 
@@ -168,6 +171,13 @@ static void interp( const struct clip_stage *clip,
       dst->data[pos_attr][3] = oow;
    }
    
+
+   /* interp perspective attribs */
+   for (j = 0; j < clip->num_perspect_attribs; j++) {
+      const unsigned attr = clip->perspect_attribs[j];
+      interp_attr(dst->data[attr], t, in->data[attr], out->data[attr]);
+   }
+
    /**
     * Compute the t in screen-space instead of 3d space to use
     * for noperspective interpolation.
@@ -177,7 +187,7 @@ static void interp( const struct clip_stage *clip,
     * pick whatever value (the interpolated point won't be in front
     * anyway), so just use the 3d t.
     */
-   {
+   if (clip->num_linear_attribs) {
       int k;
       t_nopersp = t;
       /* find either in.x != out.x or in.y != out.y */
@@ -191,24 +201,17 @@ static void interp( const struct clip_stage *clip,
             break;
          }
       }
-   }
-
-   /* Other attributes
-    */
-   for (j = 0; j < nr_attrs; j++) {
-      if (j != pos_attr && j != clip_attr) {
-         if (clip->noperspective_attribs[j])
-            interp_attr(dst->data[j], t_nopersp, in->data[j], out->data[j]);
-         else
-            interp_attr(dst->data[j], t, in->data[j], out->data[j]);
+      for (j = 0; j < clip->num_linear_attribs; j++) {
+         const unsigned attr = clip->linear_attribs[j];
+         interp_attr(dst->data[attr], t_nopersp, in->data[attr], out->data[attr]);
       }
    }
 }
 
 /**
- * Checks whether the specifed triangle is empty and if it is returns
+ * Checks whether the specified 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.
+ * Triangle is considered null/empty if its area is equal to zero.
  */
 static inline boolean
 is_tri_null(struct draw_context *draw, const struct prim_header *header)
@@ -233,11 +236,11 @@ is_tri_null(struct draw_context *draw, const struct prim_header *header)
  * Emit a post-clip polygon to the next pipeline stage.  The polygon
  * will be convex and the provoking vertex will always be vertex[0].
  */
-static void emit_poly( struct draw_stage *stage,
-                      struct vertex_header **inlist,
-                       const boolean *edgeflags,
-                      unsigned n,
-                      const struct prim_header *origPrim)
+static void emit_poly(struct draw_stage *stage,
+                      struct vertex_header **inlist,
+                      const boolean *edgeflags,
+                      unsigned n,
+                      const struct prim_header *origPrim)
 {
    struct prim_header header;
    unsigned i;
@@ -358,15 +361,16 @@ static inline float getclipdist(const struct clip_stage *clipper,
 /* Clip a triangle against the viewport and user clip planes.
  */
 static void
-do_clip_tri( struct draw_stage *stage, 
-            struct prim_header *header,
-            unsigned clipmask )
+do_clip_tri(struct draw_stage *stage,
+            struct prim_header *header,
+            unsigned clipmask)
 {
    struct clip_stage *clipper = clip_stage( stage );
    struct vertex_header *a[MAX_CLIPPED_VERTICES];
    struct vertex_header *b[MAX_CLIPPED_VERTICES];
    struct vertex_header **inlist = a;
    struct vertex_header **outlist = b;
+   struct vertex_header *prov_vertex;
    unsigned tmpnr = 0;
    unsigned n = 3;
    unsigned i;
@@ -380,7 +384,20 @@ do_clip_tri( struct draw_stage *stage,
    inlist[1] = header->v[1];
    inlist[2] = header->v[2];
 
-   viewport_index = draw_viewport_index(clipper->stage.draw, inlist[0]);
+   /*
+    * For d3d10, we need to take this from the leading (first) vertex.
+    * For GL, we could do anything (as long as we advertize
+    * GL_UNDEFINED_VERTEX for the VIEWPORT_INDEX_PROVOKING_VERTEX query),
+    * but it needs to be consistent with what other parts (i.e. driver)
+    * will do, and that seems easier with GL_PROVOKING_VERTEX logic.
+    */
+   if (stage->draw->rasterizer->flatshade_first) {
+      prov_vertex = inlist[0];
+   }
+   else {
+      prov_vertex = inlist[2];
+   }
+   viewport_index = draw_viewport_index(clipper->stage.draw, prov_vertex);
 
    if (DEBUG_CLIP) {
       const float *v0 = header->v[0]->clip;
@@ -425,7 +442,7 @@ do_clip_tri( struct draw_stage *stage,
       inEdges[n] = inEdges[0];
 
       for (i = 1; i <= n; i++) {
-        struct vertex_header *vert = inlist[i];
+         struct vertex_header *vert = inlist[i];
          boolean *edge = &inEdges[i];
 
          float dp = getclipdist(clipper, vert, plane_idx);
@@ -433,16 +450,16 @@ do_clip_tri( struct draw_stage *stage,
          if (util_is_inf_or_nan(dp))
             return; //discard nan
 
-        if (dp_prev >= 0.0f) {
+         if (dp_prev >= 0.0f) {
             assert(outcount < MAX_CLIPPED_VERTICES);
             if (outcount >= MAX_CLIPPED_VERTICES)
                return;
             outEdges[outcount] = *edge_prev;
-           outlist[outcount++] = vert_prev;
-        }
+            outlist[outcount++] = vert_prev;
+         }
 
-        if (DIFFERENT_SIGNS(dp, dp_prev)) {
-           struct vertex_header *new_vert;
+         if (DIFFERENT_SIGNS(dp, dp_prev)) {
+            struct vertex_header *new_vert;
             boolean *new_edge;
 
             assert(tmpnr < MAX_CLIPPED_VERTICES + 1);
@@ -455,19 +472,19 @@ do_clip_tri( struct draw_stage *stage,
                return;
 
             new_edge = &outEdges[outcount];
-           outlist[outcount++] = new_vert;
-
-           if (dp < 0.0f) {
-              /* Going out of bounds.  Avoid division by zero as we
-               * know dp != dp_prev from DIFFERENT_SIGNS, above.
-               */
-              float t = dp / (dp - dp_prev);
-              interp( clipper, new_vert, t, vert, vert_prev, viewport_index );
-              
-              /* Whether or not to set edge flag for the new vert depends
+            outlist[outcount++] = new_vert;
+
+            if (dp < 0.0f) {
+               /* Going out of bounds.  Avoid division by zero as we
+                * know dp != dp_prev from DIFFERENT_SIGNS, above.
+                */
+               float t = dp / (dp - dp_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
                 * copying NVIDIA's behaviour here.
-               */
+                */
                if (is_user_clip_plane) {
                   /* we want to see an edge along the clip plane */
                   *new_edge = TRUE;
@@ -478,31 +495,31 @@ do_clip_tri( struct draw_stage *stage,
                   *new_edge = *edge_prev;
                   new_vert->edgeflag = FALSE;
                }
-           }
+            }
             else {
-              /* Coming back in.
-               */
-              float t = dp_prev / (dp_prev - dp);
-              interp( clipper, new_vert, t, vert_prev, vert, viewport_index );
-
-              /* Copy starting vert's edgeflag:
-               */
-              new_vert->edgeflag = vert_prev->edgeflag;
+               /* Coming back in.
+                */
+               float t = dp_prev / (dp_prev - dp);
+               interp( clipper, new_vert, t, vert_prev, vert, viewport_index );
+
+               /* Copy starting vert's edgeflag:
+                */
+               new_vert->edgeflag = vert_prev->edgeflag;
                *new_edge = *edge_prev;
-           }
-        }
+            }
+         }
 
-        vert_prev = vert;
+         vert_prev = vert;
          edge_prev = edge;
-        dp_prev = dp;
+         dp_prev = dp;
       }
 
       /* swap in/out lists */
       {
-        struct vertex_header **tmp = inlist;
-        inlist = outlist;
-        outlist = tmp;
-        n = outcount;
+         struct vertex_header **tmp = inlist;
+         inlist = outlist;
+         outlist = tmp;
+         n = outcount;
       }
       {
          boolean *tmp = inEdges;
@@ -512,10 +529,10 @@ do_clip_tri( struct draw_stage *stage,
 
    }
 
-   /* If flat-shading, copy provoking vertex color to polygon vertex[0]
+   /* If constant interpolated, copy provoking vertex attrib to polygon vertex[0]
     */
    if (n >= 3) {
-      if (clipper->num_flat_attribs) {
+      if (clipper->num_const_attribs) {
          if (stage->draw->rasterizer->flatshade_first) {
             if (inlist[0] != header->v[0]) {
                assert(tmpnr < MAX_CLIPPED_VERTICES + 1);
@@ -535,7 +552,7 @@ do_clip_tri( struct draw_stage *stage,
             }
          }
       }
-      
+
       /* Emit the polygon as triangles to the setup stage:
        */
       emit_poly( stage, inlist, inEdges, n, header );
@@ -546,17 +563,26 @@ do_clip_tri( struct draw_stage *stage,
 /* Clip a line against the viewport and user clip planes.
  */
 static void
-do_clip_line( struct draw_stage *stage,
-             struct prim_header *header,
-             unsigned clipmask )
+do_clip_line(struct draw_stage *stage,
+             struct prim_header *header,
+             unsigned clipmask)
 {
    const struct clip_stage *clipper = clip_stage( stage );
    struct vertex_header *v0 = header->v[0];
    struct vertex_header *v1 = header->v[1];
+   struct vertex_header *prov_vertex;
    float t0 = 0.0F;
    float t1 = 0.0F;
    struct prim_header newprim;
-   int viewport_index = draw_viewport_index(clipper->stage.draw, v0);
+   int viewport_index;
+
+   if (stage->draw->rasterizer->flatshade_first) {
+      prov_vertex = v0;
+   }
+   else {
+      prov_vertex = v1;
+   }
+   viewport_index = draw_viewport_index(clipper->stage.draw, prov_vertex);
 
    while (clipmask) {
       const unsigned plane_idx = ffs(clipmask)-1;
@@ -567,17 +593,17 @@ do_clip_line( struct draw_stage *stage,
          return; //discard nan
 
       if (dp1 < 0.0F) {
-        float t = dp1 / (dp1 - dp0);
+         float t = dp1 / (dp1 - dp0);
          t1 = MAX2(t1, t);
       } 
 
       if (dp0 < 0.0F) {
-        float t = dp0 / (dp0 - dp1);
+         float t = dp0 / (dp0 - dp1);
          t0 = MAX2(t0, t);
       }
 
       if (t0 + t1 >= 1.0F)
-        return; /* discard */
+         return; /* discard */
 
       clipmask &= ~(1 << plane_idx);  /* turn off this plane's bit */
    }
@@ -615,8 +641,7 @@ do_clip_line( struct draw_stage *stage,
 
 
 static void
-clip_point( struct draw_stage *stage, 
-            struct prim_header *header )
+clip_point(struct draw_stage *stage, struct prim_header *header)
 {
    if (header->v[0]->clipmask == 0)
       stage->next->point( stage->next, header );
@@ -630,8 +655,7 @@ clip_point( struct draw_stage *stage,
  * the guard band and not just outside the vp.)
  */
 static void
-clip_point_guard_xy( struct draw_stage *stage,
-                     struct prim_header *header )
+clip_point_guard_xy(struct draw_stage *stage, struct prim_header *header)
 {
    unsigned clipmask = header->v[0]->clipmask;
    if ((clipmask & 0xffffffff) == 0)
@@ -658,8 +682,7 @@ clip_point_guard_xy( struct draw_stage *stage,
 
 
 static void
-clip_first_point( struct draw_stage *stage,
-                  struct prim_header *header )
+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);
@@ -667,8 +690,7 @@ clip_first_point( struct draw_stage *stage,
 
 
 static void
-clip_line( struct draw_stage *stage,
-          struct prim_header *header )
+clip_line(struct draw_stage *stage, struct prim_header *header)
 {
    unsigned clipmask = (header->v[0]->clipmask | 
                         header->v[1]->clipmask);
@@ -686,8 +708,7 @@ clip_line( struct draw_stage *stage,
 
 
 static void
-clip_tri( struct draw_stage *stage,
-          struct prim_header *header )
+clip_tri(struct draw_stage *stage, struct prim_header *header)
 {
    unsigned clipmask = (header->v[0]->clipmask | 
                         header->v[1]->clipmask | 
@@ -715,12 +736,24 @@ find_interp(const struct draw_fragment_shader *fs, int *indexed_interp,
    if (semantic_name == TGSI_SEMANTIC_COLOR ||
        semantic_name == TGSI_SEMANTIC_BCOLOR) {
       interp = indexed_interp[semantic_index];
+   } else if (semantic_name == TGSI_SEMANTIC_POSITION ||
+              semantic_name == TGSI_SEMANTIC_CLIPVERTEX) {
+      /* these inputs are handled specially always */
+      return -1;
    } else {
       /* Otherwise, search in the FS inputs, with a decent default
        * if we don't find it.
+       * This probably only matters for layer, vpindex, culldist, maybe
+       * front_face.
        */
       uint j;
-      interp = TGSI_INTERPOLATE_PERSPECTIVE;
+      if (semantic_name == TGSI_SEMANTIC_LAYER ||
+          semantic_name == TGSI_SEMANTIC_VIEWPORT_INDEX) {
+         interp = TGSI_INTERPOLATE_CONSTANT;
+      }
+      else {
+         interp = TGSI_INTERPOLATE_PERSPECTIVE;
+      }
       if (fs) {
          for (j = 0; j < fs->info.num_inputs; j++) {
             if (semantic_name == fs->info.input_semantic_name[j] &&
@@ -738,13 +771,16 @@ find_interp(const struct draw_fragment_shader *fs, int *indexed_interp,
  * primitive that really requires clipping.
  */
 static void 
-clip_init_state( struct draw_stage *stage )
+clip_init_state(struct draw_stage *stage)
 {
    struct clip_stage *clipper = clip_stage( stage );
    const struct draw_context *draw = stage->draw;
    const struct draw_fragment_shader *fs = draw->fs.fragment_shader;
    const struct tgsi_shader_info *info = draw_get_shader_info(draw);
    uint i, j;
+   int indexed_interp[2];
+
+   clipper->pos_attr = draw_current_shader_position_output(draw);
 
    /* We need to know for each attribute what kind of interpolation is
     * done on it (flat, smooth or noperspective).  But the information
@@ -765,7 +801,6 @@ clip_init_state( struct draw_stage *stage )
    /* First pick up the interpolation mode for
     * gl_Color/gl_SecondaryColor, with the correct default.
     */
-   int indexed_interp[2];
    indexed_interp[0] = indexed_interp[1] = draw->rasterizer->flatshade ?
       TGSI_INTERPOLATE_CONSTANT : TGSI_INTERPOLATE_PERSPECTIVE;
 
@@ -778,29 +813,33 @@ clip_init_state( struct draw_stage *stage )
       }
    }
 
-   /* Then resolve the interpolation mode for every output attribute.
-    *
-    * Given how the rest of the code, the most efficient way is to
-    * have a vector of flat-mode attributes, and a mask for
-    * noperspective attributes.
-    */
+   /* Then resolve the interpolation mode for every output attribute. */
 
-   clipper->num_flat_attribs = 0;
-   memset(clipper->noperspective_attribs, 0, sizeof(clipper->noperspective_attribs));
+   clipper->num_const_attribs = 0;
+   clipper->num_linear_attribs = 0;
+   clipper->num_perspect_attribs = 0;
    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;
+      switch (interp) {
+      case TGSI_INTERPOLATE_CONSTANT:
+         clipper->const_attribs[clipper->num_const_attribs] = i;
+         clipper->num_const_attribs++;
+         break;
+      case TGSI_INTERPOLATE_LINEAR:
+         clipper->linear_attribs[clipper->num_linear_attribs] = i;
+         clipper->num_linear_attribs++;
+         break;
+      case TGSI_INTERPOLATE_PERSPECTIVE:
+         clipper->perspect_attribs[clipper->num_perspect_attribs] = i;
+         clipper->num_perspect_attribs++;
+         break;
+      default:
+         assert(interp == -1);
+         break;
+      }
    }
    /* Search the extra vertex attributes */
    for (j = 0; j < draw->extra_shader_outputs.num; j++) {
@@ -808,39 +847,47 @@ clip_init_state( struct draw_stage *stage )
       int interp = find_interp(fs, indexed_interp,
                                draw->extra_shader_outputs.semantic_name[j],
                                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;
+      switch (interp) {
+      case TGSI_INTERPOLATE_CONSTANT:
+         clipper->const_attribs[clipper->num_const_attribs] = i + j;
+         clipper->num_const_attribs++;
+         break;
+      case TGSI_INTERPOLATE_LINEAR:
+         clipper->linear_attribs[clipper->num_linear_attribs] = i + j;
+         clipper->num_linear_attribs++;
+         break;
+      case TGSI_INTERPOLATE_PERSPECTIVE:
+         clipper->perspect_attribs[clipper->num_perspect_attribs] = i + j;
+         clipper->num_perspect_attribs++;
+         break;
+      default:
+         assert(interp == -1);
+         break;
+      }
    }
-   
+
    stage->tri = clip_tri;
    stage->line = clip_line;
 }
 
 
 
-static void clip_first_tri( struct draw_stage *stage,
-                           struct prim_header *header )
+static void clip_first_tri(struct draw_stage *stage,
+                           struct prim_header *header)
 {
    clip_init_state( stage );
    stage->tri( stage, header );
 }
 
-static void clip_first_line( struct draw_stage *stage,
-                            struct prim_header *header )
+static void clip_first_line(struct draw_stage *stage,
+                            struct prim_header *header)
 {
    clip_init_state( stage );
    stage->line( stage, header );
 }
 
 
-static void clip_flush( struct draw_stage *stage, 
-                            unsigned flags )
+static void clip_flush(struct draw_stage *stage, unsigned flags)
 {
    stage->tri = clip_first_tri;
    stage->line = clip_first_line;
@@ -848,13 +895,13 @@ static void clip_flush( struct draw_stage *stage,
 }
 
 
-static void clip_reset_stipple_counter( struct draw_stage *stage )
+static void clip_reset_stipple_counter(struct draw_stage *stage)
 {
    stage->next->reset_stipple_counter( stage->next );
 }
 
 
-static void clip_destroy( struct draw_stage *stage )
+static void clip_destroy(struct draw_stage *stage)
 {
    draw_free_temp_verts( stage );
    FREE( stage );
@@ -865,7 +912,7 @@ static void clip_destroy( struct draw_stage *stage )
  * Allocate a new clipper stage.
  * \return pointer to new stage object
  */
-struct draw_stage *draw_clip_stage( struct draw_context *draw )
+struct draw_stage *draw_clip_stage(struct draw_context *draw)
 {
    struct clip_stage *clipper = CALLOC_STRUCT(clip_stage);
    if (clipper == NULL)