[mutter] clutter/actor: Add CLUTTER_OFFSCREEN_REDIRECT_ON_IDLE



commit 282b09c17ef366aa49ae83950f087c1592b081d5
Author: Daniel van Vugt <daniel van vugt canonical com>
Date:   Wed Feb 19 14:05:57 2020 +0800

    clutter/actor: Add CLUTTER_OFFSCREEN_REDIRECT_ON_IDLE
    
    Which offscreens actor rendering only in cases where it hasn't changed for
    2 frames or more. This avoids the performance penalty of offscreening an
    actor whose content is trying to animate at full frame rate. It will
    switch automatically.
    
    https://gitlab.gnome.org/GNOME/mutter/merge_requests/1069

 clutter/clutter/clutter-actor.c                    | 13 +++-
 clutter/clutter/clutter-enums.h                    | 14 +++-
 clutter/clutter/clutter-offscreen-effect.c         |  7 ++
 .../clutter/conform/actor-offscreen-redirect.c     | 88 ++++++++++++++++++++++
 4 files changed, 118 insertions(+), 4 deletions(-)
---
diff --git a/clutter/clutter/clutter-actor.c b/clutter/clutter/clutter-actor.c
index 163d30d43..728fd5cca 100644
--- a/clutter/clutter/clutter-actor.c
+++ b/clutter/clutter/clutter-actor.c
@@ -3743,7 +3743,13 @@ needs_flatten_effect (ClutterActor *self)
                   CLUTTER_DEBUG_DISABLE_OFFSCREEN_REDIRECT))
     return FALSE;
 
