From 550f09a5e7c24731eee6f232f84ec32af64dd5ca Mon Sep 17 00:00:00 2001 From: Robert Mader Date: Sat, 25 Jun 2022 22:14:41 +0200 Subject: [PATCH] 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: --- clutter/clutter/clutter-stage-view-private.h | 6 ++ clutter/clutter/clutter-stage-view.c | 71 +++++++++++++++++--- src/backends/meta-stage-impl.c | 7 +- 3 files changed, 73 insertions(+), 11 deletions(-) diff --git a/clutter/clutter/clutter-stage-view-private.h b/clutter/clutter/clutter-stage-view-private.h index 39d8601ea..309dde496 100644 --- a/clutter/clutter/clutter-stage-view-private.h +++ b/clutter/clutter/clutter-stage-view-private.h @@ -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); diff --git a/clutter/clutter/clutter-stage-view.c b/clutter/clutter/clutter-stage-view.c index 2e47237f0..22c764799 100644 --- a/clutter/clutter/clutter-stage-view.c +++ b/clutter/clutter/clutter-stage-view.c @@ -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, ®ion_extents); + if (clutter_util_rectangle_equal (&priv->layout, ®ion_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); diff --git a/src/backends/meta-stage-impl.c b/src/backends/meta-stage-impl.c index c45aaf852..d562603c3 100644 --- a/src/backends/meta-stage-impl.c +++ b/src/backends/meta-stage-impl.c @@ -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,