[clutter] Give a chance to effects for running when picking



commit 0ede622f5132843bf1c1dc88d9322a5ca514790e
Author: Tomeu Vizoso <tomeu vizoso collabora co uk>
Date:   Thu Jun 2 14:16:23 2011 +0200

    Give a chance to effects for running when picking
    
    Some effects can change the actor's shape and position, so they need
    to run when picking.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=651700

 clutter/clutter-actor.c          |  169 +++++++++++++++++---------------
 clutter/clutter-effect-private.h |    2 +
 clutter/clutter-effect.c         |   21 ++++
 clutter/clutter-effect.h         |    6 +-
 tests/conform/test-pick.c        |  200 +++++++++++++++++++++++++++++---------
 5 files changed, 273 insertions(+), 125 deletions(-)
---
diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c
index 7835648..fcea9b4 100644
--- a/clutter/clutter-actor.c
+++ b/clutter/clutter-actor.c
@@ -2865,78 +2865,66 @@ clutter_actor_paint (ClutterActor *self)
          applications to notify when the value of the
          has_overlaps virtual changes. */
       add_or_remove_flatten_effect (self);
+    }
+  else
+    CLUTTER_COUNTER_INC (_clutter_uprof_context, actor_pick_counter);
 
-      /* We save the current paint volume so that the next time the
-       * actor queues a redraw we can constrain the redraw to just
-       * cover the union of the new bounding box and the old.
-       *
-       * We also fetch the current paint volume to perform culling so
-       * we can avoid painting actors outside the current clip region.
-       *
-       * If we are painting inside a clone, we should neither update
-       * the paint volume or use it to cull painting, since the paint
-       * box represents the location of the source actor on the
-       * screen.
-       *
-       * XXX: We are starting to do a lot of vertex transforms on
-       * the CPU in a typical paint, so at some point we should
-       * audit these and consider caching some things.
-       */
-      if (!in_clone_paint ())
-        {
-          gboolean success;
-          /* annoyingly gcc warns if uninitialized even though
-           * the initialization is redundant :-( */
-          ClutterCullResult result = CLUTTER_CULL_RESULT_IN;
-
-          if (G_LIKELY ((clutter_paint_debug_flags &
-                         (CLUTTER_DEBUG_DISABLE_CULLING |
-                          CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS)) !=
-                        (CLUTTER_DEBUG_DISABLE_CULLING |
-                         CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS)))
-            _clutter_actor_update_last_paint_volume (self);
-
-          success = cull_actor (self, &result);
-
-          if (G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_REDRAWS))
-            _clutter_actor_paint_cull_result (self, success, result);
-          else if (result == CLUTTER_CULL_RESULT_OUT && success)
-            goto done;
-        }
+  /* We save the current paint volume so that the next time the
+   * actor queues a redraw we can constrain the redraw to just
+   * cover the union of the new bounding box and the old.
+   *
+   * We also fetch the current paint volume to perform culling so
+   * we can avoid painting actors outside the current clip region.
+   *
+   * If we are painting inside a clone, we should neither update
+   * the paint volume or use it to cull painting, since the paint
+   * box represents the location of the source actor on the
+   * screen.
+   *
+   * XXX: We are starting to do a lot of vertex transforms on
+   * the CPU in a typical paint, so at some point we should
+   * audit these and consider caching some things.
+   */
+  if (!in_clone_paint ())
+    {
+      gboolean success;
+      /* annoyingly gcc warns if uninitialized even though
+       * the initialization is redundant :-( */
+      ClutterCullResult result = CLUTTER_CULL_RESULT_IN;
 
-      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);
+      if (G_LIKELY ((clutter_paint_debug_flags &
+                     (CLUTTER_DEBUG_DISABLE_CULLING |
+                      CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS)) !=
+                    (CLUTTER_DEBUG_DISABLE_CULLING |
+                     CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS)))
+        _clutter_actor_update_last_paint_volume (self);
 
-      clutter_actor_continue_paint (self);
+      success = cull_actor (self, &result);
 
-      if (priv->effects == NULL &&
-          actor_has_shader_data (self))
-        clutter_actor_shader_post_paint (self);
+      if (G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_REDRAWS))
+        _clutter_actor_paint_cull_result (self, success, result);
+      else if (result == CLUTTER_CULL_RESULT_OUT && success)
+        goto done;
+    }
 
-      if (G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_PAINT_VOLUMES))
-        _clutter_actor_draw_paint_volume (self);
+  if (priv->effects == NULL)
+    {
+      if (actor_has_shader_data (self))
+        clutter_actor_shader_pre_paint (self, FALSE);
+      priv->next_effect_to_paint = NULL;
     }
   else
