clutter/stage-view: Defer and accumulate redraw-clip on scanout

When taking the scanout path we still want to clear the
redraw-clip from the stage-view in order to ensure we skip
frames in `handle_frame_clock_frame()` if no new redraw-clip
was recorded.
This was not done previously as the accumulated redraw-clip was
needed for the next repaint, likely under the assumption that
scheduling a scanout repeatedly would be computationally cost-free.
This assumption does not hold in a VRR world.

In order to archive both, an accumulated redraw-clip for the next
paint and frame-skipping during scanout, introduce new API to defer
and accumulate redraw-clips until the next repaint.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2480>
This commit is contained in:
Robert Mader 2022-06-25 22:14:41 +02:00 committed by Robert Mader
parent 34475e7e98
commit 550f09a5e7
3 changed files with 73 additions and 11 deletions

View File

@ -59,6 +59,12 @@ const cairo_region_t * clutter_stage_view_peek_redraw_clip (ClutterStageView *vi
CLUTTER_EXPORT
cairo_region_t * clutter_stage_view_take_redraw_clip (ClutterStageView *view);
CLUTTER_EXPORT
cairo_region_t * clutter_stage_view_take_accumulated_redraw_clip (ClutterStageView *view);
CLUTTER_EXPORT
void clutter_stage_view_accumulate_redraw_clip (ClutterStageView *view);
CLUTTER_EXPORT
CoglScanout * clutter_stage_view_take_scanout (ClutterStageView *view);

View File

@ -78,6 +78,8 @@ typedef struct _ClutterStageViewPrivate
gboolean has_redraw_clip;
cairo_region_t *redraw_clip;
gboolean has_accumulated_redraw_clip;
cairo_region_t *accumulated_redraw_clip;
float refresh_rate;
int64_t vblank_duration_us;
@ -920,6 +922,23 @@ clutter_stage_view_get_offscreen_transformation_matrix (ClutterStageView *view,
view_class->get_offscreen_transformation_matrix (view, matrix);
}
static void
maybe_mark_full_redraw (ClutterStageView *view,
cairo_region_t **region)
{
ClutterStageViewPrivate *priv =
clutter_stage_view_get_instance_private (view);
if (cairo_region_num_rectangles (*region) == 1)
{
cairo_rectangle_int_t region_extents;
cairo_region_get_extents (*region, &region_extents);
if (clutter_util_rectangle_equal (&priv->layout, &region_extents))
g_clear_pointer (region, cairo_region_destroy);
}
}
void
clutter_stage_view_add_redraw_clip (ClutterStageView *view,
const cairo_rectangle_int_t *clip)
@ -948,15 +967,7 @@ clutter_stage_view_add_redraw_clip (ClutterStageView *view,
else
{
cairo_region_union_rectangle (priv->redraw_clip, clip);
if (cairo_region_num_rectangles (priv->redraw_clip) == 1)
{
cairo_rectangle_int_t redraw_clip_extents;
cairo_region_get_extents (priv->redraw_clip, &redraw_clip_extents);
if (clutter_util_rectangle_equal (&priv->layout, &redraw_clip_extents))
g_clear_pointer (&priv->redraw_clip, cairo_region_destroy);
}
maybe_mark_full_redraw (view, &priv->redraw_clip);
}
priv->has_redraw_clip = TRUE;
@ -1000,6 +1011,47 @@ clutter_stage_view_take_redraw_clip (ClutterStageView *view)
return g_steal_pointer (&priv->redraw_clip);
}
cairo_region_t *
clutter_stage_view_take_accumulated_redraw_clip (ClutterStageView *view)
{
ClutterStageViewPrivate *priv =
clutter_stage_view_get_instance_private (view);
g_return_val_if_fail (priv->has_redraw_clip, NULL);
clutter_stage_view_accumulate_redraw_clip (view);
priv->has_accumulated_redraw_clip = FALSE;
return g_steal_pointer (&priv->accumulated_redraw_clip);
}
void
clutter_stage_view_accumulate_redraw_clip (ClutterStageView *view)
{
ClutterStageViewPrivate *priv =
clutter_stage_view_get_instance_private (view);
g_return_if_fail (priv->has_redraw_clip);
if (priv->redraw_clip && priv->accumulated_redraw_clip)
{
cairo_region_union (priv->accumulated_redraw_clip, priv->redraw_clip);
maybe_mark_full_redraw (view, &priv->accumulated_redraw_clip);
}
else if (priv->redraw_clip && !priv->has_accumulated_redraw_clip)
{
priv->accumulated_redraw_clip = g_steal_pointer (&priv->redraw_clip);
}
else
{
g_clear_pointer (&priv->accumulated_redraw_clip, cairo_region_destroy);
}
g_clear_pointer (&priv->redraw_clip, cairo_region_destroy);
priv->has_accumulated_redraw_clip = TRUE;
priv->has_redraw_clip = FALSE;
}
static void
clutter_stage_default_get_offscreen_transformation_matrix (ClutterStageView *view,
graphene_matrix_t *matrix)
@ -1400,6 +1452,7 @@ clutter_stage_view_dispose (GObject *object)
g_clear_object (&priv->offscreen);
g_clear_pointer (&priv->offscreen_pipeline, cogl_object_unref);
g_clear_pointer (&priv->redraw_clip, cairo_region_destroy);
g_clear_pointer (&priv->accumulated_redraw_clip, cairo_region_destroy);
g_clear_pointer (&priv->frame_clock, clutter_frame_clock_destroy);
G_OBJECT_CLASS (clutter_stage_view_parent_class)->dispose (object);

View File

@ -473,7 +473,7 @@ meta_stage_impl_redraw_view_primary (MetaStageImpl *stage_impl,
COGL_IS_ONSCREEN (onscreen) &&
cogl_clutter_winsys_has_feature (COGL_WINSYS_FEATURE_BUFFER_AGE);
redraw_clip = clutter_stage_view_take_redraw_clip (stage_view);
redraw_clip = clutter_stage_view_take_accumulated_redraw_clip (stage_view);
/* NB: a NULL redraw clip == full stage redraw */
if (!redraw_clip)
@ -725,7 +725,10 @@ meta_stage_impl_redraw_view (ClutterStageWindow *stage_window,
scanout,
frame,
&error))
return;
{
clutter_stage_view_accumulate_redraw_clip (stage_view);
return;
}
if (!g_error_matches (error,
COGL_SCANOUT_ERROR,