clutter/stage-cogl: Ensure redraw_clip is a superset of fb_clip_region

Initially we generate the new part of fb_clip_region from the new part
of redraw_clip, scale it up and clamp. But the clamping means the new
part of fb_clip_region might now represent a slightly larger area than
the new part of redraw_clip, by one pixel.

In some rare cases where a foreground actor honours redraw_clip, but
the background actor does not (meaning it might fill all fb_clip_region),
you could find 1px rendering glitches in that gap as the background
actor paints there but the foreground actor does not.

To ensure such glitches can never happen we now regenerate the final
redraw_clip as a clamped superset of the final fb_clip_region. That's
the minimum area we must paint to ensure no gaps appear inside
fb_clip_region.

Although the fix here sounds like the intent of the old code, the old
code forgot to include the new part of fb_clip_region in the clamping
of the final redraw_clip. So the new part of redraw_clip was sometimes
kept too small for the new part of fb_clip_region.

We also move the code to the main path because technically it's also
needed when `has_buffer_age == FALSE`.

Fixes: https://gitlab.gnome.org/GNOME/mutter/-/issues/1500
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1554>
This commit is contained in:
Daniel van Vugt 2020-11-09 15:39:40 +08:00
parent f512d4fefa
commit 88600c8985

View File

@ -549,7 +549,6 @@ clutter_stage_cogl_redraw_view_primary (ClutterStageCogl *stage_cogl,
if (use_clipped_redraw) if (use_clipped_redraw)
{ {
cairo_region_t *fb_damage; cairo_region_t *fb_damage;
cairo_region_t *view_damage;
int age; int age;
fb_damage = cairo_region_create (); fb_damage = cairo_region_create ();
@ -566,15 +565,6 @@ clutter_stage_cogl_redraw_view_primary (ClutterStageCogl *stage_cogl,
/* Update the fb clip region with the extra damage. */ /* Update the fb clip region with the extra damage. */
cairo_region_union (fb_clip_region, fb_damage); cairo_region_union (fb_clip_region, fb_damage);
/* Update the redraw clip with the extra damage done to the view */
view_damage = scale_offset_and_clamp_region (fb_damage,
1.0f / fb_scale,
view_rect.x,
view_rect.y);
cairo_region_union (redraw_clip, view_damage);
cairo_region_destroy (view_damage);
cairo_region_destroy (fb_damage); cairo_region_destroy (fb_damage);
CLUTTER_NOTE (CLIPPING, "Reusing back buffer(age=%d) - repairing region: num rects: %d\n", CLUTTER_NOTE (CLIPPING, "Reusing back buffer(age=%d) - repairing region: num rects: %d\n",
@ -587,6 +577,23 @@ clutter_stage_cogl_redraw_view_primary (ClutterStageCogl *stage_cogl,
clutter_damage_history_step (view_priv->damage_history); clutter_damage_history_step (view_priv->damage_history);
} }
if (use_clipped_redraw)
{
/* Regenerate redraw_clip because:
* 1. It's missing the regions added from damage_history above; and
* 2. If using fractional scaling then it might be a fraction of a
* logical pixel (or one physical pixel) smaller than
* fb_clip_region, due to the clamping from
* offset_scale_and_clamp_region. So we need to ensure redraw_clip
* is a superset of fb_clip_region to avoid such gaps.
*/
cairo_region_destroy (redraw_clip);
redraw_clip = scale_offset_and_clamp_region (fb_clip_region,
1.0 / fb_scale,
view_rect.x,
view_rect.y);
}
if (clutter_paint_debug_flags & CLUTTER_DEBUG_PAINT_DAMAGE_REGION) if (clutter_paint_debug_flags & CLUTTER_DEBUG_PAINT_DAMAGE_REGION)
{ {
cairo_region_t *debug_redraw_clip; cairo_region_t *debug_redraw_clip;