/* Clip a line against the viewport and user clip planes. */ static void do_clip_line( struct draw_stage *stage, struct prim_header *header, unsigned clipmask ) { const struct clip_stage *clipper = clip_stage( stage ); struct vertex_header *v0 = header->v[0]; struct vertex_header *v1 = header->v[1]; const float *pos0 = v0->clip; const float *pos1 = v1->clip; float t0 = 0.0F; float t1 = 0.0F; struct prim_header newprim; while (clipmask) { const unsigned plane_idx = ffs(clipmask)-1; const float *plane = clipper->plane[plane_idx]; const float dp0 = dot4( pos0, plane ); const float dp1 = dot4( pos1, plane ); if (dp1 < 0.0F) { float t = dp1 / (dp1 - dp0); t1 = MAX2(t1, t); } if (dp0 < 0.0F) { float t = dp0 / (dp0 - dp1); t0 = MAX2(t0, t); } if (t0 + t1 >= 1.0F) return; /* discard */ clipmask &= ~(1 << plane_idx); /* turn off this plane's bit */ } if (v0->clipmask) { interp( clipper, stage->tmp[0], t0, v0, v1 ); if (clipper->flat) copy_colors(stage, stage->tmp[0], v0); newprim.v[0] = stage->tmp[0]; } else { newprim.v[0] = v0; } if (v1->clipmask) { interp( clipper, stage->tmp[1], t1, v1, v0 ); newprim.v[1] = stage->tmp[1]; } else { newprim.v[1] = v1; } stage->next->line( stage->next, &newprim ); }
/** * Copy front/back, primary/secondary colors from src vertex to dst vertex. * Used when flat shading. */ static void copy_colors( struct draw_stage *stage, struct vertex_header *dst, const struct vertex_header *src ) { const struct clip_stage *clipper = clip_stage(stage); uint i; for (i = 0; i < clipper->num_color_attribs; i++) { const uint attr = clipper->color_attribs[i]; COPY_4FV(dst->data[attr], src->data[attr]); } }
/* Update state. Could further delay this until we hit the first * primitive that really requires clipping. */ static void clip_init_state( struct draw_stage *stage ) { struct clip_stage *clipper = clip_stage( stage ); clipper->flat = stage->draw->rasterizer->flatshade ? TRUE : FALSE; if (clipper->flat) { const struct draw_vertex_shader *vs = stage->draw->vs.vertex_shader; uint i; clipper->num_color_attribs = 0; for (i = 0; i < vs->info.num_outputs; i++) { if (vs->info.output_semantic_name[i] == TGSI_SEMANTIC_COLOR || vs->info.output_semantic_name[i] == TGSI_SEMANTIC_BCOLOR) { clipper->color_attribs[clipper->num_color_attribs++] = i; } } } stage->tri = clip_tri; stage->line = clip_line; }
/* Clip a triangle against the viewport and user clip planes. */ static void do_clip_tri( struct draw_stage *stage, struct prim_header *header, unsigned clipmask ) { struct clip_stage *clipper = clip_stage( stage ); struct vertex_header *a[MAX_CLIPPED_VERTICES]; struct vertex_header *b[MAX_CLIPPED_VERTICES]; struct vertex_header **inlist = a; struct vertex_header **outlist = b; unsigned tmpnr = 0; unsigned n = 3; unsigned i; boolean aEdges[MAX_CLIPPED_VERTICES]; boolean bEdges[MAX_CLIPPED_VERTICES]; boolean *inEdges = aEdges; boolean *outEdges = bEdges; inlist[0] = header->v[0]; inlist[1] = header->v[1]; inlist[2] = header->v[2]; /* * Note: at this point we can't just use the per-vertex edge flags. * We have to observe the edge flag bits set in header->flags which * were set during primitive decomposition. Put those flags into * an edge flags array which parallels the vertex array. * Later, in the 'unfilled' pipeline stage we'll draw the edge if both * the header.flags bit is set AND the per-vertex edgeflag field is set. */ inEdges[0] = !!(header->flags & DRAW_PIPE_EDGE_FLAG_0); inEdges[1] = !!(header->flags & DRAW_PIPE_EDGE_FLAG_1); inEdges[2] = !!(header->flags & DRAW_PIPE_EDGE_FLAG_2); while (clipmask && n >= 3) { const unsigned plane_idx = ffs(clipmask)-1; const boolean is_user_clip_plane = plane_idx >= 6; const float *plane = clipper->plane[plane_idx]; struct vertex_header *vert_prev = inlist[0]; boolean *edge_prev = &inEdges[0]; float dp_prev = dot4( vert_prev->clip, plane ); unsigned outcount = 0; clipmask &= ~(1<<plane_idx); assert(n < MAX_CLIPPED_VERTICES); if (n >= MAX_CLIPPED_VERTICES) return; inlist[n] = inlist[0]; /* prevent rotation of vertices */ inEdges[n] = inEdges[0]; for (i = 1; i <= n; i++) { struct vertex_header *vert = inlist[i]; boolean *edge = &inEdges[i]; float dp = dot4( vert->clip, plane ); if (!IS_NEGATIVE(dp_prev)) { assert(outcount < MAX_CLIPPED_VERTICES); if (outcount >= MAX_CLIPPED_VERTICES) return; outEdges[outcount] = *edge_prev; outlist[outcount++] = vert_prev; } if (DIFFERENT_SIGNS(dp, dp_prev)) { struct vertex_header *new_vert; boolean *new_edge; assert(tmpnr < MAX_CLIPPED_VERTICES + 1); if (tmpnr >= MAX_CLIPPED_VERTICES + 1) return; new_vert = clipper->stage.tmp[tmpnr++]; assert(outcount < MAX_CLIPPED_VERTICES); if (outcount >= MAX_CLIPPED_VERTICES) return; new_edge = &outEdges[outcount]; outlist[outcount++] = new_vert; if (IS_NEGATIVE(dp)) { /* Going out of bounds. Avoid division by zero as we * know dp != dp_prev from DIFFERENT_SIGNS, above. */ float t = dp / (dp - dp_prev); interp( clipper, new_vert, t, vert, vert_prev ); /* Whether or not to set edge flag for the new vert depends * on whether it's a user-defined clipping plane. We're * copying NVIDIA's behaviour here. */ if (is_user_clip_plane) { /* we want to see an edge along the clip plane */ *new_edge = TRUE; new_vert->edgeflag = TRUE; } else { /* we don't want to see an edge along the frustum clip plane */ *new_edge = *edge_prev; new_vert->edgeflag = FALSE; } } else { /* Coming back in. */ float t = dp_prev / (dp_prev - dp); interp( clipper, new_vert, t, vert_prev, vert ); /* Copy starting vert's edgeflag: */ new_vert->edgeflag = vert_prev->edgeflag; *new_edge = *edge_prev; } } vert_prev = vert; edge_prev = edge; dp_prev = dp; } /* swap in/out lists */ { struct vertex_header **tmp = inlist; inlist = outlist; outlist = tmp; n = outcount; } { boolean *tmp = inEdges; inEdges = outEdges; outEdges = tmp; } } /* If flat-shading, copy provoking vertex color to polygon vertex[0] */ if (n >= 3) { if (clipper->flat) { if (stage->draw->rasterizer->flatshade_first) { if (inlist[0] != header->v[0]) { assert(tmpnr < MAX_CLIPPED_VERTICES + 1); if (tmpnr >= MAX_CLIPPED_VERTICES + 1) return; inlist[0] = dup_vert(stage, inlist[0], tmpnr++); copy_colors(stage, inlist[0], header->v[0]); } } else { if (inlist[0] != header->v[2]) { assert(tmpnr < MAX_CLIPPED_VERTICES + 1); if (tmpnr >= MAX_CLIPPED_VERTICES + 1) return; inlist[0] = dup_vert(stage, inlist[0], tmpnr++); copy_colors(stage, inlist[0], header->v[2]); } } } /* Emit the polygon as triangles to the setup stage: */ emit_poly( stage, inlist, inEdges, n, header ); } }
/* Update state. Could further delay this until we hit the first * primitive that really requires clipping. */ static void clip_init_state( struct draw_stage *stage ) { struct clip_stage *clipper = clip_stage( stage ); const struct draw_context *draw = stage->draw; const struct draw_fragment_shader *fs = draw->fs.fragment_shader; const struct tgsi_shader_info *info = draw_get_shader_info(draw); uint i, j; /* We need to know for each attribute what kind of interpolation is * done on it (flat, smooth or noperspective). But the information * is not directly accessible for outputs, only for inputs. So we * have to match semantic name and index between the VS (or GS/ES) * outputs and the FS inputs to get to the interpolation mode. * * The only hitch is with gl_FrontColor/gl_BackColor which map to * gl_Color, and their Secondary versions. First there are (up to) * two outputs for one input, so we tuck the information in a * specific array. Second if they don't have qualifiers, the * default value has to be picked from the global shade mode. * * Of course, if we don't have a fragment shader in the first * place, defaults should be used. */ /* First pick up the interpolation mode for * gl_Color/gl_SecondaryColor, with the correct default. */ int indexed_interp[2]; indexed_interp[0] = indexed_interp[1] = draw->rasterizer->flatshade ? TGSI_INTERPOLATE_CONSTANT : TGSI_INTERPOLATE_PERSPECTIVE; if (fs) { for (i = 0; i < fs->info.num_inputs; i++) { if (fs->info.input_semantic_name[i] == TGSI_SEMANTIC_COLOR) { if (fs->info.input_interpolate[i] != TGSI_INTERPOLATE_COLOR) indexed_interp[fs->info.input_semantic_index[i]] = fs->info.input_interpolate[i]; } } } /* Then resolve the interpolation mode for every output attribute. * * Given how the rest of the code, the most efficient way is to * have a vector of flat-mode attributes, and a mask for * noperspective attributes. */ clipper->num_flat_attribs = 0; memset(clipper->noperspective_attribs, 0, sizeof(clipper->noperspective_attribs)); for (i = 0; i < info->num_outputs; i++) { /* Find the interpolation mode for a specific attribute */ int interp = find_interp(fs, indexed_interp, info->output_semantic_name[i], info->output_semantic_index[i]); /* If it's flat, add it to the flat vector. Otherwise update * the noperspective mask. */ if (interp == TGSI_INTERPOLATE_CONSTANT) { clipper->flat_attribs[clipper->num_flat_attribs] = i; clipper->num_flat_attribs++; } else clipper->noperspective_attribs[i] = interp == TGSI_INTERPOLATE_LINEAR; } /* Search the extra vertex attributes */ for (j = 0; j < draw->extra_shader_outputs.num; j++) { /* Find the interpolation mode for a specific attribute */ int interp = find_interp(fs, indexed_interp, draw->extra_shader_outputs.semantic_name[j], draw->extra_shader_outputs.semantic_index[j]); /* If it's flat, add it to the flat vector. Otherwise update * the noperspective mask. */ if (interp == TGSI_INTERPOLATE_CONSTANT) { clipper->flat_attribs[clipper->num_flat_attribs] = i + j; clipper->num_flat_attribs++; } else clipper->noperspective_attribs[i + j] = interp == TGSI_INTERPOLATE_LINEAR; } stage->tri = clip_tri; stage->line = clip_line; }
/* Clip a line against the viewport and user clip planes. */ static void do_clip_line( struct draw_stage *stage, struct prim_header *header, unsigned clipmask ) { const struct clip_stage *clipper = clip_stage( stage ); struct vertex_header *v0 = header->v[0]; struct vertex_header *v1 = header->v[1]; float t0 = 0.0F; float t1 = 0.0F; struct prim_header newprim; int viewport_index = draw_viewport_index(clipper->stage.draw, v0); while (clipmask) { const unsigned plane_idx = ffs(clipmask)-1; const float dp0 = getclipdist(clipper, v0, plane_idx); const float dp1 = getclipdist(clipper, v1, plane_idx); if (util_is_inf_or_nan(dp0) || util_is_inf_or_nan(dp1)) return; //discard nan if (dp1 < 0.0F) { float t = dp1 / (dp1 - dp0); t1 = MAX2(t1, t); } if (dp0 < 0.0F) { float t = dp0 / (dp0 - dp1); t0 = MAX2(t0, t); } if (t0 + t1 >= 1.0F) return; /* discard */ clipmask &= ~(1 << plane_idx); /* turn off this plane's bit */ } if (v0->clipmask) { interp( clipper, stage->tmp[0], t0, v0, v1, viewport_index ); if (stage->draw->rasterizer->flatshade_first) { copy_flat(stage, stage->tmp[0], v0); /* copy v0 color to tmp[0] */ } else { copy_flat(stage, stage->tmp[0], v1); /* copy v1 color to tmp[0] */ } newprim.v[0] = stage->tmp[0]; } else { newprim.v[0] = v0; } if (v1->clipmask) { interp( clipper, stage->tmp[1], t1, v1, v0, viewport_index ); if (stage->draw->rasterizer->flatshade_first) { copy_flat(stage, stage->tmp[1], v0); /* copy v0 color to tmp[1] */ } else { copy_flat(stage, stage->tmp[1], v1); /* copy v1 color to tmp[1] */ } newprim.v[1] = stage->tmp[1]; } else { newprim.v[1] = v1; } stage->next->line( stage->next, &newprim ); }