-  if (priv->offscreen_redirect & CLUTTER_OFFSCREEN_REDIRECT_ALWAYS)
+  /* We need to enable the effect immediately even in ON_IDLE because that can
+   * only be implemented efficiently within the effect itself.
+   * If it was implemented here using just priv->is_dirty then we would lose
+   * the ability to animate opacity without repaints.
+   */
+  if ((priv->offscreen_redirect & CLUTTER_OFFSCREEN_REDIRECT_ALWAYS) ||
+      (priv->offscreen_redirect & CLUTTER_OFFSCREEN_REDIRECT_ON_IDLE))
     return TRUE;
   else if (priv->offscreen_redirect & CLUTTER_OFFSCREEN_REDIRECT_AUTOMATIC_FOR_OPACITY)
     {
@@ -4201,6 +4207,11 @@ clutter_actor_continue_paint (ClutterActor        *self,
             run_flags |= CLUTTER_EFFECT_PAINT_ACTOR_DIRTY;
         }
 
+      if (priv->current_effect == priv->flatten_effect &&
+          priv->offscreen_redirect & CLUTTER_OFFSCREEN_REDIRECT_ON_IDLE &&
+          run_flags & CLUTTER_EFFECT_PAINT_ACTOR_DIRTY)
+        run_flags |= CLUTTER_EFFECT_PAINT_BYPASS_EFFECT;
+
       _clutter_effect_paint (priv->current_effect, paint_context, run_flags);
 
       priv->current_effect = old_current_effect;
diff --git a/clutter/clutter/clutter-enums.h b/clutter/clutter/clutter-enums.h
index d53b7ab25..ed178762b 100644
--- a/clutter/clutter/clutter-enums.h
+++ b/clutter/clutter/clutter-enums.h
@@ -538,6 +538,10 @@ typedef enum /*< prefix=CLUTTER_ACTOR >*/
  *   virtual returns %TRUE.
  * @CLUTTER_OFFSCREEN_REDIRECT_ALWAYS: Always redirect the actor to an
  *   offscreen buffer even if it is fully opaque.
+ * @CLUTTER_OFFSCREEN_REDIRECT_ON_IDLE: Only redirect the actor if it is the
+ *   most efficient thing to do based on its recent repaint behaviour. That
+ *   means when its contents are changing less frequently than it's being used
+ *   on stage.
  *
  * Possible flags to pass to clutter_actor_set_offscreen_redirect().
  *
@@ -545,8 +549,9 @@ typedef enum /*< prefix=CLUTTER_ACTOR >*/
  */
 typedef enum /*< prefix=CLUTTER_OFFSCREEN_REDIRECT >*/
 {
-  CLUTTER_OFFSCREEN_REDIRECT_AUTOMATIC_FOR_OPACITY = 1<<0,
-  CLUTTER_OFFSCREEN_REDIRECT_ALWAYS = 1<<1
+  CLUTTER_OFFSCREEN_REDIRECT_AUTOMATIC_FOR_OPACITY = 1 << 0,
+  CLUTTER_OFFSCREEN_REDIRECT_ALWAYS                = 1 << 1,
+  CLUTTER_OFFSCREEN_REDIRECT_ON_IDLE               = 1 << 2
 } ClutterOffscreenRedirect;
 
 /**
@@ -673,12 +678,15 @@ typedef enum /*< prefix=CLUTTER_BIND >*/
  *   has queued a redraw before this paint. This implies that the effect
  *   should call clutter_actor_continue_paint() to chain to the next
  *   effect and can not cache any results from a previous paint.
+ * @CLUTTER_EFFECT_PAINT_BYPASS_EFFECT: The effect should not be used
+ *   on this frame, but it will be asked to paint the actor still.
  *
  * Flags passed to the ‘paint’ or ‘pick’ method of #ClutterEffect.
  */
 typedef enum /*< prefix=CLUTTER_EFFECT_PAINT >*/
 {
-  CLUTTER_EFFECT_PAINT_ACTOR_DIRTY = (1 << 0)
+  CLUTTER_EFFECT_PAINT_ACTOR_DIRTY   = (1 << 0),
+  CLUTTER_EFFECT_PAINT_BYPASS_EFFECT = (1 << 1)
 } ClutterEffectPaintFlags;
 
 /**
diff --git a/clutter/clutter/clutter-offscreen-effect.c b/clutter/clutter/clutter-offscreen-effect.c
index 89467e541..ac4ea98e2 100644
--- a/clutter/clutter/clutter-offscreen-effect.c
+++ b/clutter/clutter/clutter-offscreen-effect.c
@@ -469,6 +469,13 @@ clutter_offscreen_effect_paint (ClutterEffect           *effect,
   ClutterOffscreenEffect *self = CLUTTER_OFFSCREEN_EFFECT (effect);
   ClutterOffscreenEffectPrivate *priv = self->priv;
 
+  if (flags & CLUTTER_EFFECT_PAINT_BYPASS_EFFECT)
+    {
+      clutter_actor_continue_paint (priv->actor, paint_context);
+      cogl_clear_object (&priv->offscreen);
+      return;
+    }
+
   /* If we've already got a cached image and the actor hasn't been redrawn
    * then we can just use the cached image in the FBO.
    */
diff --git a/src/tests/clutter/conform/actor-offscreen-redirect.c 
b/src/tests/clutter/conform/actor-offscreen-redirect.c
index 820ccb4b4..a3faaabfc 100644
--- a/src/tests/clutter/conform/actor-offscreen-redirect.c
+++ b/src/tests/clutter/conform/actor-offscreen-redirect.c
@@ -193,6 +193,9 @@ verify_redraws (gpointer user_data)
 {
   Data *data = user_data;
 
+  clutter_actor_set_offscreen_redirect (data->container,
+                                        CLUTTER_OFFSCREEN_REDIRECT_ALWAYS);
+
   /* Queueing a redraw on the actor should cause a redraw */
   clutter_actor_queue_redraw (data->container);
   verify_redraw (data, 1);
@@ -220,6 +223,7 @@ static gboolean
 run_verify (gpointer user_data)
 {
   Data *data = user_data;
+  int i;
 
   group_has_overlaps = FALSE;
 
@@ -313,6 +317,90 @@ run_verify (gpointer user_data)
                   0,
                   255);
 
+  /* ON_IDLE: Defer redirection through the FBO until it is deemed to be the
+   * best performing option, which means when the actor's contents have
+   * stopped changing.
+   */
+  clutter_actor_set_offscreen_redirect (data->container,
+                                        CLUTTER_OFFSCREEN_REDIRECT_ON_IDLE);
+
+  /* Changing modes should not incur a redraw */
+  verify_results (data,
+                  255, 0, 0,
+                  0,
+                  255);
+
+  /* These will incur a redraw because the actor is dirty: */
+  for (i = 0; i < 10; i++)
+    {
+      clutter_actor_queue_redraw (data->container);
+      verify_results (data,
+                      255, 0, 0,
+                      1,
+                      255);
+    }
+
+  /* The actor is not dirty, but also not yet cached so a redraw is expected */
+  verify_results (data,
+                  255, 0, 0,
+                  1,
+                  255);
+
+  /* These will NOT incur a redraw because the actor is unchanged: */
+  for (i = 0; i < 10; i++)
+    {
+      verify_results (data,
+                      255, 0, 0,
+                      0,
+                      255);
+    }
+
+  /* The first opacity change should require no redaw */
+  clutter_actor_set_opacity (data->container, 64);
+  verify_results (data,
+                  255, 191, 191,
+                  0,
+                  255);
+
+  /* The second opacity change should require no redaw */
+  clutter_actor_set_opacity (data->container, 127);
+  verify_results (data,
+                  255, 127, 127,
+                  0,
+                  255);
+
+  /* The third opacity change should require no redaw */
+  clutter_actor_set_opacity (data->container, 255);
+  verify_results (data,
+                  255, 0, 0,
+                  0,
+                  255);
+
+  /* Now several frames without the actor changing AND the FBO is populated.
+   * Expect no internal repaints.
+   */
+  for (i = 0; i < 10; i++)
+    {
+      verify_results (data,
+                      255, 0, 0,
+                      0,
+                      255);
+    }
+
+  /* Another opacity change, no redraw expected */
+  clutter_actor_set_opacity (data->container, 127);
+  verify_results (data,
+                  255, 127, 127,
+                  0,
+                  255);
+
+  /* Finally the actor's content changes so a redraw is expected */
+  clutter_actor_queue_redraw (data->container);
+  verify_results (data,
+                  255, 127, 127,
+                  1,
+                  127);
+
   /* Check redraws */
   g_idle_add (verify_redraws, data);
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]