X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fgallium%2Fauxiliary%2Fdraw%2Fdraw_pipe_clip.c;h=8da0c41390fd2269dcf8139b197e9a74104c61ea;hb=f313b0c8502868dab2a87237af295a34ec0dea26;hp=122b1c796895a3513711f3c1513d8f9c6695e089;hpb=bcc13b74443137043e8a34f8cb64a5add0d8af93;p=mesa.git diff --git a/src/gallium/auxiliary/draw/draw_pipe_clip.c b/src/gallium/auxiliary/draw/draw_pipe_clip.c index 122b1c79689..8da0c41390f 100644 --- a/src/gallium/auxiliary/draw/draw_pipe_clip.c +++ b/src/gallium/auxiliary/draw/draw_pipe_clip.c @@ -39,6 +39,12 @@ #include "draw_vs.h" #include "draw_pipe.h" +#include "draw_fs.h" +#include "draw_gs.h" + + +/** Set to 1 to enable printing of coords before/after clipping */ +#define DEBUG_CLIP 0 #ifndef IS_NEGATIVE @@ -49,27 +55,25 @@ #define DIFFERENT_SIGNS(x, y) ((x) * (y) <= 0.0F && (x) - (y) != 0.0F) #endif -#ifndef MAX_CLIPPED_VERTICES #define MAX_CLIPPED_VERTICES ((2 * (6 + PIPE_MAX_CLIP_PLANES))+1) -#endif struct clip_stage { struct draw_stage stage; /**< base class */ - /* Basically duplicate some of the flatshading logic here: - */ - boolean flat; - uint num_color_attribs; - uint color_attribs[4]; /* front/back primary/secondary colors */ + /* List of the attributes to be flatshaded. */ + uint num_flat_attribs; + uint flat_attribs[PIPE_MAX_SHADER_OUTPUTS]; + + /* Mask of attributes in noperspective mode */ + boolean noperspective_attribs[PIPE_MAX_SHADER_OUTPUTS]; float (*plane)[4]; }; -/* This is a bit confusing: - */ +/** Cast wrapper */ static INLINE struct clip_stage *clip_stage( struct draw_stage *stage ) { return (struct clip_stage *)stage; @@ -81,26 +85,29 @@ static INLINE struct clip_stage *clip_stage( struct draw_stage *stage ) /* All attributes are float[4], so this is easy: */ -static void interp_attr( float *fdst, +static void interp_attr( float dst[4], float t, - const float *fin, - const float *fout ) + const float in[4], + const float out[4] ) { - fdst[0] = LINTERP( t, fout[0], fin[0] ); - fdst[1] = LINTERP( t, fout[1], fin[1] ); - fdst[2] = LINTERP( t, fout[2], fin[2] ); - fdst[3] = LINTERP( t, fout[3], fin[3] ); + dst[0] = LINTERP( t, out[0], in[0] ); + dst[1] = LINTERP( t, out[1], in[1] ); + dst[2] = LINTERP( t, out[2], in[2] ); + dst[3] = LINTERP( t, out[3], in[3] ); } -static void copy_colors( struct draw_stage *stage, - struct vertex_header *dst, - const struct vertex_header *src ) +/** + * Copy flat shaded attributes src vertex to dst vertex. + */ +static void copy_flat( 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]; + for (i = 0; i < clipper->num_flat_attribs; i++) { + const uint attr = clipper->flat_attribs[i]; COPY_4FV(dst->data[attr], src->data[attr]); } } @@ -117,27 +124,28 @@ static void interp( const struct clip_stage *clip, { const unsigned nr_attrs = draw_current_shader_outputs(clip->stage.draw); const unsigned pos_attr = draw_current_shader_position_output(clip->stage.draw); + const unsigned clip_attr = draw_current_shader_clipvertex_output(clip->stage.draw); unsigned j; + float t_nopersp; /* Vertex header. */ - { - dst->clipmask = 0; - dst->edgeflag = 0; /* will get overwritten later */ - dst->pad = 0; - dst->vertex_id = UNDEFINED_VERTEX_ID; - } + dst->clipmask = 0; + dst->edgeflag = 0; /* will get overwritten later */ + dst->have_clipdist = in->have_clipdist; + dst->vertex_id = UNDEFINED_VERTEX_ID; - /* Clip coordinates: interpolate normally + /* Interpolate the clip-space coords. */ - { - interp_attr(dst->clip, t, in->clip, out->clip); - } + interp_attr(dst->clip, t, in->clip, out->clip); + /* interpolate the clip-space position */ + interp_attr(dst->pre_clip_pos, t, in->pre_clip_pos, out->pre_clip_pos); - /* Do the projective divide and insert window coordinates: + /* Do the projective divide and viewport transformation to get + * new window coordinates: */ { - const float *pos = dst->clip; + const float *pos = dst->pre_clip_pos; const float *scale = clip->stage.draw->viewport.scale; const float *trans = clip->stage.draw->viewport.translate; const float oow = 1.0f / pos[3]; @@ -147,12 +155,41 @@ static void interp( const struct clip_stage *clip, dst->data[pos_attr][2] = pos[2] * oow * scale[2] + trans[2]; dst->data[pos_attr][3] = oow; } + + /** + * Compute the t in screen-space instead of 3d space to use + * for noperspective interpolation. + * + * The points can be aligned with the X axis, so in that case try + * the Y. When both points are at the same screen position, we can + * pick whatever value (the interpolated point won't be in front + * anyway), so just use the 3d t. + */ + { + int k; + t_nopersp = t; + /* find either in.x != out.x or in.y != out.y */ + for (k = 0; k < 2; k++) { + if (in->clip[k] != out->clip[k]) { + /* do divide by W, then compute linear interpolation factor */ + float in_coord = in->clip[k] / in->clip[3]; + float out_coord = out->clip[k] / out->clip[3]; + float dst_coord = dst->clip[k] / dst->clip[3]; + t_nopersp = (dst_coord - out_coord) / (in_coord - out_coord); + break; + } + } + } /* Other attributes */ for (j = 0; j < nr_attrs; j++) { - if (j != pos_attr) - interp_attr(dst->data[j], t, in->data[j], out->data[j]); + if (j != pos_attr && j != clip_attr) { + if (clip->noperspective_attribs[j]) + interp_attr(dst->data[j], t_nopersp, in->data[j], out->data[j]); + else + interp_attr(dst->data[j], t, in->data[j], out->data[j]); + } } } @@ -163,6 +200,7 @@ static void interp( const struct clip_stage *clip, */ static void emit_poly( struct draw_stage *stage, struct vertex_header **inlist, + const boolean *edgeflags, unsigned n, const struct prim_header *origPrim) { @@ -181,6 +219,9 @@ static void emit_poly( struct draw_stage *stage, edge_last = DRAW_PIPE_EDGE_FLAG_1; } + if (!edgeflags[0]) + edge_first = 0; + /* later stages may need the determinant, but only the sign matters */ header.det = origPrim->det; header.flags = DRAW_PIPE_RESET_STIPPLE | edge_first | edge_middle; @@ -199,15 +240,24 @@ static void emit_poly( struct draw_stage *stage, header.v[2] = inlist[0]; /* the provoking vertex */ } - if (i == n-1) + if (!edgeflags[i-1]) { + header.flags &= ~edge_middle; + } + + if (i == n - 1 && edgeflags[i]) header.flags |= edge_last; - if (0) { + if (DEBUG_CLIP) { const struct draw_vertex_shader *vs = stage->draw->vs.vertex_shader; uint j, k; debug_printf("Clipped tri: (flat-shade-first = %d)\n", stage->draw->rasterizer->flatshade_first); for (j = 0; j < 3; j++) { + debug_printf(" Vert %d: clip: %f %f %f %f\n", j, + header.v[j]->clip[0], + header.v[j]->clip[1], + header.v[j]->clip[2], + header.v[j]->clip[3]); for (k = 0; k < vs->info.num_outputs; k++) { debug_printf(" Vert %d: Attr %d: %f %f %f %f\n", j, k, header.v[j]->data[k][0], @@ -232,6 +282,29 @@ dot4(const float *a, const float *b) a[3] * b[3]); } +/* + * this function extracts the clip distance for the current plane, + * it first checks if the shader provided a clip distance, otherwise + * it works out the value using the clipvertex + */ +static INLINE float getclipdist(const struct clip_stage *clipper, + struct vertex_header *vert, + int plane_idx) +{ + const float *plane; + float dp; + if (vert->have_clipdist && plane_idx >= 6) { + /* pick the correct clipdistance element from the output vectors */ + int _idx = plane_idx - 6; + int cdi = _idx >= 4; + int vidx = cdi ? _idx - 4 : _idx; + dp = vert->data[draw_current_shader_clipdistance_output(clipper->stage.draw, cdi)][vidx]; + } else { + plane = clipper->plane[plane_idx]; + dp = dot4(vert->clip, plane); + } + return dp; +} /* Clip a triangle against the viewport and user clip planes. */ @@ -248,33 +321,82 @@ do_clip_tri( struct draw_stage *stage, 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]; + if (DEBUG_CLIP) { + const float *v0 = header->v[0]->clip; + const float *v1 = header->v[1]->clip; + const float *v2 = header->v[2]->clip; + debug_printf("Clip triangle:\n"); + debug_printf(" %f, %f, %f, %f\n", v0[0], v0[1], v0[2], v0[3]); + debug_printf(" %f, %f, %f, %f\n", v1[0], v1[1], v1[2], v1[3]); + debug_printf(" %f, %f, %f, %f\n", v2[0], v2[1], v2[2], v2[3]); + } + + /* + * 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 float *plane = clipper->plane[plane_idx]; + const boolean is_user_clip_plane = plane_idx >= 6; struct vertex_header *vert_prev = inlist[0]; - float dp_prev = dot4( vert_prev->clip, plane ); + boolean *edge_prev = &inEdges[0]; + float dp_prev; unsigned outcount = 0; + dp_prev = getclipdist(clipper, vert_prev, plane_idx); clipmask &= ~(1<= 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 ); + float dp = getclipdist(clipper, vert, plane_idx); 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 = clipper->stage.tmp[tmpnr++]; + 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)) { @@ -284,10 +406,22 @@ do_clip_tri( struct draw_stage *stage, float t = dp / (dp - dp_prev); interp( clipper, new_vert, t, vert, vert_prev ); - /* Force edgeflag true in this case: + /* 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. */ - new_vert->edgeflag = 1; - } else { + 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); @@ -296,10 +430,12 @@ do_clip_tri( struct draw_stage *stage, /* Copy starting vert's edgeflag: */ new_vert->edgeflag = vert_prev->edgeflag; + *new_edge = *edge_prev; } } vert_prev = vert; + edge_prev = edge; dp_prev = dp; } @@ -310,29 +446,42 @@ do_clip_tri( struct draw_stage *stage, outlist = tmp; n = outcount; } + { + boolean *tmp = inEdges; + inEdges = outEdges; + outEdges = tmp; + } + } /* If flat-shading, copy provoking vertex color to polygon vertex[0] */ - if (clipper->flat) { - if (stage->draw->rasterizer->flatshade_first) { - if (inlist[0] != header->v[0]) { - inlist[0] = dup_vert(stage, inlist[0], tmpnr++); - copy_colors(stage, inlist[0], header->v[0]); + if (n >= 3) { + if (clipper->num_flat_attribs) { + 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_flat(stage, inlist[0], header->v[0]); + } } - } - else { - if (inlist[0] != header->v[2]) { - inlist[0] = dup_vert(stage, inlist[0], tmpnr++); - copy_colors(stage, inlist[0], header->v[2]); + 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_flat(stage, inlist[0], header->v[2]); + } } } + + /* Emit the polygon as triangles to the setup stage: + */ + emit_poly( stage, inlist, inEdges, n, header ); } - - /* Emit the polygon as triangles to the setup stage: - */ - if (n >= 3) - emit_poly( stage, inlist, n, header ); } @@ -346,17 +495,14 @@ do_clip_line( struct draw_stage *stage, 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 ); + const float dp0 = getclipdist(clipper, v0, plane_idx); + const float dp1 = getclipdist(clipper, v1, plane_idx); if (dp1 < 0.0F) { float t = dp1 / (dp1 - dp0); @@ -376,10 +522,7 @@ do_clip_line( struct draw_stage *stage, if (v0->clipmask) { interp( clipper, stage->tmp[0], t0, v0, v1 ); - - if (clipper->flat) - copy_colors(stage, stage->tmp[0], v0); - + copy_flat(stage, stage->tmp[0], v0); newprim.v[0] = stage->tmp[0]; } else { @@ -453,20 +596,88 @@ static void clip_init_state( struct draw_stage *stage ) { struct clip_stage *clipper = clip_stage( stage ); + const struct draw_vertex_shader *vs = stage->draw->vs.vertex_shader; + const struct draw_geometry_shader *gs = stage->draw->gs.geometry_shader; + const struct draw_fragment_shader *fs = stage->draw->fs.fragment_shader; + uint i; + const struct tgsi_shader_info *vs_info = gs ? &gs->info : &vs->info; + + /* 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. + */ - clipper->flat = stage->draw->rasterizer->flatshade ? TRUE : FALSE; + /* 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] = stage->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]; + } + } + } - if (clipper->flat) { - const struct draw_vertex_shader *vs = stage->draw->vs.vertex_shader; - uint 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_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; - } + clipper->num_flat_attribs = 0; + memset(clipper->noperspective_attribs, 0, sizeof(clipper->noperspective_attribs)); + for (i = 0; i < vs_info->num_outputs; i++) { + /* Find the interpolation mode for a specific attribute + */ + int interp; + + /* If it's gl_{Front,Back}{,Secondary}Color, pick up the mode + * from the array we've filled before. */ + if (vs_info->output_semantic_name[i] == TGSI_SEMANTIC_COLOR || + vs_info->output_semantic_name[i] == TGSI_SEMANTIC_BCOLOR) { + interp = indexed_interp[vs_info->output_semantic_index[i]]; + } else { + /* Otherwise, search in the FS inputs, with a decent default + * if we don't find it. + */ + uint j; + interp = TGSI_INTERPOLATE_PERSPECTIVE; + if (fs) { + for (j = 0; j < fs->info.num_inputs; j++) { + if (vs_info->output_semantic_name[i] == fs->info.input_semantic_name[j] && + vs_info->output_semantic_index[i] == fs->info.input_semantic_index[j]) { + interp = fs->info.input_interpolate[j]; + break; + } + } + } } + + /* 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; } stage->tri = clip_tri;