diff --git a/src/gallium/drivers/radeonsi/si_pipe.h b/src/gallium/drivers/radeonsi/si_pipe.h index 1a122970021..ac96d1cbfa7 100644 --- a/src/gallium/drivers/radeonsi/si_pipe.h +++ b/src/gallium/drivers/radeonsi/si_pipe.h @@ -1255,6 +1255,23 @@ struct si_context { bool vs_disables_clipping_viewport; bool has_reset_been_notified; + /* The number of pixels outside the viewport that are not culled by the clipper. + * Normally, the clipper clips everything outside the viewport, however, points and lines + * can have vertices outside the viewport, but their edges can be inside the viewport. Those + * shouldn't be culled. The problem is that the register setting (PA_CL_GB_*_DISC_ADJ) that + * controls the discard distance, which depends on the point size and line width, applies to + * all primitive types, and we would have to set 0 distance for triangles and non-zero for + * points and lines whenever the primitive type changes, which would add overhead and cause + * context rolls. + * + * To reduce that, whenever the discard distance changes for points and lines, we keep it + * at that higher value up to a certain small number for all primitive types including all + * points and lines within a specific size. This is slightly inefficient, but it eliminates + * a lot of guardband state updates and context register changes. + */ + float min_clip_discard_distance_watermark; + float current_clip_discard_distance; + /* Precomputed IA_MULTI_VGT_PARAM */ union si_vgt_param_key ia_multi_vgt_param_key; unsigned ia_multi_vgt_param[SI_NUM_VGT_PARAM_STATES]; @@ -2114,6 +2131,28 @@ void si_check_dirty_buffers_textures(struct si_context *sctx) } } +static inline void si_set_clip_discard_distance(struct si_context *sctx, float distance) +{ + /* Determine whether the guardband registers change. + * + * When we see a value greater than min_clip_discard_distance_watermark, we increase it + * up to a certain number to eliminate those state changes next time they happen. + * See the comment at min_clip_discard_distance_watermark. + */ + if (distance > sctx->min_clip_discard_distance_watermark) { + /* The maximum number was determined from Viewperf. The number is in units of half-pixels. */ + sctx->min_clip_discard_distance_watermark = MIN2(distance, 6); + + float old_distance = sctx->current_clip_discard_distance; + float new_distance = MAX2(distance, sctx->min_clip_discard_distance_watermark); + + if (old_distance != new_distance) { + sctx->current_clip_discard_distance = new_distance; + si_mark_atom_dirty(sctx, &sctx->atoms.s.guardband); + } + } +} + /* Update these two GS_STATE fields. They depend on whatever the last shader before PS is * and the rasterizer state. * @@ -2147,16 +2186,23 @@ si_set_rasterized_prim(struct si_context *sctx, enum mesa_prim rast_prim, bool is_rect = rast_prim == SI_PRIM_RECTANGLE_LIST; bool is_points = rast_prim == MESA_PRIM_POINTS; bool is_lines = util_prim_is_lines(rast_prim); - bool is_triangles = util_rast_prim_is_triangles(rast_prim); - if ((is_points || is_lines) != util_prim_is_points_or_lines(sctx->current_rast_prim)) - si_mark_atom_dirty(sctx, &sctx->atoms.s.guardband); + if (is_points) { + si_set_clip_discard_distance(sctx, sctx->queued.named.rasterizer->max_point_size); + sctx->gs_out_prim = V_028A6C_POINTLIST; + } else if (is_lines) { + si_set_clip_discard_distance(sctx, sctx->queued.named.rasterizer->line_width); + sctx->gs_out_prim = V_028A6C_LINESTRIP; + } else if (is_rect) { + /* Don't change the clip discard distance for rectangles. */ + sctx->gs_out_prim = V_028A6C_RECTLIST; + } else { + si_set_clip_discard_distance(sctx, 0); + sctx->gs_out_prim = V_028A6C_TRISTRIP; + } sctx->current_rast_prim = rast_prim; si_vs_ps_key_update_rast_prim_smooth_stipple(sctx); - sctx->gs_out_prim = is_triangles ? V_028A6C_TRISTRIP : - is_lines ? V_028A6C_LINESTRIP : - is_rect ? V_028A6C_RECTLIST : V_028A6C_POINTLIST; si_update_ngg_prim_state_sgpr(sctx, hw_vs, ngg); } } diff --git a/src/gallium/drivers/radeonsi/si_state.c b/src/gallium/drivers/radeonsi/si_state.c index 4d61823eab3..0c47c40695c 100644 --- a/src/gallium/drivers/radeonsi/si_state.c +++ b/src/gallium/drivers/radeonsi/si_state.c @@ -1268,10 +1268,15 @@ static void si_bind_rs_state(struct pipe_context *ctx, void *state) if (old_rs->scissor_enable != rs->scissor_enable) si_mark_atom_dirty(sctx, &sctx->atoms.s.scissors); - if (old_rs->line_width != rs->line_width || old_rs->max_point_size != rs->max_point_size || - old_rs->half_pixel_center != rs->half_pixel_center) + /* This never changes for OpenGL. */ + if (old_rs->half_pixel_center != rs->half_pixel_center) si_mark_atom_dirty(sctx, &sctx->atoms.s.guardband); + if (util_prim_is_lines(sctx->current_rast_prim)) + si_set_clip_discard_distance(sctx, rs->line_width); + else if (sctx->current_rast_prim == MESA_PRIM_POINTS) + si_set_clip_discard_distance(sctx, rs->max_point_size); + if (old_rs->clip_halfz != rs->clip_halfz) si_mark_atom_dirty(sctx, &sctx->atoms.s.viewports); diff --git a/src/gallium/drivers/radeonsi/si_state_viewport.c b/src/gallium/drivers/radeonsi/si_state_viewport.c index b70900d51bf..e2de5ffe2f0 100644 --- a/src/gallium/drivers/radeonsi/si_state_viewport.c +++ b/src/gallium/drivers/radeonsi/si_state_viewport.c @@ -249,7 +249,6 @@ static void si_emit_guardband(struct si_context *sctx, unsigned index) struct si_signed_scissor vp_as_scissor; struct pipe_viewport_state vp; float left, top, right, bottom, max_range, guardband_x, guardband_y; - float discard_x, discard_y; if (sctx->vs_writes_viewport_index) { /* Shaders can draw to any viewport. Make a union of all @@ -339,28 +338,17 @@ static void si_emit_guardband(struct si_context *sctx, unsigned index) guardband_x = MIN2(-left, right); guardband_y = MIN2(-top, bottom); - discard_x = 1.0; - discard_y = 1.0; + float discard_x = 1.0; + float discard_y = 1.0; + float distance = sctx->current_clip_discard_distance; - if (unlikely(util_prim_is_points_or_lines(sctx->current_rast_prim))) { - /* When rendering wide points or lines, we need to be more - * conservative about when to discard them entirely. */ - float pixels; + /* Add half the point size / line width */ + discard_x += distance / (2.0 * vp.scale[0]); + discard_y += distance / (2.0 * vp.scale[1]); - if (sctx->current_rast_prim == MESA_PRIM_POINTS) - pixels = rs->max_point_size; - else - pixels = rs->line_width; - - /* Add half the point size / line width */ - discard_x += pixels / (2.0 * vp.scale[0]); - discard_y += pixels / (2.0 * vp.scale[1]); - - /* Discard primitives that would lie entirely outside the clip - * region. */ - discard_x = MIN2(discard_x, guardband_x); - discard_y = MIN2(discard_y, guardband_y); - } + /* Discard primitives that would lie entirely outside the viewport area. */ + discard_x = MIN2(discard_x, guardband_x); + discard_y = MIN2(discard_y, guardband_y); unsigned pa_su_vtx_cntl = S_028BE4_PIX_CENTER(rs->half_pixel_center) | S_028BE4_ROUND_MODE(V_028BE4_X_ROUND_TO_EVEN) |