Subversion Repositories Kolibri OS

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
5564 serge 1
/**************************************************************************
2
 *
3
 * Copyright 2007 VMware, Inc.
4
 * All Rights Reserved.
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a
7
 * copy of this software and associated documentation files (the
8
 * "Software"), to deal in the Software without restriction, including
9
 * without limitation the rights to use, copy, modify, merge, publish,
10
 * distribute, sub license, and/or sell copies of the Software, and to
11
 * permit persons to whom the Software is furnished to do so, subject to
12
 * the following conditions:
13
 *
14
 * The above copyright notice and this permission notice (including the
15
 * next paragraph) shall be included in all copies or substantial portions
16
 * of the Software.
17
 *
18
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21
 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
22
 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
 *
26
 **************************************************************************/
27
 
28
/**
29
 * \brief  Clipping stage
30
 *
31
 * \author  Keith Whitwell 
32
 */
33
 
34
 
35
#include "util/u_memory.h"
36
#include "util/u_math.h"
37
 
38
#include "pipe/p_shader_tokens.h"
39
 
40
#include "draw_vs.h"
41
#include "draw_pipe.h"
42
#include "draw_fs.h"
43
#include "draw_gs.h"
44
 
45
 
46
/** Set to 1 to enable printing of coords before/after clipping */
47
#define DEBUG_CLIP 0
48
 
49
 
50
#ifndef DIFFERENT_SIGNS
51
#define DIFFERENT_SIGNS(x, y) ((x) * (y) <= 0.0F && (x) - (y) != 0.0F)
52
#endif
53
 
54
#define MAX_CLIPPED_VERTICES ((2 * (6 + PIPE_MAX_CLIP_PLANES))+1)
55
 
56
 
57
 
58
struct clip_stage {
59
   struct draw_stage stage;      /**< base class */
60
 
61
   /* List of the attributes to be flatshaded. */
62
   uint num_flat_attribs;
63
   uint flat_attribs[PIPE_MAX_SHADER_OUTPUTS];
64
 
65
   /* Mask of attributes in noperspective mode */
66
   boolean noperspective_attribs[PIPE_MAX_SHADER_OUTPUTS];
67
 
68
   float (*plane)[4];
69
};
70
 
71
 
72
/** Cast wrapper */
73
static INLINE struct clip_stage *clip_stage( struct draw_stage *stage )
74
{
75
   return (struct clip_stage *)stage;
76
}
77
 
78
static INLINE unsigned
79
draw_viewport_index(struct draw_context *draw,
80
                    const struct vertex_header *leading_vertex)
81
{
82
   if (draw_current_shader_uses_viewport_index(draw)) {
83
      unsigned viewport_index_output =
84
         draw_current_shader_viewport_index_output(draw);
85
      unsigned viewport_index =
86
         *((unsigned*)leading_vertex->data[viewport_index_output]);
87
      return draw_clamp_viewport_idx(viewport_index);
88
   } else {
89
      return 0;
90
   }
91
}
92
 
93
 
94
#define LINTERP(T, OUT, IN) ((OUT) + (T) * ((IN) - (OUT)))
95
 
96
 
97
/* All attributes are float[4], so this is easy:
98
 */
99
static void interp_attr( float dst[4],
100
			 float t,
101
			 const float in[4],
102
			 const float out[4] )
103
{
104
   dst[0] = LINTERP( t, out[0], in[0] );
105
   dst[1] = LINTERP( t, out[1], in[1] );
106
   dst[2] = LINTERP( t, out[2], in[2] );
107
   dst[3] = LINTERP( t, out[3], in[3] );
108
}
109
 
110
 
111
/**
112
 * Copy flat shaded attributes src vertex to dst vertex.
113
 */
114
static void copy_flat( struct draw_stage *stage,
115
                       struct vertex_header *dst,
116
                       const struct vertex_header *src )
117
{
118
   const struct clip_stage *clipper = clip_stage(stage);
119
   uint i;
120
   for (i = 0; i < clipper->num_flat_attribs; i++) {
121
      const uint attr = clipper->flat_attribs[i];
122
      COPY_4FV(dst->data[attr], src->data[attr]);
123
   }
124
}
125
 
126
/* Interpolate between two vertices to produce a third.
127
 */
128
static void interp( const struct clip_stage *clip,
129
		    struct vertex_header *dst,
130
		    float t,
131
		    const struct vertex_header *out,
132
		    const struct vertex_header *in,
133
                    unsigned viewport_index )