-    {
-      ClutterColor col = { 0, };
+    priv->next_effect_to_paint =
+      _clutter_meta_group_peek_metas (priv->effects);
 
-      CLUTTER_COUNTER_INC (_clutter_uprof_context, actor_pick_counter);
+  clutter_actor_continue_paint (self);
 
-      _clutter_id_to_color (_clutter_actor_get_pick_id (self), &col);
+  if (priv->effects == NULL &&
+      actor_has_shader_data (self))
+    clutter_actor_shader_post_paint (self);
 
-      /* Actor will then paint silhouette of itself in supplied
-       * color.  See clutter_stage_get_actor_at_pos() for where
-       * picking is enabled.
-       */
-      g_signal_emit (self, actor_signals[PICK], 0, &col);
-    }
+  if (G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_PAINT_VOLUMES))
+    _clutter_actor_draw_paint_volume (self);
 
 done:
   if (clip_set)
@@ -2981,9 +2969,24 @@ clutter_actor_continue_paint (ClutterActor *self)
      actual actor */
   if (priv->next_effect_to_paint == NULL)
     {
-      priv->propagated_one_redraw = FALSE;
+      if (_clutter_context_get_pick_mode () == CLUTTER_PICK_NONE)
+        {
+          priv->propagated_one_redraw = FALSE;
+
+          g_signal_emit (self, actor_signals[PAINT], 0);
+        }
+      else
+        {
+          ClutterColor col = { 0, };
 
-      g_signal_emit (self, actor_signals[PAINT], 0);
+          _clutter_id_to_color (_clutter_actor_get_pick_id (self), &col);
+
+          /* Actor will then paint silhouette of itself in supplied
+           * color.  See clutter_stage_get_actor_at_pos() for where
+           * picking is enabled.
+           */
+          g_signal_emit (self, actor_signals[PICK], 0, &col);
+        }
     }
   else
     {
@@ -2997,19 +3000,31 @@ clutter_actor_continue_paint (ClutterActor *self)
       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)
+      if (_clutter_context_get_pick_mode () == CLUTTER_PICK_NONE)
         {
-          /* If there's an effect queued with this redraw then all
-             effects up to that one will be considered dirty. It is
-             expected the queued effect will paint the cached image
-             and not call clutter_actor_continue_paint again (although
-             it should work ok if it does) */
-          if (priv->effect_to_redraw == NULL ||
-              priv->current_effect != priv->effect_to_redraw)
-            run_flags |= CLUTTER_EFFECT_RUN_ACTOR_DIRTY;
+          if (priv->propagated_one_redraw)
+            {
+              /* If there's an effect queued with this redraw then all
+                 effects up to that one will be considered dirty. It
+                 is expected the queued effect will paint the cached
+                 image and not call clutter_actor_continue_paint again
+                 (although it should work ok if it does) */
+              if (priv->effect_to_redraw == NULL ||
+                  priv->current_effect != priv->effect_to_redraw)
+                run_flags |= CLUTTER_EFFECT_RUN_ACTOR_DIRTY;
+            }
+
+          _clutter_effect_paint (priv->current_effect, run_flags);
         }
+      else
+        {
+          /* We can't determine when an actor has been modified since
+             its last pick so lets just assume it has always been
+             modified */
+          run_flags |= CLUTTER_EFFECT_RUN_ACTOR_DIRTY;
 
-      _clutter_effect_paint (priv->current_effect, run_flags);
+          _clutter_effect_pick (priv->current_effect, run_flags);
+        }
 
       priv->current_effect = old_current_effect;
     }
diff --git a/clutter/clutter-effect-private.h b/clutter/clutter-effect-private.h
index f53bfb5..903455b 100644
--- a/clutter/clutter-effect-private.h
+++ b/clutter/clutter-effect-private.h
@@ -11,6 +11,8 @@ gboolean        _clutter_effect_get_paint_volume        (ClutterEffect      *eff
                                                          ClutterPaintVolume *volume);
 void            _clutter_effect_paint                   (ClutterEffect      *effect,
                                                          ClutterEffectRunFlags flags);
+void            _clutter_effect_pick                    (ClutterEffect      *effect,
+                                                         ClutterEffectRunFlags flags);
 
 G_END_DECLS
 
diff --git a/clutter/clutter-effect.c b/clutter/clutter-effect.c
index fea555b..b32d1d4 100644
--- a/clutter/clutter-effect.c
+++ b/clutter/clutter-effect.c
@@ -249,6 +249,17 @@ clutter_effect_real_paint (ClutterEffect         *effect,
 }
 
 static void
