1 /**************************************************************************
3 * Copyright 2007 Tungsten Graphics, Inc., Cedar Park, Texas.
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:
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
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.
26 **************************************************************************/
29 * \brief Clipping stage
31 * \author Keith Whitwell <keith@tungstengraphics.com>
35 #include "util/u_memory.h"
36 #include "util/u_math.h"
38 #include "pipe/p_shader_tokens.h"
41 #include "draw_pipe.h"
45 #define IS_NEGATIVE(X) ((X) < 0.0)
48 #ifndef DIFFERENT_SIGNS
49 #define DIFFERENT_SIGNS(x, y) ((x) * (y) <= 0.0F && (x) - (y) != 0.0F)
52 #define MAX_CLIPPED_VERTICES ((2 * (6 + PIPE_MAX_CLIP_PLANES))+1)
57 struct draw_stage stage
; /**< base class */
59 /* Basically duplicate some of the flatshading logic here:
62 uint num_color_attribs
;
63 uint color_attribs
[4]; /* front/back primary/secondary colors */
70 static INLINE
struct clip_stage
*clip_stage( struct draw_stage
*stage
)
72 return (struct clip_stage
*)stage
;
76 #define LINTERP(T, OUT, IN) ((OUT) + (T) * ((IN) - (OUT)))
79 /* All attributes are float[4], so this is easy:
81 static void interp_attr( float dst
[4],
86 dst
[0] = LINTERP( t
, out
[0], in
[0] );
87 dst
[1] = LINTERP( t
, out
[1], in
[1] );
88 dst
[2] = LINTERP( t
, out
[2], in
[2] );
89 dst
[3] = LINTERP( t
, out
[3], in
[3] );
94 * Copy front/back, primary/secondary colors from src vertex to dst vertex.
95 * Used when flat shading.
97 static void copy_colors( struct draw_stage
*stage
,
98 struct vertex_header
*dst
,
99 const struct vertex_header
*src
)
101 const struct clip_stage
*clipper
= clip_stage(stage
);
103 for (i
= 0; i
< clipper
->num_color_attribs
; i
++) {
104 const uint attr
= clipper
->color_attribs
[i
];
105 COPY_4FV(dst
->data
[attr
], src
->data
[attr
]);
111 /* Interpolate between two vertices to produce a third.
113 static void interp( const struct clip_stage
*clip
,
114 struct vertex_header
*dst
,
116 const struct vertex_header
*out
,
117 const struct vertex_header
*in
)
119 const unsigned nr_attrs
= draw_current_shader_outputs(clip
->stage
.draw
);
120 const unsigned pos_attr
= draw_current_shader_position_output(clip
->stage
.draw
);
121 const unsigned clip_attr
= draw_current_shader_clipvertex_output(clip
->stage
.draw
);
122 unsigned clip_dist
[2];
125 clip_dist
[0] = draw_current_shader_clipdistance_output(clip
->stage
.draw
, 0);
126 clip_dist
[1] = draw_current_shader_clipdistance_output(clip
->stage
.draw
, 1);
131 dst
->edgeflag
= 0; /* will get overwritten later */
132 dst
->have_clipdist
= in
->have_clipdist
;
133 dst
->vertex_id
= UNDEFINED_VERTEX_ID
;
135 /* Interpolate the clip-space coords.
137 interp_attr(dst
->clip
, t
, in
->clip
, out
->clip
);
138 /* interpolate the clip-space position */
139 interp_attr(dst
->pre_clip_pos
, t
, in
->pre_clip_pos
, out
->pre_clip_pos
);
141 /* Do the projective divide and viewport transformation to get
142 * new window coordinates:
145 const float *pos
= dst
->pre_clip_pos
;
146 const float *scale
= clip
->stage
.draw
->viewport
.scale
;
147 const float *trans
= clip
->stage
.draw
->viewport
.translate
;
148 const float oow
= 1.0f
/ pos
[3];
150 dst
->data
[pos_attr
][0] = pos
[0] * oow
* scale
[0] + trans
[0];
151 dst
->data
[pos_attr
][1] = pos
[1] * oow
* scale
[1] + trans
[1];
152 dst
->data
[pos_attr
][2] = pos
[2] * oow
* scale
[2] + trans
[2];
153 dst
->data
[pos_attr
][3] = oow
;
158 for (j
= 0; j
< nr_attrs
; j
++) {
159 if (j
!= pos_attr
&& j
!= clip_attr
)
160 interp_attr(dst
->data
[j
], t
, in
->data
[j
], out
->data
[j
]);
166 * Emit a post-clip polygon to the next pipeline stage. The polygon
167 * will be convex and the provoking vertex will always be vertex[0].
169 static void emit_poly( struct draw_stage
*stage
,
170 struct vertex_header
**inlist
,
171 const boolean
*edgeflags
,
173 const struct prim_header
*origPrim
)
175 struct prim_header header
;
177 ushort edge_first
, edge_middle
, edge_last
;
179 if (stage
->draw
->rasterizer
->flatshade_first
) {
180 edge_first
= DRAW_PIPE_EDGE_FLAG_0
;
181 edge_middle
= DRAW_PIPE_EDGE_FLAG_1
;
182 edge_last
= DRAW_PIPE_EDGE_FLAG_2
;
185 edge_first
= DRAW_PIPE_EDGE_FLAG_2
;
186 edge_middle
= DRAW_PIPE_EDGE_FLAG_0
;
187 edge_last
= DRAW_PIPE_EDGE_FLAG_1
;
193 /* later stages may need the determinant, but only the sign matters */
194 header
.det
= origPrim
->det
;
195 header
.flags
= DRAW_PIPE_RESET_STIPPLE
| edge_first
| edge_middle
;
198 for (i
= 2; i
< n
; i
++, header
.flags
= edge_middle
) {
199 /* order the triangle verts to respect the provoking vertex mode */
200 if (stage
->draw
->rasterizer
->flatshade_first
) {
201 header
.v
[0] = inlist
[0]; /* the provoking vertex */
202 header
.v
[1] = inlist
[i
-1];
203 header
.v
[2] = inlist
[i
];
206 header
.v
[0] = inlist
[i
-1];
207 header
.v
[1] = inlist
[i
];
208 header
.v
[2] = inlist
[0]; /* the provoking vertex */
211 if (!edgeflags
[i
-1]) {
212 header
.flags
&= ~edge_middle
;
215 if (i
== n
- 1 && edgeflags
[i
])
216 header
.flags
|= edge_last
;
219 const struct draw_vertex_shader
*vs
= stage
->draw
->vs
.vertex_shader
;
221 debug_printf("Clipped tri: (flat-shade-first = %d)\n",
222 stage
->draw
->rasterizer
->flatshade_first
);
223 for (j
= 0; j
< 3; j
++) {
224 for (k
= 0; k
< vs
->info
.num_outputs
; k
++) {
225 debug_printf(" Vert %d: Attr %d: %f %f %f %f\n", j
, k
,
226 header
.v
[j
]->data
[k
][0],
227 header
.v
[j
]->data
[k
][1],
228 header
.v
[j
]->data
[k
][2],
229 header
.v
[j
]->data
[k
][3]);
234 stage
->next
->tri( stage
->next
, &header
);
240 dot4(const float *a
, const float *b
)
242 return (a
[0] * b
[0] +
249 * this function extracts the clip distance for the current plane,
250 * it first checks if the shader provided a clip distance, otherwise
251 * it works out the value using the clipvertex
253 static INLINE
float getclipdist(const struct clip_stage
*clipper
,
254 struct vertex_header
*vert
,
259 if (vert
->have_clipdist
&& plane_idx
>= 6) {
260 /* pick the correct clipdistance element from the output vectors */
261 int _idx
= plane_idx
- 6;
263 int vidx
= cdi
? _idx
- 4 : _idx
;
264 dp
= vert
->data
[draw_current_shader_clipdistance_output(clipper
->stage
.draw
, cdi
)][vidx
];
266 plane
= clipper
->plane
[plane_idx
];
267 dp
= dot4(vert
->clip
, plane
);
272 /* Clip a triangle against the viewport and user clip planes.
275 do_clip_tri( struct draw_stage
*stage
,
276 struct prim_header
*header
,
279 struct clip_stage
*clipper
= clip_stage( stage
);
280 struct vertex_header
*a
[MAX_CLIPPED_VERTICES
];
281 struct vertex_header
*b
[MAX_CLIPPED_VERTICES
];
282 struct vertex_header
**inlist
= a
;
283 struct vertex_header
**outlist
= b
;
287 boolean aEdges
[MAX_CLIPPED_VERTICES
];
288 boolean bEdges
[MAX_CLIPPED_VERTICES
];
289 boolean
*inEdges
= aEdges
;
290 boolean
*outEdges
= bEdges
;
292 inlist
[0] = header
->v
[0];
293 inlist
[1] = header
->v
[1];
294 inlist
[2] = header
->v
[2];
297 * Note: at this point we can't just use the per-vertex edge flags.
298 * We have to observe the edge flag bits set in header->flags which
299 * were set during primitive decomposition. Put those flags into
300 * an edge flags array which parallels the vertex array.
301 * Later, in the 'unfilled' pipeline stage we'll draw the edge if both
302 * the header.flags bit is set AND the per-vertex edgeflag field is set.
304 inEdges
[0] = !!(header
->flags
& DRAW_PIPE_EDGE_FLAG_0
);
305 inEdges
[1] = !!(header
->flags
& DRAW_PIPE_EDGE_FLAG_1
);
306 inEdges
[2] = !!(header
->flags
& DRAW_PIPE_EDGE_FLAG_2
);
308 while (clipmask
&& n
>= 3) {
309 const unsigned plane_idx
= ffs(clipmask
)-1;
310 const boolean is_user_clip_plane
= plane_idx
>= 6;
311 struct vertex_header
*vert_prev
= inlist
[0];
312 boolean
*edge_prev
= &inEdges
[0];
314 unsigned outcount
= 0;
316 dp_prev
= getclipdist(clipper
, vert_prev
, plane_idx
);
317 clipmask
&= ~(1<<plane_idx
);
319 assert(n
< MAX_CLIPPED_VERTICES
);
320 if (n
>= MAX_CLIPPED_VERTICES
)
322 inlist
[n
] = inlist
[0]; /* prevent rotation of vertices */
323 inEdges
[n
] = inEdges
[0];
325 for (i
= 1; i
<= n
; i
++) {
326 struct vertex_header
*vert
= inlist
[i
];
327 boolean
*edge
= &inEdges
[i
];
329 float dp
= getclipdist(clipper
, vert
, plane_idx
);
331 if (!IS_NEGATIVE(dp_prev
)) {
332 assert(outcount
< MAX_CLIPPED_VERTICES
);
333 if (outcount
>= MAX_CLIPPED_VERTICES
)
335 outEdges
[outcount
] = *edge_prev
;
336 outlist
[outcount
++] = vert_prev
;
339 if (DIFFERENT_SIGNS(dp
, dp_prev
)) {
340 struct vertex_header
*new_vert
;
343 assert(tmpnr
< MAX_CLIPPED_VERTICES
+ 1);
344 if (tmpnr
>= MAX_CLIPPED_VERTICES
+ 1)
346 new_vert
= clipper
->stage
.tmp
[tmpnr
++];
348 assert(outcount
< MAX_CLIPPED_VERTICES
);
349 if (outcount
>= MAX_CLIPPED_VERTICES
)
352 new_edge
= &outEdges
[outcount
];
353 outlist
[outcount
++] = new_vert
;
355 if (IS_NEGATIVE(dp
)) {
356 /* Going out of bounds. Avoid division by zero as we
357 * know dp != dp_prev from DIFFERENT_SIGNS, above.
359 float t
= dp
/ (dp
- dp_prev
);
360 interp( clipper
, new_vert
, t
, vert
, vert_prev
);
362 /* Whether or not to set edge flag for the new vert depends
363 * on whether it's a user-defined clipping plane. We're
364 * copying NVIDIA's behaviour here.
366 if (is_user_clip_plane
) {
367 /* we want to see an edge along the clip plane */
369 new_vert
->edgeflag
= TRUE
;
372 /* we don't want to see an edge along the frustum clip plane */
373 *new_edge
= *edge_prev
;
374 new_vert
->edgeflag
= FALSE
;
380 float t
= dp_prev
/ (dp_prev
- dp
);
381 interp( clipper
, new_vert
, t
, vert_prev
, vert
);
383 /* Copy starting vert's edgeflag:
385 new_vert
->edgeflag
= vert_prev
->edgeflag
;
386 *new_edge
= *edge_prev
;
395 /* swap in/out lists */
397 struct vertex_header
**tmp
= inlist
;
403 boolean
*tmp
= inEdges
;
410 /* If flat-shading, copy provoking vertex color to polygon vertex[0]
414 if (stage
->draw
->rasterizer
->flatshade_first
) {
415 if (inlist
[0] != header
->v
[0]) {
416 assert(tmpnr
< MAX_CLIPPED_VERTICES
+ 1);
417 if (tmpnr
>= MAX_CLIPPED_VERTICES
+ 1)
419 inlist
[0] = dup_vert(stage
, inlist
[0], tmpnr
++);
420 copy_colors(stage
, inlist
[0], header
->v
[0]);
424 if (inlist
[0] != header
->v
[2]) {
425 assert(tmpnr
< MAX_CLIPPED_VERTICES
+ 1);
426 if (tmpnr
>= MAX_CLIPPED_VERTICES
+ 1)
428 inlist
[0] = dup_vert(stage
, inlist
[0], tmpnr
++);
429 copy_colors(stage
, inlist
[0], header
->v
[2]);
434 /* Emit the polygon as triangles to the setup stage:
436 emit_poly( stage
, inlist
, inEdges
, n
, header
);
441 /* Clip a line against the viewport and user clip planes.
444 do_clip_line( struct draw_stage
*stage
,
445 struct prim_header
*header
,
448 const struct clip_stage
*clipper
= clip_stage( stage
);
449 struct vertex_header
*v0
= header
->v
[0];
450 struct vertex_header
*v1
= header
->v
[1];
453 struct prim_header newprim
;
456 const unsigned plane_idx
= ffs(clipmask
)-1;
457 const float dp0
= getclipdist(clipper
, v0
, plane_idx
);
458 const float dp1
= getclipdist(clipper
, v1
, plane_idx
);
461 float t
= dp1
/ (dp1
- dp0
);
466 float t
= dp0
/ (dp0
- dp1
);
471 return; /* discard */
473 clipmask
&= ~(1 << plane_idx
); /* turn off this plane's bit */
477 interp( clipper
, stage
->tmp
[0], t0
, v0
, v1
);
480 copy_colors(stage
, stage
->tmp
[0], v0
);
482 newprim
.v
[0] = stage
->tmp
[0];
489 interp( clipper
, stage
->tmp
[1], t1
, v1
, v0
);
490 newprim
.v
[1] = stage
->tmp
[1];
496 stage
->next
->line( stage
->next
, &newprim
);
501 clip_point( struct draw_stage
*stage
,
502 struct prim_header
*header
)
504 if (header
->v
[0]->clipmask
== 0)
505 stage
->next
->point( stage
->next
, header
);
510 clip_line( struct draw_stage
*stage
,
511 struct prim_header
*header
)
513 unsigned clipmask
= (header
->v
[0]->clipmask
|
514 header
->v
[1]->clipmask
);
517 /* no clipping needed */
518 stage
->next
->line( stage
->next
, header
);
520 else if ((header
->v
[0]->clipmask
&
521 header
->v
[1]->clipmask
) == 0) {
522 do_clip_line(stage
, header
, clipmask
);
524 /* else, totally clipped */
529 clip_tri( struct draw_stage
*stage
,
530 struct prim_header
*header
)
532 unsigned clipmask
= (header
->v
[0]->clipmask
|
533 header
->v
[1]->clipmask
|
534 header
->v
[2]->clipmask
);
537 /* no clipping needed */
538 stage
->next
->tri( stage
->next
, header
);
540 else if ((header
->v
[0]->clipmask
&
541 header
->v
[1]->clipmask
&
542 header
->v
[2]->clipmask
) == 0) {
543 do_clip_tri(stage
, header
, clipmask
);
548 /* Update state. Could further delay this until we hit the first
549 * primitive that really requires clipping.
552 clip_init_state( struct draw_stage
*stage
)
554 struct clip_stage
*clipper
= clip_stage( stage
);
556 clipper
->flat
= stage
->draw
->rasterizer
->flatshade
? TRUE
: FALSE
;
559 const struct draw_vertex_shader
*vs
= stage
->draw
->vs
.vertex_shader
;
562 clipper
->num_color_attribs
= 0;
563 for (i
= 0; i
< vs
->info
.num_outputs
; i
++) {
564 if (vs
->info
.output_semantic_name
[i
] == TGSI_SEMANTIC_COLOR
||
565 vs
->info
.output_semantic_name
[i
] == TGSI_SEMANTIC_BCOLOR
) {
566 clipper
->color_attribs
[clipper
->num_color_attribs
++] = i
;
571 stage
->tri
= clip_tri
;
572 stage
->line
= clip_line
;
577 static void clip_first_tri( struct draw_stage
*stage
,
578 struct prim_header
*header
)
580 clip_init_state( stage
);
581 stage
->tri( stage
, header
);
584 static void clip_first_line( struct draw_stage
*stage
,
585 struct prim_header
*header
)
587 clip_init_state( stage
);
588 stage
->line( stage
, header
);
592 static void clip_flush( struct draw_stage
*stage
,
595 stage
->tri
= clip_first_tri
;
596 stage
->line
= clip_first_line
;
597 stage
->next
->flush( stage
->next
, flags
);
601 static void clip_reset_stipple_counter( struct draw_stage
*stage
)
603 stage
->next
->reset_stipple_counter( stage
->next
);
607 static void clip_destroy( struct draw_stage
*stage
)
609 draw_free_temp_verts( stage
);
615 * Allocate a new clipper stage.
616 * \return pointer to new stage object
618 struct draw_stage
*draw_clip_stage( struct draw_context
*draw
)
620 struct clip_stage
*clipper
= CALLOC_STRUCT(clip_stage
);
624 clipper
->stage
.draw
= draw
;
625 clipper
->stage
.name
= "clipper";
626 clipper
->stage
.point
= clip_point
;
627 clipper
->stage
.line
= clip_first_line
;
628 clipper
->stage
.tri
= clip_first_tri
;
629 clipper
->stage
.flush
= clip_flush
;
630 clipper
->stage
.reset_stipple_counter
= clip_reset_stipple_counter
;
631 clipper
->stage
.destroy
= clip_destroy
;
633 clipper
->plane
= draw
->plane
;
635 if (!draw_alloc_temp_verts( &clipper
->stage
, MAX_CLIPPED_VERTICES
+1 ))
638 return &clipper
->stage
;
642 clipper
->stage
.destroy( &clipper
->stage
);