134
{
135
   const unsigned nr_attrs = draw_num_shader_outputs(clip->stage.draw);
136
   const unsigned pos_attr = draw_current_shader_position_output(clip->stage.draw);
137
   const unsigned clip_attr = draw_current_shader_clipvertex_output(clip->stage.draw);
138
   unsigned j;
139
   float t_nopersp;
140
 
141
   /* Vertex header.
142
    */
143
   dst->clipmask = 0;
144
   dst->edgeflag = 0;        /* will get overwritten later */
145
   dst->have_clipdist = in->have_clipdist;
146
   dst->vertex_id = UNDEFINED_VERTEX_ID;
147
 
148
   /* Interpolate the clip-space coords.
149
    */
150
   interp_attr(dst->clip, t, in->clip, out->clip);
151
   /* interpolate the clip-space position */
152
   interp_attr(dst->pre_clip_pos, t, in->pre_clip_pos, out->pre_clip_pos);
153
 
154
   /* Do the projective divide and viewport transformation to get
155
    * new window coordinates:
156
    */
157
   {
158
      const float *pos = dst->pre_clip_pos;
159
      const float *scale =
160
         clip->stage.draw->viewports[viewport_index].scale;
161
      const float *trans =
162
         clip->stage.draw->viewports[viewport_index].translate;
163
      const float oow = 1.0f / pos[3];
164
 
165
      dst->data[pos_attr][0] = pos[0] * oow * scale[0] + trans[0];
166
      dst->data[pos_attr][1] = pos[1] * oow * scale[1] + trans[1];
167
      dst->data[pos_attr][2] = pos[2] * oow * scale[2] + trans[2];
168
      dst->data[pos_attr][3] = oow;
169
   }
170
 
171
   /**
172
    * Compute the t in screen-space instead of 3d space to use
173
    * for noperspective interpolation.
174
    *
175
    * The points can be aligned with the X axis, so in that case try
176
    * the Y.  When both points are at the same screen position, we can
177
    * pick whatever value (the interpolated point won't be in front
178
    * anyway), so just use the 3d t.
179
    */
180
   {
181
      int k;
182
      t_nopersp = t;
183
      /* find either in.x != out.x or in.y != out.y */
184
      for (k = 0; k < 2; k++) {
185
         if (in->clip[k] != out->clip[k]) {
186
            /* do divide by W, then compute linear interpolation factor */
187
            float in_coord = in->clip[k] / in->clip[3];
188
            float out_coord = out->clip[k] / out->clip[3];
189
            float dst_coord = dst->clip[k] / dst->clip[3];
190
            t_nopersp = (dst_coord - out_coord) / (in_coord - out_coord);
191
            break;
192
         }
193
      }
194
   }
195
 
196
   /* Other attributes
197
    */
198
   for (j = 0; j < nr_attrs; j++) {
199
      if (j != pos_attr && j != clip_attr) {
200
         if (clip->noperspective_attribs[j])
201
            interp_attr(dst->data[j], t_nopersp, in->data[j], out->data[j]);
202
         else
203
            interp_attr(dst->data[j], t, in->data[j], out->data[j]);
204
      }
205
   }
206
}
207
 
208
/**
209
 * Checks whether the specifed triangle is empty and if it is returns
210
 * true, otherwise returns false.
211
 * Triangle is considered null/empty if it's area is qual to zero.
212
 */
213
static INLINE boolean
214
is_tri_null(struct draw_context *draw, const struct prim_header *header)
215
{
216
   const unsigned pos_attr = draw_current_shader_position_output(draw);
217
   float x1 = header->v[1]->data[pos_attr][0] - header->v[0]->data[pos_attr][0];
218
   float y1 = header->v[1]->data[pos_attr][1] - header->v[0]->data[pos_attr][1];
219
   float z1 = header->v[1]->data[pos_attr][2] - header->v[0]->data[pos_attr][2];
220
 
221
   float x2 = header->v[2]->data[pos_attr][0] - header->v[0]->data[pos_attr][0];
222
   float y2 = header->v[2]->data[pos_attr][1] - header->v[0]->data[pos_attr][1];
223
   float z2 = header->v[2]->data[pos_attr][2] - header->v[0]->data[pos_attr][2];
224
 
225
   float vx = y1 * z2 - z1 * y2;
226
   float vy = x1 * z2 - z1 * x2;
227
   float vz = x1 * y2 - y1 * x2;
228
 
229
   return (vx*vx  + vy*vy + vz*vz) == 0.f;
230
}
231
 
