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 | }>>>>=>><>><>>>>>>>>>=>> |