diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index 783564832..fcea9b4db 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -2865,79 +2865,67 @@ clutter_actor_paint (ClutterActor *self) applications to notify when the value of the has_overlaps virtual changes. */ add_or_remove_flatten_effect (self); - - /* 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; - } - - 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); - - clutter_actor_continue_paint (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)) - _clutter_actor_draw_paint_volume (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 ()) { - ClutterColor col = { 0, }; + gboolean success; + /* annoyingly gcc warns if uninitialized even though + * the initialization is redundant :-( */ + ClutterCullResult result = CLUTTER_CULL_RESULT_IN; - CLUTTER_COUNTER_INC (_clutter_uprof_context, actor_pick_counter); + 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_id_to_color (_clutter_actor_get_pick_id (self), &col); + success = cull_actor (self, &result); - /* 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_REDRAWS)) + _clutter_actor_paint_cull_result (self, success, result); + else if (result == CLUTTER_CULL_RESULT_OUT && success) + goto done; } + 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); + + clutter_actor_continue_paint (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)) + _clutter_actor_draw_paint_volume (self); + done: if (clip_set) cogl_clip_pop(); @@ -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); + g_signal_emit (self, actor_signals[PAINT], 0); + } + else + { + ClutterColor col = { 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); + _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_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 f53bfb51c..903455bf8 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 fea555bcc..b32d1d422 100644 --- a/clutter/clutter-effect.c +++ b/clutter/clutter-effect.c @@ -248,6 +248,17 @@ clutter_effect_real_paint (ClutterEffect *effect, _clutter_effect_post_paint (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 cb50abd77..17e651e15 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 44f2a66b2..3955f500d 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 (test_num == 4) + x = 1; + else + x = 0; - if (g_test_verbose ()) - g_print ("% 3i,% 3i / %p -> ", - x, y, state->actors[y * ACTORS_X + x]); + for (; x < ACTORS_X; x++) + { + gboolean pass = FALSE; + gfloat pick_x; + ClutterActor *actor; - 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; + pick_x = x * state->actor_width + state->actor_width / 2; - 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 (test_num == 4) + pick_x -= SHIFT_STEP; - if (g_test_verbose ()) - g_print ("%p: %s\n", actor, pass ? "pass" : "FAIL"); - } + 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 (!pass) - state->pass = FALSE; - } + 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 ();