232
/**
233
 * Emit a post-clip polygon to the next pipeline stage.  The polygon
234
 * will be convex and the provoking vertex will always be vertex[0].
235
 */
236
static void emit_poly( struct draw_stage *stage,
237
		       struct vertex_header **inlist,
238
                       const boolean *edgeflags,
239
		       unsigned n,
240
		       const struct prim_header *origPrim)
241
{
242
   struct prim_header header;
243
   unsigned i;
244
   ushort edge_first, edge_middle, edge_last;
245
   boolean last_tri_was_null = FALSE;
246
   boolean tri_was_not_null = FALSE;
247
 
248
   if (stage->draw->rasterizer->flatshade_first) {
249
      edge_first  = DRAW_PIPE_EDGE_FLAG_0;
250
      edge_middle = DRAW_PIPE_EDGE_FLAG_1;
251
      edge_last   = DRAW_PIPE_EDGE_FLAG_2;
252
   }
253
   else {
254
      edge_first  = DRAW_PIPE_EDGE_FLAG_2;
255
      edge_middle = DRAW_PIPE_EDGE_FLAG_0;
256
      edge_last   = DRAW_PIPE_EDGE_FLAG_1;
257
   }
258
 
259
   if (!edgeflags[0])
260
      edge_first = 0;
261
 
262
   /* later stages may need the determinant, but only the sign matters */
263
   header.det = origPrim->det;
264
   header.flags = DRAW_PIPE_RESET_STIPPLE | edge_first | edge_middle;
265
   header.pad = 0;
266
 
267
   for (i = 2; i < n; i++, header.flags = edge_middle) {
268
      boolean tri_null;
269
      /* order the triangle verts to respect the provoking vertex mode */
270
      if (stage->draw->rasterizer->flatshade_first) {
271
         header.v[0] = inlist[0];  /* the provoking vertex */
272
         header.v[1] = inlist[i-1];
273
         header.v[2] = inlist[i];
274
      }
275
      else {
276
         header.v[0] = inlist[i-1];
277
         header.v[1] = inlist[i];
278
         header.v[2] = inlist[0];  /* the provoking vertex */
279
      }
280
 
281
      tri_null = is_tri_null(stage->draw, &header);
282
      /* If we generated a triangle with an area, aka. non-null triangle,
283
       * or if the previous triangle was also null then skip all subsequent
284
       * null triangles */
285
      if ((tri_was_not_null && tri_null) || (last_tri_was_null && tri_null)) {
286
         last_tri_was_null = tri_null;
287
         continue;
288
      }
289
      last_tri_was_null = tri_null;
290
      if (!tri_null) {
291
         tri_was_not_null = TRUE;
292
      }
293
 
294
      if (!edgeflags[i-1]) {
295
         header.flags &= ~edge_middle;
296
      }
297
 
298
      if (i == n - 1 && edgeflags[i])
299
         header.flags |= edge_last;
300
 
301
      if (DEBUG_CLIP) {
302
         uint j, k;
303
         debug_printf("Clipped tri: (flat-shade-first = %d)\n",
304
                      stage->draw->rasterizer->flatshade_first);
305
         for (j = 0; j < 3; j++) {
306
            debug_printf("  Vert %d: clip: %f %f %f %f\n", j,
307
                         header.v[j]->clip[0],
308
                         header.v[j]->clip[1],
309
                         header.v[j]->clip[2],
310
                         header.v[j]->clip[3]);
311
            for (k = 0; k < draw_num_shader_outputs(stage->draw); k++) {
312
               debug_printf("  Vert %d: Attr %d:  %f %f %f %f\n", j, k,
313
                            header.v[j]->data[k][0],
314
                            header.v[j]->data[k][1],
315
                            header.v[j]->data[k][2],
316
                            header.v[j]->data[k][3]);
317
            }
318
         }
319
      }
320
      stage->next->tri( stage->next, &header );
321
   }
322
}
323
 
324
 
325
static INLINE float
326
dot4(const float *a, const float *b)
327
{
328
   return (a[0] * b[0] +
329
           a[1] * b[1] +
330
           a[2] * b[2] +
331
           a[3] * b[3]);
332
}
333
 
334
/*
335
 * this function extracts the clip distance for the current plane,
336
 * it first checks if the shader provided a clip distance, otherwise
337
 * it works out the value using the clipvertex
338
 */
