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 57struct 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 */ 72static 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 */ 83static 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 */ 98static 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 */ 114static 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 */ 191static 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 261static INLINE float 262dot4(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 */ 275static 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 */ 296static void 297do_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 */ 465static void 466do_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 519static void 520clip_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 528static void 529clip_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 547static void 548clip_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 */ 570static void 571clip_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 662static 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 669static 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 677static 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 686static void clip_reset_stipple_counter( struct draw_stage *stage ) 687{ 688 stage->next->reset_stipple_counter( stage->next ); 689} 690 691 692static 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 */ 703struct 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