[mutter] clutter/actor: Add CLUTTER_OFFSCREEN_REDIRECT_ON_IDLE
- From: Jonas Ådahl <jadahl src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [mutter] clutter/actor: Add CLUTTER_OFFSCREEN_REDIRECT_ON_IDLE
- Date: Fri, 21 Feb 2020 10:21:57 +0000 (UTC)
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]