339
static INLINE float getclipdist(const struct clip_stage *clipper,
340
                                struct vertex_header *vert,
341
                                int plane_idx)
342
{
343
   const float *plane;
344
   float dp;
345
   if (vert->have_clipdist && plane_idx >= 6) {
346
      /* pick the correct clipdistance element from the output vectors */
347
      int _idx = plane_idx - 6;
348
      int cdi = _idx >= 4;
349
      int vidx = cdi ? _idx - 4 : _idx;
350
      dp = vert->data[draw_current_shader_clipdistance_output(clipper->stage.draw, cdi)][vidx];
351
   } else {
352
      plane = clipper->plane[plane_idx];
353
      dp = dot4(vert->clip, plane);
354
   }
355
   return dp;
356
}
357
 
358
/* Clip a triangle against the viewport and user clip planes.
359
 */
360
static void
361
do_clip_tri( struct draw_stage *stage,
362
	     struct prim_header *header,
363
	     unsigned clipmask )
364
{
365
   struct clip_stage *clipper = clip_stage( stage );
366
   struct vertex_header *a[MAX_CLIPPED_VERTICES];
367
   struct vertex_header *b[MAX_CLIPPED_VERTICES];
368
   struct vertex_header **inlist = a;
369
   struct vertex_header **outlist = b;
370
   unsigned tmpnr = 0;
371
   unsigned n = 3;
372
   unsigned i;
373
   boolean aEdges[MAX_CLIPPED_VERTICES];
374
   boolean bEdges[MAX_CLIPPED_VERTICES];
375
   boolean *inEdges = aEdges;
376
   boolean *outEdges = bEdges;
377
   int viewport_index = 0;
378
 
379
   inlist[0] = header->v[0];
380
   inlist[1] = header->v[1];
381
   inlist[2] = header->v[2];
382
 
383
   viewport_index = draw_viewport_index(clipper->stage.draw, inlist[0]);
384
 
385
   if (DEBUG_CLIP) {
386
      const float *v0 = header->v[0]->clip;
387
      const float *v1 = header->v[1]->clip;
388
      const float *v2 = header->v[2]->clip;
389
      debug_printf("Clip triangle:\n");
390
      debug_printf(" %f, %f, %f, %f\n", v0[0], v0[1], v0[2], v0[3]);
391
      debug_printf(" %f, %f, %f, %f\n", v1[0], v1[1], v1[2], v1[3]);
392
      debug_printf(" %f, %f, %f, %f\n", v2[0], v2[1], v2[2], v2[3]);
393
   }
394
 
395
   /*
396
    * Note: at this point we can't just use the per-vertex edge flags.
397
    * We have to observe the edge flag bits set in header->flags which
398
    * were set during primitive decomposition.  Put those flags into
399
    * an edge flags array which parallels the vertex array.
400
    * Later, in the 'unfilled' pipeline stage we'll draw the edge if both
401
    * the header.flags bit is set AND the per-vertex edgeflag field is set.
402
    */
403
   inEdges[0] = !!(header->flags & DRAW_PIPE_EDGE_FLAG_0);
404
   inEdges[1] = !!(header->flags & DRAW_PIPE_EDGE_FLAG_1);
405
   inEdges[2] = !!(header->flags & DRAW_PIPE_EDGE_FLAG_2);
406
 
407
   while (clipmask && n >= 3) {
408
      const unsigned plane_idx = ffs(clipmask)-1;
409
      const boolean is_user_clip_plane = plane_idx >= 6;
410
      struct vertex_header *vert_prev = inlist[0];
411
      boolean *edge_prev = &inEdges[0];
412
      float dp_prev;
413
      unsigned outcount = 0;
414
 
415
      dp_prev = getclipdist(clipper, vert_prev, plane_idx);
416
      clipmask &= ~(1<
417
 
418
      if (util_is_inf_or_nan(dp_prev))
419
         return; //discard nan
420
 
421
      assert(n < MAX_CLIPPED_VERTICES);
422
      if (n >= MAX_CLIPPED_VERTICES)
423
         return;
424
      inlist[n] = inlist[0]; /* prevent rotation of vertices */
425
      inEdges[n] = inEdges[0];
426
 
427
      for (i = 1; i <= n; i++) {
428
	 struct vertex_header *vert = inlist[i];
429
         boolean *edge = &inEdges[i];
430
 
431
         float dp = getclipdist(clipper, vert, plane_idx);
432
 
433
         if (util_is_inf_or_nan(dp))
434
            return; //discard nan
435
 
436
	 if (dp_prev >= 0.0f) {
437
            assert(outcount < MAX_CLIPPED_VERTICES);
438
            if (outcount >= MAX_CLIPPED_VERTICES)
439
               return;
440
            outEdges[outcount] = *edge_prev;
441
	    outlist[outcount++] = vert_prev;
442
	 }
443
 
444
	 if (DIFFERENT_SIGNS(dp, dp_prev)) {
445
	    struct vertex_header *new_vert;
446
            boolean *new_edge;
447
 
448
            assert(tmpnr < MAX_CLIPPED_VERTICES + 1);
449
            if (tmpnr >= MAX_CLIPPED_VERTICES + 1)
450
               return;
451
            new_vert = clipper->stage.tmp[tmpnr++];
452
 
453
            assert(outcount < MAX_CLIPPED_VERTICES);
454
            if (outcount >= MAX_CLIPPED_VERTICES)
455
               return;
456
 
457
            new_edge = &outEdges[outcount];
458
	    outlist[outcount++] = new_vert;
459
 
460
	    if (dp < 0.0f) {
461
	       /* Going out of bounds.  Avoid division by zero as we
462
		* know dp != dp_prev from DIFFERENT_SIGNS, above.
463
		*/
464
	       float t = dp / (dp - dp_prev);
465
	       interp( clipper, new_vert, t, vert, vert_prev, viewport_index );
466
 
467
	       /* Whether or not to set edge flag for the new vert depends
468
                * on whether it's a user-defined clipping plane.  We're
469
                * copying NVIDIA's behaviour here.
470
		*/
471
               if (is_user_clip_plane) {
472
                  /* we want to see an edge along the clip plane */
473
                  *new_edge = TRUE;
474
                  new_vert->edgeflag = TRUE;
475
               }
476
               else {
477
                  /* we don't want to see an edge along the frustum clip plane */
478
                  *new_edge = *edge_prev;
479
                  new_vert->edgeflag = FALSE;
480
               }
481
	    }
482
            else {
483
	       /* Coming back in.
484
		*/
485
	       float t = dp_prev / (dp_prev - dp);
486
	       interp( clipper, new_vert, t, vert_prev, vert, viewport_index );
487
 
488
	       /* Copy starting vert's edgeflag:
489
		*/
490
	       new_vert->edgeflag = vert_prev->edgeflag;
491
               *new_edge = *edge_prev;
492
	    }
493
	 }
494
 
495
	 vert_prev = vert;
496
         edge_prev = edge;
497
	 dp_prev = dp;
498
      }
499
 
500
      /* swap in/out lists */
501
      {
502
	 struct vertex_header **tmp = inlist;
503
	 inlist = outlist;
504
	 outlist = tmp;
505
	 n = outcount;
506
      }
507
      {
508
         boolean *tmp = inEdges;
509
         inEdges = outEdges;
510
         outEdges = tmp;
511
      }
512
 
513
   }
514
 
515
   /* If flat-shading, copy provoking vertex color to polygon vertex[0]
516
    */
517
   if (n >= 3) {
518
      if (clipper->num_flat_attribs) {
519
         if (stage->draw->rasterizer->flatshade_first) {
520
            if (inlist[0] != header->v[0]) {
521
               assert(tmpnr < MAX_CLIPPED_VERTICES + 1);
522
               if (tmpnr >= MAX_CLIPPED_VERTICES + 1)
523
                  return;
524
               inlist[0] = dup_vert(stage, inlist[0], tmpnr++);
525
               copy_flat(stage, inlist[0], header->v[0]);
526
            }
527
         }
528
         else {
529
            if (inlist[0] != header->v[2]) {
530
               assert(tmpnr < MAX_CLIPPED_VERTICES + 1);
531
               if (tmpnr >= MAX_CLIPPED_VERTICES + 1)
532
                  return;
533
               inlist[0] = dup_vert(stage, inlist[0], tmpnr++);
534
               copy_flat(stage, inlist[0], header->v[2]);
535
            }
536
         }
537
      }
538
 
539
      /* Emit the polygon as triangles to the setup stage:
540
       */
541
      emit_poly( stage, inlist, inEdges, n, header );
542
   }
543
}
544
 
545
 
546
/* Clip a line against the viewport and user clip planes.
547
 */
548
static void
549
do_clip_line( struct draw_stage *stage,
550
	      struct prim_header *header,
551
	      unsigned clipmask )
552
{
553
   const struct clip_stage *clipper = clip_stage( stage );
554
   struct vertex_header *v0 = header->v[0];
555
   struct vertex_header *v1 = header->v[1];
556
   float t0 = 0.0F;
557
   float t1 = 0.0F;
558
   struct prim_header newprim;
559
   int viewport_index = draw_viewport_index(clipper->stage.draw, v0);
560
 
561
   while (clipmask) {
562
      const unsigned plane_idx = ffs(clipmask)-1;
563
      const float dp0 = getclipdist(clipper, v0, plane_idx);
564
      const float dp1 = getclipdist(clipper, v1, plane_idx);
565
 
566
      if (util_is_inf_or_nan(dp0) || util_is_inf_or_nan(dp1))
567
         return; //discard nan
568
 
569
      if (dp1 < 0.0F) {
570
	 float t = dp1 / (dp1 - dp0);
571
         t1 = MAX2(t1, t);
572
      }
573
 
574
      if (dp0 < 0.0F) {
575
	 float t = dp0 / (dp0 - dp1);
576
         t0 = MAX2(t0, t);
577
      }
578
 
579
      if (t0 + t1 >= 1.0F)
580
	 return; /* discard */
581
 
582
      clipmask &= ~(1 << plane_idx);  /* turn off this plane's bit */
583
   }
584
 
585
   if (v0->clipmask) {
586
      interp( clipper, stage->tmp[0], t0, v0, v1, viewport_index );
587
      if (stage->draw->rasterizer->flatshade_first) {
588
         copy_flat(stage, stage->tmp[0], v0);  /* copy v0 color to tmp[0] */
589
      }
590
      else {
591
         copy_flat(stage, stage->tmp[0], v1);  /* copy v1 color to tmp[0] */
592
      }
593
      newprim.v[0] = stage->tmp[0];
594
   }
595
   else {
596
      newprim.v[0] = v0;
597
   }
598
 
599
   if (v1->clipmask) {
600
      interp( clipper, stage->tmp[1], t1, v1, v0, viewport_index );
601
      if (stage->draw->rasterizer->flatshade_first) {
602
         copy_flat(stage, stage->tmp[1], v0);  /* copy v0 color to tmp[1] */
603
      }
604
      else {
605
         copy_flat(stage, stage->tmp[1], v1);  /* copy v1 color to tmp[1] */
606
      }
607
      newprim.v[1] = stage->tmp[1];
608
   }
609
   else {
610
      newprim.v[1] = v1;
611
   }
612
 
613
   stage->next->line( stage->next, &newprim );
614
}
615
 
616
 
617
static void
618
clip_point( struct draw_stage *stage,
619
            struct prim_header *header )
620
{
621
   if (header->v[0]->clipmask == 0)
622
      stage->next->point( stage->next, header );
623
}
624
 
625
 
626
/*
627
 * Clip points but ignore the first 4 (xy) clip planes.
628
 * (Because the generated clip mask is completely unaffacted by guard band,
629
 * we still need to manually evaluate the x/y planes if they are outside
630
 * the guard band and not just outside the vp.)
631
 */
632
static void
633
clip_point_guard_xy( struct draw_stage *stage,
634
                     struct prim_header *header )
635
{
636
   unsigned clipmask = header->v[0]->clipmask;
637
   if ((clipmask & 0xffffffff) == 0)
638
      stage->next->point(stage->next, header);
639
   else if ((clipmask & 0xfffffff0) == 0) {
640
      while (clipmask) {
641
         const unsigned plane_idx = ffs(clipmask)-1;
642
         clipmask &= ~(1 << plane_idx);  /* turn off this plane's bit */
643
         /* TODO: this should really do proper guardband clipping,
644
          * currently just throw out infs/nans.
645
          * Also note that vertices with negative w values MUST be tossed
646
          * out (not sure if proper guardband clipping would do this
647
          * automatically). These would usually be captured by depth clip
648
          * too but this can be disabled.
649
          */
650
         if (header->v[0]->clip[3] <= 0.0f ||
651
             util_is_inf_or_nan(header->v[0]->clip[0]) ||
652
             util_is_inf_or_nan(header->v[0]->clip[1]))
653
            return;
654
      }
655
      stage->next->point(stage->next, header);
656
   }
657
}
658
 
659
 
660
static void
661
clip_first_point( struct draw_stage *stage,
662
                  struct prim_header *header )
663
{
664
   stage->point = stage->draw->guard_band_points_xy ? clip_point_guard_xy : clip_point;
665
   stage->point(stage, header);
666
}
667
 
668
 
669
static void
670
clip_line( struct draw_stage *stage,
671
	   struct prim_header *header )
672
{
673
   unsigned clipmask = (header->v[0]->clipmask |
674
                        header->v[1]->clipmask);
675
 
676
   if (clipmask == 0) {
677
      /* no clipping needed */
678
      stage->next->line( stage->next, header );
679
   }
680
   else if ((header->v[0]->clipmask &
681
             header->v[1]->clipmask) == 0) {
682
      do_clip_line(stage, header, clipmask);
683
   }
684
   /* else, totally clipped */
685
}
686
 
687
 
688
static void
689
clip_tri( struct draw_stage *stage,
690
          struct prim_header *header )
691
{
692
   unsigned clipmask = (header->v[0]->clipmask |
693
                        header->v[1]->clipmask |
694
                        header->v[2]->clipmask);
695
 
696
   if (clipmask == 0) {
697
      /* no clipping needed */
698
      stage->next->tri( stage->next, header );
699
   }
700
   else if ((header->v[0]->clipmask &
701
             header->v[1]->clipmask &
702
             header->v[2]->clipmask) == 0) {
703
      do_clip_tri(stage, header, clipmask);
704
   }
705
}
706
 
707
 
708
static int
709
find_interp(const struct draw_fragment_shader *fs, int *indexed_interp,
710
            uint semantic_name, uint semantic_index)
711
{
712
   int interp;
713
   /* If it's gl_{Front,Back}{,Secondary}Color, pick up the mode
714
    * from the array we've filled before. */
715
   if (semantic_name == TGSI_SEMANTIC_COLOR ||
716
       semantic_name == TGSI_SEMANTIC_BCOLOR) {
717
      interp = indexed_interp[semantic_index];
718
   } else {
719
      /* Otherwise, search in the FS inputs, with a decent default
720
       * if we don't find it.
721
       */
722
      uint j;
723
      interp = TGSI_INTERPOLATE_PERSPECTIVE;
724
      if (fs) {
725
         for (j = 0; j < fs->info.num_inputs; j++) {
726
            if (semantic_name == fs->info.input_semantic_name[j] &&
727
                semantic_index == fs->info.input_semantic_index[j]) {
728
               interp = fs->info.input_interpolate[j];
729
               break;
730
            }
731
         }
732
      }
733
   }
734
   return interp;
735
}
736
 
737
/* Update state.  Could further delay this until we hit the first
738
 * primitive that really requires clipping.
739
 */
740
static void
741
clip_init_state( struct draw_stage *stage )
742
{
743
   struct clip_stage *clipper = clip_stage( stage );
744
   const struct draw_context *draw = stage->draw;
745
   const struct draw_fragment_shader *fs = draw->fs.fragment_shader;
746
   const struct tgsi_shader_info *info = draw_get_shader_info(draw);
747
   uint i, j;
748
 
749
   /* We need to know for each attribute what kind of interpolation is
750
    * done on it (flat, smooth or noperspective).  But the information
751
    * is not directly accessible for outputs, only for inputs.  So we
752
    * have to match semantic name and index between the VS (or GS/ES)
753
    * outputs and the FS inputs to get to the interpolation mode.
754
    *
755
    * The only hitch is with gl_FrontColor/gl_BackColor which map to
756
    * gl_Color, and their Secondary versions.  First there are (up to)
757
    * two outputs for one input, so we tuck the information in a
758
    * specific array.  Second if they don't have qualifiers, the
759
    * default value has to be picked from the global shade mode.
760
    *
761
    * Of course, if we don't have a fragment shader in the first
762
    * place, defaults should be used.
763
    */
764
 
765
   /* First pick up the interpolation mode for
766
    * gl_Color/gl_SecondaryColor, with the correct default.
767
    */
768
   int indexed_interp[2];
769
   indexed_interp[0] = indexed_interp[1] = draw->rasterizer->flatshade ?
770
      TGSI_INTERPOLATE_CONSTANT : TGSI_INTERPOLATE_PERSPECTIVE;
771
 
772
   if (fs) {
773
      for (i = 0; i < fs->info.num_inputs; i++) {
774
         if (fs->info.input_semantic_name[i] == TGSI_SEMANTIC_COLOR) {
775
            if (fs->info.input_interpolate[i] != TGSI_INTERPOLATE_COLOR)
776
               indexed_interp[fs->info.input_semantic_index[i]] = fs->info.input_interpolate[i];
777
         }
778
      }
779
   }
780
 
781
   /* Then resolve the interpolation mode for every output attribute.
782
    *
783
    * Given how the rest of the code, the most efficient way is to
784
    * have a vector of flat-mode attributes, and a mask for
785
    * noperspective attributes.
786
    */
787
 
788
   clipper->num_flat_attribs = 0;
789
   memset(clipper->noperspective_attribs, 0, sizeof(clipper->noperspective_attribs));
790
   for (i = 0; i < info->num_outputs; i++) {
791
      /* Find the interpolation mode for a specific attribute */
792
      int interp = find_interp(fs, indexed_interp,
793
                               info->output_semantic_name[i],
794
                               info->output_semantic_index[i]);
795
      /* If it's flat, add it to the flat vector.  Otherwise update
796
       * the noperspective mask.
797
       */
798
 
799
      if (interp == TGSI_INTERPOLATE_CONSTANT) {
800
         clipper->flat_attribs[clipper->num_flat_attribs] = i;
801
         clipper->num_flat_attribs++;
802
      } else
803
         clipper->noperspective_attribs[i] = interp == TGSI_INTERPOLATE_LINEAR;
804
   }
805
   /* Search the extra vertex attributes */
806
   for (j = 0; j < draw->extra_shader_outputs.num; j++) {
807
      /* Find the interpolation mode for a specific attribute */
808
      int interp = find_interp(fs, indexed_interp,
809
                               draw->extra_shader_outputs.semantic_name[j],
810
                               draw->extra_shader_outputs.semantic_index[j]);
811
      /* If it's flat, add it to the flat vector.  Otherwise update
812
       * the noperspective mask.
813
       */
814
      if (interp == TGSI_INTERPOLATE_CONSTANT) {
815
         clipper->flat_attribs[clipper->num_flat_attribs] = i + j;
816
         clipper->num_flat_attribs++;
817
      } else
818
         clipper->noperspective_attribs[i + j] = interp == TGSI_INTERPOLATE_LINEAR;
819
   }
820
 
821
   stage->tri = clip_tri;
822
   stage->line = clip_line;
823
}
824
 
825
 
826
 
827
static void clip_first_tri( struct draw_stage *stage,
828
			    struct prim_header *header )
829
{
830
   clip_init_state( stage );
831
   stage->tri( stage, header );
832
}
833
 
834
static void clip_first_line( struct draw_stage *stage,
835
			     struct prim_header *header )
836
{
837
   clip_init_state( stage );
838
   stage->line( stage, header );
839
}
840
 
841
 
842
static void clip_flush( struct draw_stage *stage,
843
			     unsigned flags )
844
{
845
   stage->tri = clip_first_tri;
846
   stage->line = clip_first_line;
847
   stage->next->flush( stage->next, flags );
848
}
849
 
850
 
851
static void clip_reset_stipple_counter( struct draw_stage *stage )
852
{
853
   stage->next->reset_stipple_counter( stage->next );
854
}
855
 
856
 
857
static void clip_destroy( struct draw_stage *stage )
858
{
859
   draw_free_temp_verts( stage );
860
   FREE( stage );
861
}
862
 
863
 
864
/**
865
 * Allocate a new clipper stage.
866
 * \return pointer to new stage object
867
 */
868
struct draw_stage *draw_clip_stage( struct draw_context *draw )
869
{
870
   struct clip_stage *clipper = CALLOC_STRUCT(clip_stage);
871
   if (clipper == NULL)
872
      goto fail;
873
 
874
   clipper->stage.draw = draw;
875
   clipper->stage.name = "clipper";
876
   clipper->stage.point = clip_first_point;
877
   clipper->stage.line = clip_first_line;
878
   clipper->stage.tri = clip_first_tri;
879
   clipper->stage.flush = clip_flush;
880
   clipper->stage.reset_stipple_counter = clip_reset_stipple_counter;
881
   clipper->stage.destroy = clip_destroy;
882
 
883
   clipper->plane = draw->plane;
884
 
885
   if (!draw_alloc_temp_verts( &clipper->stage, MAX_CLIPPED_VERTICES+1 ))
886
      goto fail;
887
 
888
   return &clipper->stage;
889
 
890
 fail:
891
   if (clipper)
892
      clipper->stage.destroy( &clipper->stage );
893
 
894
   return NULL;
895
}