+clutter_effect_real_pick (ClutterEffect         *effect,
+                          ClutterEffectRunFlags  flags)
+{
+  ClutterActorMeta *actor_meta = CLUTTER_ACTOR_META (effect);
+  ClutterActor *actor;
+
+  actor = clutter_actor_meta_get_actor (actor_meta);
+  clutter_actor_continue_paint (actor);
+}
+
+static void
 clutter_effect_notify (GObject    *gobject,
                        GParamSpec *pspec)
 {
@@ -276,6 +287,7 @@ clutter_effect_class_init (ClutterEffectClass *klass)
   klass->post_paint = clutter_effect_real_post_paint;
   klass->get_paint_volume = clutter_effect_real_get_paint_volume;
   klass->paint = clutter_effect_real_paint;
+  klass->pick = clutter_effect_real_pick;
 }
 
 static void
@@ -308,6 +320,15 @@ _clutter_effect_paint (ClutterEffect         *effect,
   CLUTTER_EFFECT_GET_CLASS (effect)->paint (effect, flags);
 }
 
+void
+_clutter_effect_pick (ClutterEffect         *effect,
+                      ClutterEffectRunFlags  flags)
+{
+  g_return_if_fail (CLUTTER_IS_EFFECT (effect));
+
+  CLUTTER_EFFECT_GET_CLASS (effect)->pick (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 cb50abd..17e651e 100644
--- a/clutter/clutter-effect.h
+++ b/clutter/clutter-effect.h
@@ -49,7 +49,7 @@ typedef struct _ClutterEffectClass      ClutterEffectClass;
  * 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.
+ * Flags passed to the â??paintâ?? or â??pickâ?? method of #ClutterEffect.
  */
 typedef enum
 {
@@ -96,8 +96,10 @@ struct _ClutterEffectClass
   void     (* paint)            (ClutterEffect        *effect,
                                  ClutterEffectRunFlags flags);
 
+  void     (* pick)             (ClutterEffect        *effect,
+                                 ClutterEffectRunFlags flags);
+
   /*< private >*/
-  void (* _clutter_effect3) (void);
   void (* _clutter_effect4) (void);
   void (* _clutter_effect5) (void);
   void (* _clutter_effect6) (void);
diff --git a/tests/conform/test-pick.c b/tests/conform/test-pick.c
index 44f2a66..3955f50 100644
--- a/tests/conform/test-pick.c
+++ b/tests/conform/test-pick.c
@@ -6,6 +6,7 @@
 #define STAGE_HEIGHT 480
 #define ACTORS_X 12
 #define ACTORS_Y 16
+#define SHIFT_STEP STAGE_WIDTH / ACTORS_X
 
 typedef struct _State State;
 
@@ -18,6 +19,75 @@ struct _State
   gboolean pass;
 };
 
+struct _ShiftEffect
+{
+  ClutterShaderEffect parent_instance;
+};
+
+struct _ShiftEffectClass
+{
+  ClutterShaderEffectClass parent_class;
+};
+
+typedef struct _ShiftEffect       ShiftEffect;
+typedef struct _ShiftEffectClass  ShiftEffectClass;
+
+#define TYPE_SHIFT_EFFECT        (shift_effect_get_type ())
+
+G_DEFINE_TYPE (ShiftEffect,
+               shift_effect,
+               CLUTTER_TYPE_SHADER_EFFECT);
+
+static void
+shader_paint (ClutterEffect         *effect,
+              ClutterEffectRunFlags  flags)
+{
+  ClutterShaderEffect *shader = CLUTTER_SHADER_EFFECT (effect);
+  float tex_width;
+  ClutterActor *actor =
+    clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (effect));
+
+  g_debug ("shader_paint");
+
+  clutter_shader_effect_set_shader_source (shader,
+    "uniform sampler2D tex;\n"
+    "uniform float step;\n"
+    "void main (void)\n"
+    "{\n"
+    "  gl_FragColor = texture2D(tex, vec2 (gl_TexCoord[0].s + step,\n"
+    "                                      gl_TexCoord[0].t));\n"
+    "}\n");
+
+  tex_width = clutter_actor_get_width (actor);
+
+  clutter_shader_effect_set_uniform (shader, "tex", G_TYPE_INT, 1, 0);
+  clutter_shader_effect_set_uniform (shader, "step", G_TYPE_FLOAT, 1,
+                                     SHIFT_STEP / tex_width);
+
+  CLUTTER_EFFECT_CLASS (shift_effect_parent_class)->paint (effect, flags);
+}
+
+static void
+shader_pick (ClutterEffect         *effect,
+             ClutterEffectRunFlags  flags)
+{
+  shader_paint (effect, flags);
+}
+
+static void
+shift_effect_class_init (ShiftEffectClass *klass)
+{
+  ClutterEffectClass *shader_class = CLUTTER_EFFECT_CLASS (klass);
+
+  shader_class->paint = shader_paint;
+  shader_class->pick = shader_pick;
+}
+
+static void
+shift_effect_init (ShiftEffect *self)
+{
+}
+
 static gboolean
 on_timeout (State *state)
 {
@@ -34,7 +104,7 @@ on_timeout (State *state)
   clutter_stage_get_actor_at_pos (CLUTTER_STAGE (state->stage),
                                   CLUTTER_PICK_REACTIVE, 10, 10);
 
-  for (test_num = 0; test_num < 3; test_num++)
+  for (test_num = 0; test_num < 5; test_num++)
     {
       if (test_num == 0)
         {
@@ -69,53 +139,91 @@ on_timeout (State *state)
           if (g_test_verbose ())
             g_print ("Clipped covering actor:\n");
         }
+      else if (test_num == 3)
+        {
+          clutter_actor_hide (over_actor);
+
+          clutter_actor_add_effect_with_name (CLUTTER_ACTOR (state->stage),
+                                              "blur",
+                                              clutter_blur_effect_new ());
+
+          if (g_test_verbose ())
+            g_print ("With blur effect:\n");
+        }
+      else if (test_num == 4)
+        {
+          clutter_actor_hide (over_actor);
+          clutter_actor_remove_effect_by_name (CLUTTER_ACTOR (state->stage),
+                                               "blur");
+
+          clutter_actor_add_effect_with_name (CLUTTER_ACTOR (state->stage),
+            "shift",
+            g_object_new (TYPE_SHIFT_EFFECT, NULL));
+
+          if (g_test_verbose ())
+            g_print ("With shift effect:\n");
+        }
 
       for (y = 0; y < ACTORS_Y; y++)
-        for (x = 0; x < ACTORS_X; x++)
-          {
-            gboolean pass = FALSE;
-            ClutterActor *actor
-              = clutter_stage_get_actor_at_pos (CLUTTER_STAGE (state->stage),
-                                                CLUTTER_PICK_ALL,
-                                                x * state->actor_width
-                                                + state->actor_width / 2,
-                                                y * state->actor_height
-                                                + state->actor_height / 2);
-
-            if (g_test_verbose ())
-              g_print ("% 3i,% 3i / %p -> ",
-                       x, y, state->actors[y * ACTORS_X + x]);
-
-            if (actor == NULL)
-              {
-                if (g_test_verbose ())
-                  g_print ("NULL:       FAIL\n");
-              }
-            else if (actor == over_actor)
-              {
-                if (test_num == 2
-                    && x >= 2 && x < ACTORS_X - 2
-                    && y >= 2 && y < ACTORS_Y - 2)
-                  pass = TRUE;
-
-                if (g_test_verbose ())
-                  g_print ("over_actor: %s\n", pass ? "pass" : "FAIL");
-              }
-            else
-              {
-                if (actor == state->actors[y * ACTORS_X + x]
-                    && (test_num != 2
-                        || x < 2 || x >= ACTORS_X - 2
-                        || y < 2 || y >= ACTORS_Y - 2))
-                  pass = TRUE;
-
-                if (g_test_verbose ())
-                  g_print ("%p: %s\n", actor, pass ? "pass" : "FAIL");
-              }
-
-            if (!pass)
-              state->pass = FALSE;
-          }
+        {
+          if (test_num == 4)
+            x = 1;
+          else
+            x = 0;
+
+          for (; x < ACTORS_X; x++)
+            {
+              gboolean pass = FALSE;
+              gfloat pick_x;
+              ClutterActor *actor;
+
+              pick_x = x * state->actor_width + state->actor_width / 2;
+
+              if (test_num == 4)
+                pick_x -= SHIFT_STEP;
+
+              actor
+                = clutter_stage_get_actor_at_pos (CLUTTER_STAGE (state->stage),
+                                                  CLUTTER_PICK_ALL,
+                                                  pick_x,
+                                                  y * state->actor_height
+                                                  + state->actor_height / 2);
+
+              if (g_test_verbose ())
+                g_print ("% 3i,% 3i / %p -> ",
+                         x, y, state->actors[y * ACTORS_X + x]);
+
+              if (actor == NULL)
+                {
+                  if (g_test_verbose ())
+                    g_print ("NULL:       FAIL\n");
+                }
+              else if (actor == over_actor)
+                {
+                  if (test_num == 2
+                      && x >= 2 && x < ACTORS_X - 2
+                      && y >= 2 && y < ACTORS_Y - 2)
+                    pass = TRUE;
+
+                  if (g_test_verbose ())
+                    g_print ("over_actor: %s\n", pass ? "pass" : "FAIL");
+                }
+              else
+                {
+                  if (actor == state->actors[y * ACTORS_X + x]
+                      && (test_num != 2
+                          || x < 2 || x >= ACTORS_X - 2
+                          || y < 2 || y >= ACTORS_Y - 2))
+                    pass = TRUE;
+
+                  if (g_test_verbose ())
+                    g_print ("%p: %s\n", actor, pass ? "pass" : "FAIL");
+                }
+
+              if (!pass)
+                state->pass = FALSE;
+            }
+        }
     }
 
   clutter_main_quit ();



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