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=3265dcd154a90964ced2f5f48c2bf0c65a073d4e;hpb=2e28c1fda24988241ef7abb91b7d896e38f5df26;p=mesa.git diff --git a/src/gallium/auxiliary/draw/draw_pipe_clip.c b/src/gallium/auxiliary/draw/draw_pipe_clip.c index 3265dcd154a..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,30 +55,28 @@ #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 clipper { +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: - */ -static INLINE struct clipper *clipper_stage( struct draw_stage *stage ) +/** Cast wrapper */ +static INLINE struct clip_stage *clip_stage( struct draw_stage *stage ) { - return (struct clipper *)stage; + return (struct clip_stage *)stage; } @@ -81,25 +85,29 @@ static INLINE struct clipper *clipper_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 clipper *clipper = clipper_stage(stage); + 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]); } } @@ -108,35 +116,36 @@ static void copy_colors( struct draw_stage *stage, /* Interpolate between two vertices to produce a third. */ -static void interp( const struct clipper *clip, +static void interp( const struct clip_stage *clip, struct vertex_header *dst, float t, const struct vertex_header *out, const struct vertex_header *in ) { - const unsigned nr_attrs = clip->stage.draw->vs.num_vs_outputs; - const unsigned pos_attr = clip->stage.draw->vs.position_output; + 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]; @@ -146,27 +155,72 @@ static void interp( const struct clipper *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]); + } } } +/** + * Emit a post-clip polygon to the next pipeline stage. The polygon + * will be convex and the provoking vertex will always be vertex[0]. + */ static void emit_poly( struct draw_stage *stage, struct vertex_header **inlist, + const boolean *edgeflags, unsigned n, const struct prim_header *origPrim) { struct prim_header header; unsigned i; + ushort edge_first, edge_middle, edge_last; + + if (stage->draw->rasterizer->flatshade_first) { + edge_first = DRAW_PIPE_EDGE_FLAG_0; + edge_middle = DRAW_PIPE_EDGE_FLAG_1; + edge_last = DRAW_PIPE_EDGE_FLAG_2; + } + else { + edge_first = DRAW_PIPE_EDGE_FLAG_2; + edge_middle = DRAW_PIPE_EDGE_FLAG_0; + edge_last = DRAW_PIPE_EDGE_FLAG_1; + } - const ushort edge_first = DRAW_PIPE_EDGE_FLAG_2; - const ushort edge_middle = DRAW_PIPE_EDGE_FLAG_0; - const ushort 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; @@ -174,18 +228,36 @@ static void emit_poly( struct draw_stage *stage, header.pad = 0; for (i = 2; i < n; i++, header.flags = edge_middle) { - header.v[0] = inlist[i-1]; - header.v[1] = inlist[i]; - header.v[2] = inlist[0]; /* keep in v[2] for flatshading */ + /* order the triangle verts to respect the provoking vertex mode */ + if (stage->draw->rasterizer->flatshade_first) { + header.v[0] = inlist[0]; /* the provoking vertex */ + header.v[1] = inlist[i-1]; + header.v[2] = inlist[i]; + } + else { + header.v[0] = inlist[i-1]; + header.v[1] = inlist[i]; + header.v[2] = inlist[0]; /* the provoking vertex */ + } - if (i == n-1) - header.flags |= edge_last; + if (!edgeflags[i-1]) { + header.flags &= ~edge_middle; + } - if (0) { + if (i == n - 1 && edgeflags[i]) + header.flags |= edge_last; + + if (DEBUG_CLIP) { const struct draw_vertex_shader *vs = stage->draw->vs.vertex_shader; uint j, k; - debug_printf("Clipped tri:\n"); + 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], @@ -200,15 +272,39 @@ static void emit_poly( struct draw_stage *stage, } } + static INLINE float dot4(const float *a, const float *b) { - return (a[0]*b[0] + - a[1]*b[1] + - a[2]*b[2] + - a[3]*b[3]); + return (a[0] * b[0] + + a[1] * b[1] + + a[2] * b[2] + + 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. */ @@ -217,7 +313,7 @@ do_clip_tri( struct draw_stage *stage, struct prim_header *header, unsigned clipmask ) { - struct clipper *clipper = clipper_stage( stage ); + 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; @@ -225,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)) { @@ -261,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); @@ -273,37 +430,58 @@ 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; } + /* swap in/out lists */ { struct vertex_header **tmp = inlist; inlist = outlist; outlist = tmp; n = outcount; } - } - - /* If flat-shading, copy color to new provoking vertex. - */ - if (clipper->flat && inlist[0] != header->v[2]) { - if (1) { - inlist[0] = dup_vert(stage, inlist[0], tmpnr++); + { + boolean *tmp = inEdges; + inEdges = outEdges; + outEdges = tmp; } - copy_colors(stage, inlist[0], header->v[2]); } - - - /* Emit the polygon as triangles to the setup stage: + /* If flat-shading, copy provoking vertex color to polygon vertex[0] */ - if (n >= 3) - emit_poly( stage, inlist, n, header ); + 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]) { + 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 ); + } } @@ -314,20 +492,17 @@ do_clip_line( struct draw_stage *stage, struct prim_header *header, unsigned clipmask ) { - const struct clipper *clipper = clipper_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); @@ -347,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 { @@ -416,27 +588,96 @@ clip_tri( struct draw_stage *stage, } } + /* 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 clipper *clipper = clipper_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; @@ -488,14 +729,12 @@ static void clip_destroy( struct draw_stage *stage ) */ struct draw_stage *draw_clip_stage( struct draw_context *draw ) { - struct clipper *clipper = CALLOC_STRUCT(clipper); + struct clip_stage *clipper = CALLOC_STRUCT(clip_stage); if (clipper == NULL) goto fail; - if (!draw_alloc_temp_verts( &clipper->stage, MAX_CLIPPED_VERTICES+1 )) - goto fail; - clipper->stage.draw = draw; + clipper->stage.name = "clipper"; clipper->stage.point = clip_point; clipper->stage.line = clip_first_line; clipper->stage.tri = clip_first_tri; @@ -505,6 +744,9 @@ struct draw_stage *draw_clip_stage( struct draw_context *draw ) clipper->plane = draw->plane; + if (!draw_alloc_temp_verts( &clipper->stage, MAX_CLIPPED_VERTICES+1 )) + goto fail; + return &clipper->stage; fail: