r300: pop-free clipping

Ported from r600: fix pop-free clipping by Patrick Lerda

Signed-off-by: Pavel Ondračka <pavel.ondracka@gmail.com>
Reviewed-by: Marek Olšák <marek.olsak@amd.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/37977>
This commit is contained in:
Pavel Ondračka 2025-10-17 12:50:15 +02:00 committed by Marge Bot
parent 4d36c637dd
commit 0d4aa5f55f
9 changed files with 220 additions and 24 deletions

View file

@ -1,16 +1,9 @@
dEQP-GLES2.functional.clipping.line.wide_line_clip_viewport_center,Fail
dEQP-GLES2.functional.clipping.line.wide_line_clip_viewport_corner,Fail
dEQP-GLES2.functional.clipping.point.wide_point_clip,Fail
dEQP-GLES2.functional.clipping.point.wide_point_clip_viewport_center,Fail
dEQP-GLES2.functional.clipping.point.wide_point_clip_viewport_corner,Fail
dEQP-GLES2.functional.fbo.completeness.renderable.texture.color0.rgb_half_float_oes,Fail dEQP-GLES2.functional.fbo.completeness.renderable.texture.color0.rgb_half_float_oes,Fail
dEQP-GLES2.functional.fbo.render.repeated_clear.tex2d_rgb,Fail dEQP-GLES2.functional.fbo.render.repeated_clear.tex2d_rgb,Fail
dEQP-GLES2.functional.fbo.render.repeated_clear.tex2d_rgba,Fail dEQP-GLES2.functional.fbo.render.repeated_clear.tex2d_rgba,Fail
dEQP-GLES2.functional.polygon_offset.fixed16_displacement_with_units,Fail dEQP-GLES2.functional.polygon_offset.fixed16_displacement_with_units,Fail
dEQP-GLES2.functional.polygon_offset.fixed16_render_with_units,Fail dEQP-GLES2.functional.polygon_offset.fixed16_render_with_units,Fail
dEQP-GLES2.functional.rasterization.primitives.line_loop_wide,Fail
dEQP-GLES2.functional.rasterization.primitives.line_strip_wide,Fail
dEQP-GLES2.functional.shaders.builtin_variable.frontfacing,Fail dEQP-GLES2.functional.shaders.builtin_variable.frontfacing,Fail
dEQP-GLES2.functional.shaders.conversions.vector_combine.vec2_bool_to_ivec3_fragment,Fail dEQP-GLES2.functional.shaders.conversions.vector_combine.vec2_bool_to_ivec3_fragment,Fail
dEQP-GLES2.functional.shaders.conversions.vector_combine.vec2_bvec2_to_ivec4_fragment,Fail dEQP-GLES2.functional.shaders.conversions.vector_combine.vec2_bvec2_to_ivec4_fragment,Fail

View file

@ -1,8 +1,3 @@
dEQP-GLES2.functional.clipping.line.wide_line_clip_viewport_center,Fail
dEQP-GLES2.functional.clipping.line.wide_line_clip_viewport_corner,Fail
dEQP-GLES2.functional.clipping.point.wide_point_clip,Fail
dEQP-GLES2.functional.clipping.point.wide_point_clip_viewport_center,Fail
dEQP-GLES2.functional.clipping.point.wide_point_clip_viewport_corner,Fail
dEQP-GLES2.functional.fbo.completeness.renderable.texture.color0.rgb_half_float_oes,Fail dEQP-GLES2.functional.fbo.completeness.renderable.texture.color0.rgb_half_float_oes,Fail
dEQP-GLES2.functional.fbo.render.repeated_clear.tex2d_rgb,Fail dEQP-GLES2.functional.fbo.render.repeated_clear.tex2d_rgb,Fail

View file

@ -1,11 +1,3 @@
# Wide points fully clipped when they should show up partially.
dEQP-GLES2.functional.clipping.point.wide_point_clip,Fail
dEQP-GLES2.functional.clipping.point.wide_point_clip_viewport_center,Fail
dEQP-GLES2.functional.clipping.point.wide_point_clip_viewport_corner,Fail
dEQP-GLES2.functional.clipping.line.wide_line_clip_viewport_center,Fail
dEQP-GLES2.functional.clipping.line.wide_line_clip_viewport_corner,Fail
# "Framebuffer checked as complete, expected incomplete" # "Framebuffer checked as complete, expected incomplete"
dEQP-GLES2.functional.fbo.completeness.renderable.texture.color0.rgb_half_float_oes,Fail dEQP-GLES2.functional.fbo.completeness.renderable.texture.color0.rgb_half_float_oes,Fail
@ -17,10 +9,6 @@ dEQP-GLES2.functional.polygon_offset.fixed16_displacement_with_units,Fail
dEQP-GLES2.functional.polygon_offset.fixed16_render_with_units,Fail dEQP-GLES2.functional.polygon_offset.fixed16_render_with_units,Fail
dEQP-GLES2.functional.polygon_offset.fixed16_factor_1_slope,Fail dEQP-GLES2.functional.polygon_offset.fixed16_factor_1_slope,Fail
# "Invalid line width at (119, 179) - (119, 180). Detected width of 2, expected 1"
dEQP-GLES2.functional.rasterization.primitives.line_strip_wide,Fail,Fail
dEQP-GLES2.functional.rasterization.primitives.line_loop_wide,Fail
dEQP-GLES2.functional.texture.format.a8_cube_npot,Fail dEQP-GLES2.functional.texture.format.a8_cube_npot,Fail
dEQP-GLES2.functional.texture.format.l8_cube_npot,Fail dEQP-GLES2.functional.texture.format.l8_cube_npot,Fail
dEQP-GLES2.functional.texture.format.la88_cube_npot,Fail dEQP-GLES2.functional.texture.format.la88_cube_npot,Fail

View file

@ -104,6 +104,7 @@ static void r300_destroy_context(struct pipe_context* context)
FREE(r300->invariant_state.state); FREE(r300->invariant_state.state);
FREE(r300->rs_block_state.state); FREE(r300->rs_block_state.state);
FREE(r300->sample_mask.state); FREE(r300->sample_mask.state);
FREE(r300->guardband_state.state);
FREE(r300->scissor_state.state); FREE(r300->scissor_state.state);
FREE(r300->textures_state.state); FREE(r300->textures_state.state);
FREE(r300->vap_invariant_state.state); FREE(r300->vap_invariant_state.state);
@ -184,6 +185,7 @@ static bool r300_setup_atoms(struct r300_context* r300)
/* VAP. */ /* VAP. */
R300_INIT_ATOM(viewport_state, 9); R300_INIT_ATOM(viewport_state, 9);
R300_INIT_ATOM(pvs_flush, 2); R300_INIT_ATOM(pvs_flush, 2);
R300_INIT_ATOM(guardband_state, 5);
R300_INIT_ATOM(vap_invariant_state, is_r500 || !has_tcl ? 11 : 9); R300_INIT_ATOM(vap_invariant_state, is_r500 || !has_tcl ? 11 : 9);
R300_INIT_ATOM(vertex_stream_state, 0); R300_INIT_ATOM(vertex_stream_state, 0);
R300_INIT_ATOM(vs_state, 0); R300_INIT_ATOM(vs_state, 0);
@ -228,6 +230,7 @@ static bool r300_setup_atoms(struct r300_context* r300)
R300_ALLOC_ATOM(fb_state, pipe_framebuffer_state); R300_ALLOC_ATOM(fb_state, pipe_framebuffer_state);
R300_ALLOC_ATOM(gpu_flush, pipe_framebuffer_state); R300_ALLOC_ATOM(gpu_flush, pipe_framebuffer_state);
r300->sample_mask.state = malloc(4); r300->sample_mask.state = malloc(4);
R300_ALLOC_ATOM(guardband_state, r300_guardband_state);
R300_ALLOC_ATOM(scissor_state, pipe_scissor_state); R300_ALLOC_ATOM(scissor_state, pipe_scissor_state);
R300_ALLOC_ATOM(rs_block_state, r300_rs_block); R300_ALLOC_ATOM(rs_block_state, r300_rs_block);
R300_ALLOC_ATOM(fs_constants, r300_constant_buffer); R300_ALLOC_ATOM(fs_constants, r300_constant_buffer);
@ -408,6 +411,18 @@ struct pipe_context* r300_create_context(struct pipe_screen* screen,
if (!r300_setup_atoms(r300)) if (!r300_setup_atoms(r300))
goto fail; goto fail;
r300->current_clip_discard_distance = 0.0f;
r300->min_clip_discard_distance_watermark = 0.0f;
r300->current_rast_prim = ~0u;
struct r300_guardband_state *guard =
(struct r300_guardband_state *)r300->guardband_state.state;
if (guard) {
guard->vert_clip = 1.0f;
guard->vert_disc = 1.0f;
guard->horz_clip = 1.0f;
guard->horz_disc = 1.0f;
}
r300_init_blit_functions(r300); r300_init_blit_functions(r300);
r300_init_flush_functions(r300); r300_init_flush_functions(r300);
r300_init_query_functions(r300); r300_init_query_functions(r300);

View file

@ -40,6 +40,18 @@ enum colormask_swizzle {
COLORMASK_NUM_SWIZZLES COLORMASK_NUM_SWIZZLES
}; };
static inline bool r300_prim_is_lines(unsigned prim)
{
switch (prim) {
case MESA_PRIM_LINES:
case MESA_PRIM_LINE_LOOP:
case MESA_PRIM_LINE_STRIP:
return true;
default:
return false;
}
}
struct r300_atom { struct r300_atom {
/* Name, for debugging. */ /* Name, for debugging. */
const char* name; const char* name;
@ -144,6 +156,9 @@ struct r300_rs_state {
/* This is emitted in the draw function. */ /* This is emitted in the draw function. */
uint32_t color_control; /* R300_GA_COLOR_CONTROL: 0x4278 */ uint32_t color_control; /* R300_GA_COLOR_CONTROL: 0x4278 */
float max_point_size;
float line_width;
}; };
struct r300_rs_block { struct r300_rs_block {
@ -203,6 +218,13 @@ struct r300_texture_sampler_state {
uint32_t border_color; /* R300_TX_BORDER_COLOR: 0x45c0 */ uint32_t border_color; /* R300_TX_BORDER_COLOR: 0x45c0 */
}; };
struct r300_guardband_state {
float vert_clip;
float vert_disc;
float horz_clip;
float horz_disc;
};
struct r300_textures_state { struct r300_textures_state {
/* Textures. */ /* Textures. */
struct r300_sampler_view *sampler_views[16]; struct r300_sampler_view *sampler_views[16];
@ -499,6 +521,8 @@ struct r300_context {
struct r300_atom scissor_state; struct r300_atom scissor_state;
/* Sample mask. */ /* Sample mask. */
struct r300_atom sample_mask; struct r300_atom sample_mask;
/* Guard band configuration. */
struct r300_atom guardband_state;
/* Invariant state. This must be emitted to get the engine started. */ /* Invariant state. This must be emitted to get the engine started. */
struct r300_atom invariant_state; struct r300_atom invariant_state;
/* Viewport state. */ /* Viewport state. */
@ -580,6 +604,24 @@ struct r300_context {
bool alpha_to_one; bool alpha_to_one;
bool alpha_to_coverage; bool alpha_to_coverage;
/* 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 (VAP_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 current_clip_discard_distance;
float min_clip_discard_distance_watermark;
unsigned current_rast_prim;
void *dsa_decompress_zmask; void *dsa_decompress_zmask;
struct pipe_vertex_buffer vertex_buffer[PIPE_MAX_ATTRIBS]; struct pipe_vertex_buffer vertex_buffer[PIPE_MAX_ATTRIBS];
@ -709,6 +751,9 @@ void r300_init_render_functions(struct r300_context *r300);
void r300_init_state_functions(struct r300_context* r300); void r300_init_state_functions(struct r300_context* r300);
void r300_init_resource_functions(struct r300_context* r300); void r300_init_resource_functions(struct r300_context* r300);
void r300_update_guardband_state(struct r300_context *r300);
void r300_set_clip_discard_distance(struct r300_context *r300, float distance);
/* r300_blit.c */ /* r300_blit.c */
void r300_decompress_zmask(struct r300_context *r300); void r300_decompress_zmask(struct r300_context *r300);
void r300_decompress_zmask_locked_unsafe(struct r300_context *r300); void r300_decompress_zmask_locked_unsafe(struct r300_context *r300);

View file

@ -859,6 +859,24 @@ void r300_emit_sample_mask(struct r300_context *r300,
END_CS; END_CS;
} }
void r300_emit_guardband_state(struct r300_context *r300,
unsigned size, void *state)
{
struct r300_guardband_state *guard = (struct r300_guardband_state *)state;
CS_LOCALS(r300);
if (!guard)
return;
BEGIN_CS(size);
OUT_CS_REG_SEQ(R300_VAP_GB_VERT_CLIP_ADJ, 4);
OUT_CS_32F(guard->vert_clip);
OUT_CS_32F(guard->vert_disc);
OUT_CS_32F(guard->horz_clip);
OUT_CS_32F(guard->horz_disc);
END_CS;
}
void r300_emit_scissor_state(struct r300_context* r300, void r300_emit_scissor_state(struct r300_context* r300,
unsigned size, void* state) unsigned size, void* state)
{ {

View file

@ -65,6 +65,8 @@ void r300_emit_rs_block_state(struct r300_context* r300,
void r300_emit_sample_mask(struct r300_context *r300, void r300_emit_sample_mask(struct r300_context *r300,
unsigned size, void *state); unsigned size, void *state);
void r300_emit_guardband_state(struct r300_context *r300,
unsigned size, void *state);
void r300_emit_scissor_state(struct r300_context* r300, void r300_emit_scissor_state(struct r300_context* r300,
unsigned size, void* state); unsigned size, void* state);

View file

@ -778,6 +778,27 @@ static unsigned r300_max_vertex_count(struct r300_context *r300)
return result; return result;
} }
static void
r300_update_clip_discard_distance(struct r300_context *r300, unsigned prim)
{
struct r300_rs_state *rs = (struct r300_rs_state*)r300->rs_state.state;
float target_distance = 0.0f;
if (rs) {
if (prim == MESA_PRIM_POINTS)
target_distance = rs->max_point_size;
else if (r300_prim_is_lines(prim))
target_distance = rs->line_width;
}
if (r300->current_rast_prim != prim) {
r300->current_rast_prim = prim;
r300_set_clip_discard_distance(r300, target_distance);
} else if (prim == MESA_PRIM_POINTS || r300_prim_is_lines(prim)) {
r300_set_clip_discard_distance(r300, target_distance);
}
}
static void r300_draw_vbo(struct pipe_context* pipe, static void r300_draw_vbo(struct pipe_context* pipe,
const struct pipe_draw_info *dinfo, const struct pipe_draw_info *dinfo,
@ -800,6 +821,8 @@ static void r300_draw_vbo(struct pipe_context* pipe,
return; return;
} }
r300_update_clip_discard_distance(r300, info.mode);
if (r300->sprite_coord_enable != 0) if (r300->sprite_coord_enable != 0)
if ((info.mode == MESA_PRIM_POINTS) != r300->is_point) { if ((info.mode == MESA_PRIM_POINTS) != r300->is_point) {
r300->is_point = !r300->is_point; r300->is_point = !r300->is_point;

View file

@ -55,6 +55,107 @@ r300_get_scissor_from_viewport(const struct pipe_viewport_state *vp,
scissor->maxy = CLAMP(half_h + vp->translate[1], 0, max_scissor); scissor->maxy = CLAMP(half_h + vp->translate[1], 0, max_scissor);
} }
#define R300_MAX_VIEWPORT_RANGE 16384.0f
void r300_update_guardband_state(struct r300_context *r300)
{
struct r300_guardband_state *guard =
(struct r300_guardband_state *)r300->guardband_state.state;
if (!guard)
return;
const float distance = r300->current_clip_discard_distance;
if (distance == 0.0f) {
if (guard->vert_clip != 1.0f || guard->vert_disc != 1.0f ||
guard->horz_clip != 1.0f || guard->horz_disc != 1.0f) {
DBG(r300, DBG_RS, "r300: guardband reset for zero distance\n");
guard->vert_clip = 1.0f;
guard->vert_disc = 1.0f;
guard->horz_clip = 1.0f;
guard->horz_disc = 1.0f;
r300_mark_atom_dirty(r300, &r300->pvs_flush);
r300_mark_atom_dirty(r300, &r300->guardband_state);
}
return;
}
const struct pipe_viewport_state *vp = &r300->viewport;
float scale_x = fabs(vp->scale[0]);
float scale_y = fabs(vp->scale[1]);
float translate_x = vp->translate[0];
float translate_y = vp->translate[1];
/* Treat a 0x0 viewport as 1x1 to prevent a division by zero. */
if (scale_x == 0.0f)
scale_x = 0.5f;
if (scale_y == 0.0f)
scale_y = 0.5f;
/* Find the biggest guard band that is inside the supported viewport
* range. The guard band is specified as a horizontal and vertical
* distance from (0,0) in clip space.
*
* This is done by applying the inverse viewport transformation
* on the viewport limits to get those limits in clip space.
*
* Use a limit one pixel smaller to allow for some precision error.
*/
const float max_range = R300_MAX_VIEWPORT_RANGE - 1.0f;
float left = (-max_range - translate_x) / scale_x;
float right = (max_range - translate_x) / scale_x;
float top = (-max_range - translate_y) / scale_y;
float bottom = (max_range - translate_y) / scale_y;
assert(left <= -1 && top <= -1 && right >= 1 && bottom >= 1);
float guardband_x = MIN2(-left, right);
float guardband_y = MIN2(-top, bottom);
float discard_x = 1.0f + distance / (2.0f * scale_x);
float discard_y = 1.0f + distance / (2.0f * scale_y);
discard_x = MIN2(discard_x, guardband_x);
discard_y = MIN2(discard_y, guardband_y);
if (guard->vert_clip == guardband_y &&
guard->vert_disc == discard_y &&
guard->horz_clip == guardband_x &&
guard->horz_disc == discard_x) {
return;
}
guard->vert_clip = guardband_y;
guard->vert_disc = discard_y;
guard->horz_clip = guardband_x;
guard->horz_disc = discard_x;
r300_mark_atom_dirty(r300, &r300->pvs_flush);
r300_mark_atom_dirty(r300, &r300->guardband_state);
}
void r300_set_clip_discard_distance(struct r300_context *r300, 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 > r300->min_clip_discard_distance_watermark) {
/* This is based on r600, which is based on Viewperf. The number is in half-pixels. */
r300->min_clip_discard_distance_watermark = MIN2(distance, 6.0f);
}
float new_distance = MAX2(distance, r300->min_clip_discard_distance_watermark);
if (r300->current_clip_discard_distance != new_distance) {
r300->current_clip_discard_distance = new_distance;
}
r300_update_guardband_state(r300);
}
static void r300_delete_vs_state(struct pipe_context* pipe, void* shader); static void r300_delete_vs_state(struct pipe_context* pipe, void* shader);
static void r300_delete_fs_state(struct pipe_context* pipe, void* shader); static void r300_delete_fs_state(struct pipe_context* pipe, void* shader);
@ -1265,6 +1366,7 @@ static void* r300_create_rs_state(struct pipe_context* pipe,
(pack_float_16_6x(state->point_size) << R300_POINTSIZE_X_SHIFT); (pack_float_16_6x(state->point_size) << R300_POINTSIZE_X_SHIFT);
/* Point size clamping. */ /* Point size clamping. */
float max_point_size;
if (state->point_size_per_vertex) { if (state->point_size_per_vertex) {
/* Per-vertex point size. /* Per-vertex point size.
* Clamp to [0, max FB size] */ * Clamp to [0, max FB size] */
@ -1273,6 +1375,7 @@ static void* r300_create_rs_state(struct pipe_context* pipe,
point_minmax = point_minmax =
(pack_float_16_6x(min_psiz) << R300_GA_POINT_MINMAX_MIN_SHIFT) | (pack_float_16_6x(min_psiz) << R300_GA_POINT_MINMAX_MIN_SHIFT) |
(pack_float_16_6x(max_psiz) << R300_GA_POINT_MINMAX_MAX_SHIFT); (pack_float_16_6x(max_psiz) << R300_GA_POINT_MINMAX_MAX_SHIFT);
max_point_size = max_psiz;
} else { } else {
/* We cannot disable the point-size vertex output, /* We cannot disable the point-size vertex output,
* so clamp it. */ * so clamp it. */
@ -1280,6 +1383,7 @@ static void* r300_create_rs_state(struct pipe_context* pipe,
point_minmax = point_minmax =
(pack_float_16_6x(psiz) << R300_GA_POINT_MINMAX_MIN_SHIFT) | (pack_float_16_6x(psiz) << R300_GA_POINT_MINMAX_MIN_SHIFT) |
(pack_float_16_6x(psiz) << R300_GA_POINT_MINMAX_MAX_SHIFT); (pack_float_16_6x(psiz) << R300_GA_POINT_MINMAX_MAX_SHIFT);
max_point_size = psiz;
} }
/* Line control. */ /* Line control. */
@ -1421,6 +1525,9 @@ static void* r300_create_rs_state(struct pipe_context* pipe,
END_CB; END_CB;
} }
rs->max_point_size = max_point_size;
rs->line_width = state->line_width;
return (void*)rs; return (void*)rs;
} }
@ -1485,6 +1592,16 @@ static void r300_bind_rs_state(struct pipe_context* pipe, void* state)
if (last_scissor_enabled != r300->scissor_enabled) { if (last_scissor_enabled != r300->scissor_enabled) {
r300_mark_atom_dirty(r300, &r300->scissor_state); r300_mark_atom_dirty(r300, &r300->scissor_state);
} }
if (rs) {
if (r300->current_rast_prim == MESA_PRIM_POINTS) {
r300_set_clip_discard_distance(r300, rs->max_point_size);
} else if (r300_prim_is_lines(r300->current_rast_prim)) {
r300_set_clip_discard_distance(r300, rs->line_width);
}
} else {
r300_set_clip_discard_distance(r300, 0.0f);
}
} }
/* Free rasterizer state. */ /* Free rasterizer state. */