From c3aa4d24bf32a7aacdcd96b0807ba780738431a1 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Wed, 2 Mar 2011 16:55:10 +0000 Subject: [PATCH] clutter-effect: Add a 'run' virtual This adds a new virtual to ClutterEffect which is intended to be a more flexible replacement for the pre and post_paint functions. The implementation of a run virtual would look something like this: void effect_run (ClutterEffect *effect, ClutterEffectRunFlags flags) { /* Set up state */ /* ... */ /* Chain to the next item in the paint sequence */ clutter_actor_continue_paint (priv->actor); /* Clean up state */ /* ... */ } ClutterActor now just calls this virtual instead of the pre_paint and post_paint functions. It keeps track of the next effect in the list so that it knows what to do when clutter_actor_continue_paint is called. clutter_actor_continue_paint is a new function added just for implementing effects. The default implementation of the run virtual just calls pre_paint and post_paint so that existing effects will continue to work. An effect is allowed to conditionally skip calling clutter_actor_continue_paint(). This is useful to implement effects that cache the image of an actor. The flags parameter can be used to determine if the actor is dirty since the last paint. ClutterActor sets this flag whenever propagated_one_redraw is TRUE which means that a redraw for this actor or one of its children was queued. --- clutter/clutter-actor.c | 140 +++++++++++---------- clutter/clutter-actor.h | 1 + clutter/clutter-effect-private.h | 2 + clutter/clutter-effect.c | 124 +++++++++++++----- clutter/clutter-effect.h | 19 ++- doc/reference/clutter/clutter-sections.txt | 2 + 6 files changed, 189 insertions(+), 99 deletions(-) diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index 138e9cfe1..5395faf03 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -476,6 +476,11 @@ struct _ClutterActorPrivate ClutterPaintVolume paint_volume; + /* This is used when painting effects to implement the + clutter_actor_continue_paint() function. It points to the node in + the list of effects that is next in the chain */ + const GList *next_effect_to_paint; + /* NB: This volume isn't relative to this actor, it is in eye * coordinates so that it can remain valid after the actor changes. */ @@ -2342,60 +2347,6 @@ _clutter_actor_apply_modelview_transform (ClutterActor *self, CLUTTER_ACTOR_GET_CLASS (self)->apply_transform (self, matrix); } -static gboolean -_clutter_actor_effects_pre_paint (ClutterActor *self) -{ - ClutterActorPrivate *priv = self->priv; - const GList *effects, *l; - gboolean was_pre_painted = FALSE; - - priv->current_effect = NULL; - - effects = _clutter_meta_group_peek_metas (priv->effects); - for (l = effects; l != NULL; l = l->next) - { - ClutterEffect *effect = l->data; - ClutterActorMeta *meta = l->data; - - if (!clutter_actor_meta_get_enabled (meta)) - continue; - - priv->current_effect = l->data; - - was_pre_painted |= _clutter_effect_pre_paint (effect); - } - - priv->current_effect = NULL; - - return was_pre_painted; -} - -static void -_clutter_actor_effects_post_paint (ClutterActor *self) -{ - ClutterActorPrivate *priv = self->priv; - const GList *effects, *l; - - priv->current_effect = NULL; - - /* we walk the list backwards, to unwind the post-paint order */ - effects = _clutter_meta_group_peek_metas (priv->effects); - for (l = g_list_last ((GList *) effects); l != NULL; l = l->prev) - { - ClutterEffect *effect = l->data; - ClutterActorMeta *meta = l->data; - - if (!clutter_actor_meta_get_enabled (meta)) - continue; - - priv->current_effect = l->data; - - _clutter_effect_post_paint (effect); - } - - priv->current_effect = NULL; -} - /* Recursively applies the transforms associated with this actor and * its ancestors to the given matrix. Use NULL if you want this * to go all the way down to the stage. @@ -2780,8 +2731,6 @@ clutter_actor_paint (ClutterActor *self) if (pick_mode == CLUTTER_PICK_NONE) { - gboolean effect_painted = FALSE; - CLUTTER_COUNTER_INC (_clutter_uprof_context, actor_paint_counter); /* We save the current paint volume so that the next time the @@ -2822,17 +2771,20 @@ clutter_actor_paint (ClutterActor *self) goto done; } - if (priv->effects != NULL) - effect_painted = _clutter_actor_effects_pre_paint (self); - else if (actor_has_shader_data (self)) - clutter_actor_shader_pre_paint (self, FALSE); + if (priv->effects == NULL) + { + if (actor_has_shader_data (self)) + clutter_actor_shader_pre_paint (self, FALSE); + priv->next_effect_to_paint = NULL; + } + else + priv->next_effect_to_paint = + _clutter_meta_group_peek_metas (priv->effects); - priv->propagated_one_redraw = FALSE; - g_signal_emit (self, actor_signals[PAINT], 0); + clutter_actor_continue_paint (self); - if (effect_painted) - _clutter_actor_effects_post_paint (self); - else if (actor_has_shader_data (self)) + if (priv->effects == NULL && + actor_has_shader_data (self)) clutter_actor_shader_post_paint (self); if (G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_PAINT_VOLUMES)) @@ -2863,6 +2815,64 @@ done: CLUTTER_UNSET_PRIVATE_FLAGS (self, CLUTTER_IN_PAINT); } +/** + * clutter_actor_continue_paint: + * @self: A #ClutterActor + * + * Run the next stage of the paint sequence. This function should only + * be called within the implementation of the ‘run’ virtual of a + * #ClutterEffect. It will cause the run method of the next effect to + * be applied, or it will paint the actual actor if the current effect + * is the last effect in the chain. + * + * Since: 1.8 + */ +void +clutter_actor_continue_paint (ClutterActor *self) +{ + ClutterActorPrivate *priv; + + g_return_if_fail (CLUTTER_IS_ACTOR (self)); + /* This should only be called from with in the ‘run’ implementation + of a ClutterEffect */ + g_return_if_fail (CLUTTER_ACTOR_IN_PAINT (self)); + + priv = self->priv; + + /* Skip any effects that are disabled */ + while (priv->next_effect_to_paint && + !clutter_actor_meta_get_enabled (priv->next_effect_to_paint->data)) + priv->next_effect_to_paint = priv->next_effect_to_paint->next; + + /* If this has come from the last effect then we'll just paint the + actual actor */ + if (priv->next_effect_to_paint == NULL) + { + priv->propagated_one_redraw = FALSE; + + g_signal_emit (self, actor_signals[PAINT], 0); + } + else + { + ClutterActorMeta *old_current_effect; + ClutterEffectRunFlags run_flags = 0; + + /* Cache the current effect so that we can put it back before + returning */ + old_current_effect = priv->current_effect; + + priv->current_effect = priv->next_effect_to_paint->data; + priv->next_effect_to_paint = priv->next_effect_to_paint->next; + + if (priv->propagated_one_redraw) + run_flags |= CLUTTER_EFFECT_RUN_ACTOR_DIRTY; + + _clutter_effect_run (CLUTTER_EFFECT (priv->current_effect), run_flags); + + priv->current_effect = old_current_effect; + } +} + /* internal helper function set the rotation angle without affecting the center point */ diff --git a/clutter/clutter-actor.h b/clutter/clutter-actor.h index adc013aa8..32096175f 100644 --- a/clutter/clutter-actor.h +++ b/clutter/clutter-actor.h @@ -314,6 +314,7 @@ void clutter_actor_unrealize (ClutterActor void clutter_actor_map (ClutterActor *self); void clutter_actor_unmap (ClutterActor *self); void clutter_actor_paint (ClutterActor *self); +void clutter_actor_continue_paint (ClutterActor *self); void clutter_actor_queue_redraw (ClutterActor *self); void clutter_actor_queue_relayout (ClutterActor *self); diff --git a/clutter/clutter-effect-private.h b/clutter/clutter-effect-private.h index 8b0153cef..94a69ff28 100644 --- a/clutter/clutter-effect-private.h +++ b/clutter/clutter-effect-private.h @@ -9,6 +9,8 @@ gboolean _clutter_effect_pre_paint (ClutterEffect *eff void _clutter_effect_post_paint (ClutterEffect *effect); gboolean _clutter_effect_get_paint_volume (ClutterEffect *effect, ClutterPaintVolume *volume); +void _clutter_effect_run (ClutterEffect *effect, + ClutterEffectRunFlags flags); G_END_DECLS diff --git a/clutter/clutter-effect.c b/clutter/clutter-effect.c index cf8775d02..95550d0c4 100644 --- a/clutter/clutter-effect.c +++ b/clutter/clutter-effect.c @@ -38,34 +38,74 @@ * * * Implementing a ClutterEffect - * Creating a sub-class of #ClutterEffect requires the implementation - * of two virtual functions: + * + * Creating a sub-class of #ClutterEffect requires overriding the + * ‘run’ method. The implementation of the function should look + * something like this: + * + * + * void effect_run (ClutterEffect *effect, ClutterEffectRunFlags flags) + * { + * /* Set up initialisation of the paint such as binding a + * CoglOffscreen or other operations */ + * + * /* Chain to the next item in the paint sequence. This will either call + * ‘run’ on the next effect or just paint the actor if this is + * the last effect. */ + * ClutterActor *actor = + * clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (effect)); + * clutter_actor_continue_paint (actor); + * + * /* perform any cleanup of state, such as popping the + * CoglOffscreen */ + * } + * + * + * The effect can optionally avoid calling + * clutter_actor_continue_paint() to skip any further stages of + * the paint sequence. This is useful for example if the effect + * contains a cached image of the actor. In that case it can + * optimise painting by avoiding the actor paint and instead + * painting the cached image. The %CLUTTER_EFFECT_RUN_ACTOR_DIRTY + * flag is useful in this case. Clutter will set this flag when a + * redraw has been queued on the actor since it was last + * painted. The effect can use this information to decide if the + * cached image is still valid. + * + * + * The ‘run’ virtual was added in Clutter 1.8. Prior to that there + * were two separate functions as follows. + * * * pre_paint(), which is called * before painting the #ClutterActor. * post_paint(), which is called * after painting the #ClutterActor. * - * The pre_paint() function should be used to set + * The pre_paint() function was used to set * up the #ClutterEffect right before the #ClutterActor's paint * sequence. This function can fail, and return %FALSE; in that case, no * post_paint() invocation will follow. - * The post_paint() function is called after the + * The post_paint() function was called after the * #ClutterActor's paint sequence. - * The pre_paint() phase could be seen as a custom - * handler to the #ClutterActor::paint signal, while the - * post_paint() phase could be seen as a custom handler - * to the #ClutterActor::paint signal connected using - * g_signal_connect_after(). + * + * With these two functions it is not possible to skip the rest of + * the paint sequence. The default implementation of the ‘run’ + * virtual calls pre_paint(), clutter_actor_continue_paint() and + * then post_paint() so that existing actors that aren't using the + * run virtual will continue to work. New actors using the run + * virtual do not need to implement pre or post paint. + * * * A simple ClutterEffect implementation - * The example below creates two rectangles: one will be painted - * "behind" the actor, while another will be painted "on top" of the actor. - * The set_actor() implementation will create the two - * materials used for the two different rectangles; the - * pre_paint() function will paint the first material - * using cogl_rectangle(), while the post_paint() - * phase will paint the second material. + * The example below creates two rectangles: one will be + * painted "behind" the actor, while another will be painted "on + * top" of the actor. The set_actor() + * implementation will create the two materials used for the two + * different rectangles; the run() function + * will paint the first material using cogl_rectangle(), before + * continuing and then it will paint paint the second material + * after. * * typedef struct { * ClutterEffect parent_instance; @@ -116,31 +156,19 @@ * } * * static gboolean - * my_effect_pre_paint (ClutterEffect *effect) + * my_effect_run (ClutterEffect *effect) * { * MyEffect *self = MY_EFFECT (effect); * gfloat width, height; * - * /* If we were disabled we don't need to paint anything */ - * if (!clutter_actor_meta_get_enabled (CLUTTER_ACTOR_META (effect))) - * return FALSE; - * * clutter_actor_get_size (self->actor, &width, &height); * * /* Paint the first rectangle in the upper left quadrant */ * cogl_set_source (self->rect_1); * cogl_rectangle (0, 0, width / 2, height / 2); * - * return TRUE; - * } - * - * static void - * my_effect_post_paint (ClutterEffect *effect) - * { - * MyEffect *self = MY_EFFECT (effect); - * gfloat width, height; - * - * clutter_actor_get_size (self->actor, &width, &height); + * /* Continue to the rest of the paint sequence */ + * clutter_actor_continue_paint (self->actor); * * /* Paint the second rectangle in the lower right quadrant */ * cogl_set_source (self->rect_2); @@ -154,8 +182,7 @@ * * meta_class->set_actor = my_effect_set_actor; * - * klass->pre_paint = my_effect_pre_paint; - * klass->post_paint = my_effect_post_paint; + * klass->run = my_effect_run; * } * * @@ -199,6 +226,27 @@ clutter_effect_real_get_paint_volume (ClutterEffect *effect, return TRUE; } +static void +clutter_effect_real_run (ClutterEffect *effect, + ClutterEffectRunFlags flags) +{ + ClutterActorMeta *actor_meta = CLUTTER_ACTOR_META (effect); + ClutterActor *actor; + gboolean pre_paint_succeeded; + + /* The default implementation provides a compatibility wrapper for + effects that haven't migrated to use the 'run' virtual yet. This + just calls the old pre and post virtuals before chaining on */ + + pre_paint_succeeded = _clutter_effect_pre_paint (effect); + + actor = clutter_actor_meta_get_actor (actor_meta); + clutter_actor_continue_paint (actor); + + if (pre_paint_succeeded) + _clutter_effect_post_paint (effect); +} + static void clutter_effect_notify (GObject *gobject, GParamSpec *pspec) @@ -226,6 +274,7 @@ clutter_effect_class_init (ClutterEffectClass *klass) klass->pre_paint = clutter_effect_real_pre_paint; klass->post_paint = clutter_effect_real_post_paint; klass->get_paint_volume = clutter_effect_real_get_paint_volume; + klass->run = clutter_effect_real_run; } static void @@ -249,6 +298,15 @@ _clutter_effect_post_paint (ClutterEffect *effect) CLUTTER_EFFECT_GET_CLASS (effect)->post_paint (effect); } +void +_clutter_effect_run (ClutterEffect *effect, + ClutterEffectRunFlags flags) +{ + g_return_if_fail (CLUTTER_IS_EFFECT (effect)); + + CLUTTER_EFFECT_GET_CLASS (effect)->run (effect, flags); +} + gboolean _clutter_effect_get_paint_volume (ClutterEffect *effect, ClutterPaintVolume *volume) diff --git a/clutter/clutter-effect.h b/clutter/clutter-effect.h index 422c89af5..1e2c124f2 100644 --- a/clutter/clutter-effect.h +++ b/clutter/clutter-effect.h @@ -42,6 +42,20 @@ G_BEGIN_DECLS typedef struct _ClutterEffectClass ClutterEffectClass; +/** + * ClutterEffectRunFlags: + * @CLUTTER_EFFECT_RUN_ACTOR_DIRTY: The actor or one of its children + * 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. + * + * Flags passed to the ‘run’ method of #ClutterEffect. + */ +typedef enum +{ + CLUTTER_EFFECT_RUN_ACTOR_DIRTY = (1 << 0) +} ClutterEffectRunFlags; + /** * ClutterEffect: * @@ -61,6 +75,7 @@ struct _ClutterEffect * @pre_paint: virtual function * @post_paint: virtual function * @get_paint_volume: virtual function + * @run: virtual function * * The #ClutterEffectClass structure contains only private data * @@ -78,8 +93,10 @@ struct _ClutterEffectClass gboolean (* get_paint_volume) (ClutterEffect *effect, ClutterPaintVolume *volume); + void (* run) (ClutterEffect *effect, + ClutterEffectRunFlags flags); + /*< private >*/ - void (* _clutter_effect2) (void); void (* _clutter_effect3) (void); void (* _clutter_effect4) (void); void (* _clutter_effect5) (void); diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index 3bad5941e..0c91b9326 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -291,6 +291,7 @@ clutter_actor_hide_all clutter_actor_realize clutter_actor_unrealize clutter_actor_paint +clutter_actor_continue_paint clutter_actor_queue_redraw clutter_actor_queue_relayout clutter_actor_destroy @@ -2467,6 +2468,7 @@ ClutterClickActionPrivate clutter-effect ClutterEffect ClutterEffectClass +ClutterEffectRunFlags CLUTTER_TYPE_EFFECT CLUTTER_EFFECT