1 /**************************************************************************
2 *
3 * Copyright 2007 Tungsten Graphics, Inc., Cedar Park, Texas.
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 TUNGSTEN GRAPHICS 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 <keith@tungstengraphics.com>
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
44
45 #ifndef IS_NEGATIVE
46 #define IS_NEGATIVE(X) ((X) < 0.0)
47 #endif
48
49 #ifndef DIFFERENT_SIGNS
50 #define DIFFERENT_SIGNS(x, y) ((x) * (y) <= 0.0F && (x) - (y) != 0.0F)
51 #endif
52
53 #define MAX_CLIPPED_VERTICES ((2 * (6 + PIPE_MAX_CLIP_PLANES))+1)
54
55
56
57 struct clip_stage {
58 struct draw_stage stage; /**< base class */
59
60 /* List of the attributes to be flatshaded. */
61 uint num_flat_attribs;
62 uint flat_attribs[PIPE_MAX_SHADER_OUTPUTS];
63
64 /* Mask of attributes in noperspective mode */
65 boolean noperspective_attribs[PIPE_MAX_SHADER_OUTPUTS];
66
67 float (*plane)[4];
68 };
69
70
71 /** Cast wrapper */
clip_stage(struct draw_stage * stage)72 static INLINE struct clip_stage *clip_stage( struct draw_stage *stage )
73 {
74 return (struct clip_stage *)stage;
75 }
76
77
78 #define LINTERP(T, OUT, IN) ((OUT) + (T) * ((IN) - (OUT)))
79
80
81 /* All attributes are float[4], so this is easy:
82 */
interp_attr(float dst[4],float t,const float in[4],const float out[4])83 static void interp_attr( float dst[4],
84 float t,
85 const float in[4],
86 const float out[4] )
87 {
88 dst[0] = LINTERP( t, out[0], in[0] );
89 dst[1] = LINTERP( t, out[1], in[1] );
90 dst[2] = LINTERP( t, out[2], in[2] );
91 dst[3] = LINTERP( t, out[3], in[3] );
92 }
93
94
95 /**
96 * Copy flat shaded attributes src vertex to dst vertex.
97 */
copy_flat(struct draw_stage * stage,struct vertex_header * dst,const struct vertex_header * src)98 static void copy_flat( struct draw_stage *stage,
99 struct vertex_header *dst,
100 const struct vertex_header *src )
101 {
102 const struct clip_stage *clipper = clip_stage(stage);
103 uint i;
104 for (i = 0; i < clipper->num_flat_attribs; i++) {
105 const uint attr = clipper->flat_attribs[i];
106 COPY_4FV(dst->data[attr], src->data[attr]);
107 }
108 }
109
110
111
112 /* Interpolate between two vertices to produce a third.
113 */
interp(const struct clip_stage * clip,struct vertex_header * dst,float t,const struct vertex_header * out,const struct vertex_header * in)114 static void interp( const struct clip_stage *clip,
115 struct vertex_header *dst,
116 float t,
117 const struct vertex_header *out,
118 const struct vertex_header *in )
119 {
120 const unsigned nr_attrs = draw_current_shader_outputs(clip->stage.draw);
121 const unsigned pos_attr = draw_current_shader_position_output(clip->stage.draw);
122 const unsigned clip_attr = draw_current_shader_clipvertex_output(clip->stage.draw);
123 unsigned j;
124 float t_nopersp;
125
126 /* Vertex header.
127 */
128 dst->clipmask = 0;
129 dst->edgeflag = 0; /* will get overwritten later */
130 dst->have_clipdist = in->have_clipdist;
131 dst->vertex_id = UNDEFINED_VERTEX_ID;
132
133 /* Interpolate the clip-space coords.
134 */
135 interp_attr(dst->clip, t, in->clip, out->clip);
136 /* interpolate the clip-space position */
137 interp_attr(dst->pre_clip_pos, t, in->pre_clip_pos, out->pre_clip_pos);
138
139 /* Do the projective divide and viewport transformation to get
140 * new window coordinates:
141 */
142 {
143 const float *pos = dst->pre_clip_pos;
144 const float *scale = clip->stage.draw->viewport.scale;
145 const float *trans = clip->stage.draw->viewport.translate;
146 const float oow = 1.0f / pos[3];
147
148 dst->data[pos_attr][0] = pos[0] * oow * scale[0] + trans[0];
149 dst->data[pos_attr][1] = pos[1] * oow * scale[1] + trans[1];
150 dst->data[pos_attr][2] = pos[2] * oow * scale[2] + trans[2];
151 dst->data[pos_attr][3] = oow;
152 }
153
154 /**
155 * Compute the t in screen-space instead of 3d space to use
156 * for noperspective interpolation.
157 *
158 * The points can be aligned with the X axis, so in that case try
159 * the Y. When both points are at the same screen position, we can
160 * pick whatever value (the interpolated point won't be in front
161 * anyway), so just use the 3d t.
162 */
163 {
164 int k;
165 t_nopersp = t;
166 for (k = 0; k < 2; k++)
167 if (in->data[pos_attr][k] != out->data[pos_attr][k]) {
168 t_nopersp = (dst->data[pos_attr][k] - out->data[pos_attr][k]) /
169 (in->data[pos_attr][k] - out->data[pos_attr][k]);
170 break;
171 }
172 }
173
174 /* Other attributes
175 */
176 for (j = 0; j < nr_attrs; j++) {
177 if (j != pos_attr && j != clip_attr) {
178 if (clip->noperspective_attribs[j])
179 interp_attr(dst->data[j], t_nopersp, in->data[j], out->data[j]);
180 else
181 interp_attr(dst->data[j], t, in->data[j], out->data[j]);
182 }
183 }
184 }
185
186
187 /**
188 * Emit a post-clip polygon to the next pipeline stage. The polygon
189 * will be convex and the provoking vertex will always be vertex[0].
190 */
emit_poly(struct draw_stage * stage,struct vertex_header ** inlist,const boolean * edgeflags,unsigned n,const struct prim_header * origPrim)191 static void emit_poly( struct draw_stage *stage,
192 struct vertex_header **inlist,
193 const boolean *edgeflags,
194 unsigned n,
195 const struct prim_header *origPrim)
196 {
197 struct prim_header header;
198 unsigned i;
199 ushort edge_first, edge_middle, edge_last;
200
201 if (stage->draw->rasterizer->flatshade_first) {
202 edge_first = DRAW_PIPE_EDGE_FLAG_0;
203 edge_middle = DRAW_PIPE_EDGE_FLAG_1;
204 edge_last = DRAW_PIPE_EDGE_FLAG_2;
205 }
206 else {
207 edge_first = DRAW_PIPE_EDGE_FLAG_2;
208 edge_middle = DRAW_PIPE_EDGE_FLAG_0;
209 edge_last = DRAW_PIPE_EDGE_FLAG_1;
210 }
211
212 if (!edgeflags[0])
213 edge_first = 0;
214
215 /* later stages may need the determinant, but only the sign matters */
216 header.det = origPrim->det;
217 header.flags = DRAW_PIPE_RESET_STIPPLE | edge_first | edge_middle;
218 header.pad = 0;
219
220 for (i = 2; i < n; i++, header.flags = edge_middle) {
221 /* order the triangle verts to respect the provoking vertex mode */
222 if (stage->draw->rasterizer->flatshade_first) {
223 header.v[0] = inlist[0]; /* the provoking vertex */
224 header.v[1] = inlist[i-1];
225 header.v[2] = inlist[i];
226 }
227 else {
228 header.v[0] = inlist[i-1];
229 header.v[1] = inlist[i];
230 header.v[2] = inlist[0]; /* the provoking vertex */
231 }
232
233 if (!edgeflags[i-1]) {
234 header.flags &= ~edge_middle;
235 }
236
237 if (i == n - 1 && edgeflags[i])
238 header.flags |= edge_last;
239
240 if (0) {
241 const struct draw_vertex_shader *vs = stage->draw->vs.vertex_shader;
242 uint j, k;
243 debug_printf("Clipped tri: (flat-shade-first = %d)\n",
244 stage->draw->rasterizer->flatshade_first);
245 for (j = 0; j < 3; j++) {
246 for (k = 0; k < vs->info.num_outputs; k++) {
247 debug_printf(" Vert %d: Attr %d: %f %f %f %f\n", j, k,
248 header.v[j]->data[k][0],
249 header.v[j]->data[k][1],
250 header.v[j]->data[k][2],
251 header.v[j]->data[k][3]);
252 }
253 }
254 }
255
256 stage->next->tri( stage->next, &header );
257 }
258 }
259
260
261 static INLINE float
dot4(const float * a,const float * b)262 dot4(const float *a, const float *b)
263 {
264 return (a[0] * b[0] +
265 a[1] * b[1] +
266 a[2] * b[2] +
267 a[3] * b[3]);
268 }
269
270 /*
271 * this function extracts the clip distance for the current plane,
272 * it first checks if the shader provided a clip distance, otherwise
273 * it works out the value using the clipvertex
274 */
getclipdist(const struct clip_stage * clipper,struct vertex_header * vert,int plane_idx)275 static INLINE float getclipdist(const struct clip_stage *clipper,
276 struct vertex_header *vert,
277 int plane_idx)
278 {
279 const float *plane;
280 float dp;
281 if (vert->have_clipdist && plane_idx >= 6) {
282 /* pick the correct clipdistance element from the output vectors */
283 int _idx = plane_idx - 6;
284 int cdi = _idx >= 4;
285 int vidx = cdi ? _idx - 4 : _idx;
286 dp = vert->data[draw_current_shader_clipdistance_output(clipper->stage.draw, cdi)][vidx];
287 } else {
288 plane = clipper->plane[plane_idx];
289 dp = dot4(vert->clip, plane);
290 }
291 return dp;
292 }
293
294 /* Clip a triangle against the viewport and user clip planes.
295 */
296 static void
do_clip_tri(struct draw_stage * stage,struct prim_header * header,unsigned clipmask)297 do_clip_tri( struct draw_stage *stage,
298 struct prim_header *header,
299 unsigned clipmask )
300 {
301 struct clip_stage *clipper = clip_stage( stage );
302 struct vertex_header *a[MAX_CLIPPED_VERTICES];
303 struct vertex_header *b[MAX_CLIPPED_VERTICES];
304 struct vertex_header **inlist = a;
305 struct vertex_header **outlist = b;
306 unsigned tmpnr = 0;
307 unsigned n = 3;
308 unsigned i;
309 boolean aEdges[MAX_CLIPPED_VERTICES];
310 boolean bEdges[MAX_CLIPPED_VERTICES];
311 boolean *inEdges = aEdges;
312 boolean *outEdges = bEdges;
313
314 inlist[0] = header->v[0];
315 inlist[1] = header->v[1];
316 inlist[2] = header->v[2];
317
318 /*
319 * Note: at this point we can't just use the per-vertex edge flags.
320 * We have to observe the edge flag bits set in header->flags which
321 * were set during primitive decomposition. Put those flags into
322 * an edge flags array which parallels the vertex array.
323 * Later, in the 'unfilled' pipeline stage we'll draw the edge if both
324 * the header.flags bit is set AND the per-vertex edgeflag field is set.
325 */
326 inEdges[0] = !!(header->flags & DRAW_PIPE_EDGE_FLAG_0);
327 inEdges[1] = !!(header->flags & DRAW_PIPE_EDGE_FLAG_1);
328 inEdges[2] = !!(header->flags & DRAW_PIPE_EDGE_FLAG_2);
329
330 while (clipmask && n >= 3) {
331 const unsigned plane_idx = ffs(clipmask)-1;
332 const boolean is_user_clip_plane = plane_idx >= 6;
333 struct vertex_header *vert_prev = inlist[0];
334 boolean *edge_prev = &inEdges[0];
335 float dp_prev;
336 unsigned outcount = 0;
337
338 dp_prev = getclipdist(clipper, vert_prev, plane_idx);
339 clipmask &= ~(1<<plane_idx);
340
341 assert(n < MAX_CLIPPED_VERTICES);
342 if (n >= MAX_CLIPPED_VERTICES)
343 return;
344 inlist[n] = inlist[0]; /* prevent rotation of vertices */
345 inEdges[n] = inEdges[0];
346
347 for (i = 1; i <= n; i++) {
348 struct vertex_header *vert = inlist[i];
349 boolean *edge = &inEdges[i];
350
351 float dp = getclipdist(clipper, vert, plane_idx);
352
353 if (!IS_NEGATIVE(dp_prev)) {
354 assert(outcount < MAX_CLIPPED_VERTICES);
355 if (outcount >= MAX_CLIPPED_VERTICES)
356 return;
357 outEdges[outcount] = *edge_prev;
358 outlist[outcount++] = vert_prev;
359 }
360
361 if (DIFFERENT_SIGNS(dp, dp_prev)) {
362 struct vertex_header *new_vert;
363 boolean *new_edge;
364
365 assert(tmpnr < MAX_CLIPPED_VERTICES + 1);
366 if (tmpnr >= MAX_CLIPPED_VERTICES + 1)
367 return;
368 new_vert = clipper->stage.tmp[tmpnr++];
369
370 assert(outcount < MAX_CLIPPED_VERTICES);
371 if (outcount >= MAX_CLIPPED_VERTICES)
372 return;
373
374 new_edge = &outEdges[outcount];
375 outlist[outcount++] = new_vert;
376
377 if (IS_NEGATIVE(dp)) {
378 /* Going out of bounds. Avoid division by zero as we
379 * know dp != dp_prev from DIFFERENT_SIGNS, above.
380 */
381 float t = dp / (dp - dp_prev);
382 interp( clipper, new_vert, t, vert, vert_prev );
383
384 /* Whether or not to set edge flag for the new vert depends
385 * on whether it's a user-defined clipping plane. We're
386 * copying NVIDIA's behaviour here.
387 */
388 if (is_user_clip_plane) {
389 /* we want to see an edge along the clip plane */
390 *new_edge = TRUE;
391 new_vert->edgeflag = TRUE;
392 }
393 else {
394 /* we don't want to see an edge along the frustum clip plane */
395 *new_edge = *edge_prev;
396 new_vert->edgeflag = FALSE;
397 }
398 }
399 else {
400 /* Coming back in.
401 */
402 float t = dp_prev / (dp_prev - dp);
403 interp( clipper, new_vert, t, vert_prev, vert );
404
405 /* Copy starting vert's edgeflag:
406 */
407 new_vert->edgeflag = vert_prev->edgeflag;
408 *new_edge = *edge_prev;
409 }
410 }
411
412 vert_prev = vert;
413 edge_prev = edge;
414 dp_prev = dp;
415 }
416
417 /* swap in/out lists */
418 {
419 struct vertex_header **tmp = inlist;
420 inlist = outlist;
421 outlist = tmp;
422 n = outcount;
423 }
424 {
425 boolean *tmp = inEdges;
426 inEdges = outEdges;
427 outEdges = tmp;
428 }
429
430 }
431
432 /* If flat-shading, copy provoking vertex color to polygon vertex[0]
433 */
434 if (n >= 3) {
435 if (clipper->num_flat_attribs) {
436 if (stage->draw->rasterizer->flatshade_first) {
437 if (inlist[0] != header->v[0]) {
438 assert(tmpnr < MAX_CLIPPED_VERTICES + 1);
439 if (tmpnr >= MAX_CLIPPED_VERTICES + 1)
440 return;
441 inlist[0] = dup_vert(stage, inlist[0], tmpnr++);
442 copy_flat(stage, inlist[0], header->v[0]);
443 }
444 }
445 else {
446 if (inlist[0] != header->v[2]) {
447 assert(tmpnr < MAX_CLIPPED_VERTICES + 1);
448 if (tmpnr >= MAX_CLIPPED_VERTICES + 1)
449 return;
450 inlist[0] = dup_vert(stage, inlist[0], tmpnr++);
451 copy_flat(stage, inlist[0], header->v[2]);
452 }
453 }
454 }
455
456 /* Emit the polygon as triangles to the setup stage:
457 */
458 emit_poly( stage, inlist, inEdges, n, header );
459 }
460 }
461
462
463 /* Clip a line against the viewport and user clip planes.
464 */
465 static void
do_clip_line(struct draw_stage * stage,struct prim_header * header,unsigned clipmask)466 do_clip_line( struct draw_stage *stage,
467 struct prim_header *header,
468 unsigned clipmask )
469 {
470 const struct clip_stage *clipper = clip_stage( stage );
471 struct vertex_header *v0 = header->v[0];
472 struct vertex_header *v1 = header->v[1];
473 float t0 = 0.0F;
474 float t1 = 0.0F;
475 struct prim_header newprim;
476
477 while (clipmask) {
478 const unsigned plane_idx = ffs(clipmask)-1;
479 const float dp0 = getclipdist(clipper, v0, plane_idx);
480 const float dp1 = getclipdist(clipper, v1, plane_idx);
481
482 if (dp1 < 0.0F) {
483 float t = dp1 / (dp1 - dp0);
484 t1 = MAX2(t1, t);
485 }
486
487 if (dp0 < 0.0F) {
488 float t = dp0 / (dp0 - dp1);
489 t0 = MAX2(t0, t);
490 }
491
492 if (t0 + t1 >= 1.0F)
493 return; /* discard */
494
495 clipmask &= ~(1 << plane_idx); /* turn off this plane's bit */
496 }
497
498 if (v0->clipmask) {
499 interp( clipper, stage->tmp[0], t0, v0, v1 );
500 copy_flat(stage, stage->tmp[0], v0);
501 newprim.v[0] = stage->tmp[0];
502 }
503 else {
504 newprim.v[0] = v0;
505 }
506
507 if (v1->clipmask) {
508 interp( clipper, stage->tmp[1], t1, v1, v0 );
509 newprim.v[1] = stage->tmp[1];
510 }
511 else {
512 newprim.v[1] = v1;
513 }
514
515 stage->next->line( stage->next, &newprim );
516 }
517
518
519 static void
clip_point(struct draw_stage * stage,struct prim_header * header)520 clip_point( struct draw_stage *stage,
521 struct prim_header *header )
522 {
523 if (header->v[0]->clipmask == 0)
524 stage->next->point( stage->next, header );
525 }
526
527
528 static void
clip_line(struct draw_stage * stage,struct prim_header * header)529 clip_line( struct draw_stage *stage,
530 struct prim_header *header )
531 {
532 unsigned clipmask = (header->v[0]->clipmask |
533 header->v[1]->clipmask);
534
535 if (clipmask == 0) {
536 /* no clipping needed */
537 stage->next->line( stage->next, header );
538 }
539 else if ((header->v[0]->clipmask &
540 header->v[1]->clipmask) == 0) {
541 do_clip_line(stage, header, clipmask);
542 }
543 /* else, totally clipped */
544 }
545
546
547 static void
clip_tri(struct draw_stage * stage,struct prim_header * header)548 clip_tri( struct draw_stage *stage,
549 struct prim_header *header )
550 {
551 unsigned clipmask = (header->v[0]->clipmask |
552 header->v[1]->clipmask |
553 header->v[2]->clipmask);
554
555 if (clipmask == 0) {
556 /* no clipping needed */
557 stage->next->tri( stage->next, header );
558 }
559 else if ((header->v[0]->clipmask &
560 header->v[1]->clipmask &
561 header->v[2]->clipmask) == 0) {
562 do_clip_tri(stage, header, clipmask);
563 }
564 }
565
566
567 /* Update state. Could further delay this until we hit the first
568 * primitive that really requires clipping.
569 */
570 static void
clip_init_state(struct draw_stage * stage)571 clip_init_state( struct draw_stage *stage )
572 {
573 struct clip_stage *clipper = clip_stage( stage );
574 const struct draw_vertex_shader *vs = stage->draw->vs.vertex_shader;
575 const struct draw_fragment_shader *fs = stage->draw->fs.fragment_shader;
576 uint i;
577
578 /* We need to know for each attribute what kind of interpolation is
579 * done on it (flat, smooth or noperspective). But the information
580 * is not directly accessible for outputs, only for inputs. So we
581 * have to match semantic name and index between the VS (or GS/ES)
582 * outputs and the FS inputs to get to the interpolation mode.
583 *
584 * The only hitch is with gl_FrontColor/gl_BackColor which map to
585 * gl_Color, and their Secondary versions. First there are (up to)
586 * two outputs for one input, so we tuck the information in a
587 * specific array. Second if they don't have qualifiers, the
588 * default value has to be picked from the global shade mode.
589 *
590 * Of course, if we don't have a fragment shader in the first
591 * place, defaults should be used.
592 */
593
594 /* First pick up the interpolation mode for
595 * gl_Color/gl_SecondaryColor, with the correct default.
596 */
597 int indexed_interp[2];
598 indexed_interp[0] = indexed_interp[1] = stage->draw->rasterizer->flatshade ?
599 TGSI_INTERPOLATE_CONSTANT : TGSI_INTERPOLATE_PERSPECTIVE;
600
601 if (fs) {
602 for (i = 0; i < fs->info.num_inputs; i++) {
603 if (fs->info.input_semantic_name[i] == TGSI_SEMANTIC_COLOR) {
604 if (fs->info.input_interpolate[i] != TGSI_INTERPOLATE_COLOR)
605 indexed_interp[fs->info.input_semantic_index[i]] = fs->info.input_interpolate[i];
606 }
607 }
608 }
609
610 /* Then resolve the interpolation mode for every output attribute.
611 *
612 * Given how the rest of the code, the most efficient way is to
613 * have a vector of flat-mode attributes, and a mask for
614 * noperspective attributes.
615 */
616
617 clipper->num_flat_attribs = 0;
618 memset(clipper->noperspective_attribs, 0, sizeof(clipper->noperspective_attribs));
619 for (i = 0; i < vs->info.num_outputs; i++) {
620 /* Find the interpolation mode for a specific attribute
621 */
622 int interp;
623
624 /* If it's gl_{Front,Back}{,Secondary}Color, pick up the mode
625 * from the array we've filled before. */
626 if (vs->info.output_semantic_name[i] == TGSI_SEMANTIC_COLOR ||
627 vs->info.output_semantic_name[i] == TGSI_SEMANTIC_BCOLOR) {
628 interp = indexed_interp[vs->info.output_semantic_index[i]];
629 } else {
630 /* Otherwise, search in the FS inputs, with a decent default
631 * if we don't find it.
632 */
633 uint j;
634 interp = TGSI_INTERPOLATE_PERSPECTIVE;
635 if (fs) {
636 for (j = 0; j < fs->info.num_inputs; j++) {
637 if (vs->info.output_semantic_name[i] == fs->info.input_semantic_name[j] &&
638 vs->info.output_semantic_index[i] == fs->info.input_semantic_index[j]) {
639 interp = fs->info.input_interpolate[j];
640 break;
641 }
642 }
643 }
644 }
645
646 /* If it's flat, add it to the flat vector. Otherwise update
647 * the noperspective mask.
648 */
649 if (interp == TGSI_INTERPOLATE_CONSTANT) {
650 clipper->flat_attribs[clipper->num_flat_attribs] = i;
651 clipper->num_flat_attribs++;
652 } else
653 clipper->noperspective_attribs[i] = interp == TGSI_INTERPOLATE_LINEAR;
654 }
655
656 stage->tri = clip_tri;
657 stage->line = clip_line;
658 }
659
660
661
clip_first_tri(struct draw_stage * stage,struct prim_header * header)662 static void clip_first_tri( struct draw_stage *stage,
663 struct prim_header *header )
664 {
665 clip_init_state( stage );
666 stage->tri( stage, header );
667 }
668
clip_first_line(struct draw_stage * stage,struct prim_header * header)669 static void clip_first_line( struct draw_stage *stage,
670 struct prim_header *header )
671 {
672 clip_init_state( stage );
673 stage->line( stage, header );
674 }
675
676
clip_flush(struct draw_stage * stage,unsigned flags)677 static void clip_flush( struct draw_stage *stage,
678 unsigned flags )
679 {
680 stage->tri = clip_first_tri;
681 stage->line = clip_first_line;
682 stage->next->flush( stage->next, flags );
683 }
684
685
clip_reset_stipple_counter(struct draw_stage * stage)686 static void clip_reset_stipple_counter( struct draw_stage *stage )
687 {
688 stage->next->reset_stipple_counter( stage->next );
689 }
690
691
clip_destroy(struct draw_stage * stage)692 static void clip_destroy( struct draw_stage *stage )
693 {
694 draw_free_temp_verts( stage );
695 FREE( stage );
696 }
697
698
699 /**
700 * Allocate a new clipper stage.
701 * \return pointer to new stage object
702 */
draw_clip_stage(struct draw_context * draw)703 struct draw_stage *draw_clip_stage( struct draw_context *draw )
704 {
705 struct clip_stage *clipper = CALLOC_STRUCT(clip_stage);
706 if (clipper == NULL)
707 goto fail;
708
709 clipper->stage.draw = draw;
710 clipper->stage.name = "clipper";
711 clipper->stage.point = clip_point;
712 clipper->stage.line = clip_first_line;
713 clipper->stage.tri = clip_first_tri;
714 clipper->stage.flush = clip_flush;
715 clipper->stage.reset_stipple_counter = clip_reset_stipple_counter;
716 clipper->stage.destroy = clip_destroy;
717
718 clipper->plane = draw->plane;
719
720 if (!draw_alloc_temp_verts( &clipper->stage, MAX_CLIPPED_VERTICES+1 ))
721 goto fail;
722
723 return &clipper->stage;
724
725 fail:
726 if (clipper)
727 clipper->stage.destroy( &clipper->stage );
728
729 return NULL;
730 }
731