From 8a709b5766788d89bdb4cb22dfd01d83186589cc Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Sat, 17 Oct 2020 12:24:13 -0300 Subject: [PATCH 01/12] clutter/stage: Add pick trace It will help profiling picking times, and compare this future patches with status quo. https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1509 --- clutter/clutter/clutter-stage.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c index 2a6a7e9be6..9d1211076d 100644 --- a/clutter/clutter/clutter-stage.c +++ b/clutter/clutter/clutter-stage.c @@ -1397,6 +1397,8 @@ _clutter_stage_do_pick_on_view (ClutterStage *stage, g_assert (context->pick_mode == CLUTTER_PICK_NONE); + COGL_TRACE_BEGIN_SCOPED (ClutterStagePickView, "Pick (view)"); + if (mode != priv->cached_pick_mode) { ClutterPickContext *pick_context; -- GitLab From a4961ad4a7c21da853f9d9b186c99637029ff9b1 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Fri, 16 Oct 2020 09:59:54 -0300 Subject: [PATCH 02/12] clutter/pick-context: Make pick context aware of the pick mode Pass the pick mode during construction, and add a getter for it. It'll be used by the next patches to make clutter_actor_should_pick_paint() not depend on a global context. https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1509 --- clutter/clutter/clutter-pick-context-private.h | 3 ++- clutter/clutter/clutter-pick-context.c | 14 +++++++++++++- clutter/clutter/clutter-pick-context.h | 3 +++ clutter/clutter/clutter-stage.c | 2 +- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/clutter/clutter/clutter-pick-context-private.h b/clutter/clutter/clutter-pick-context-private.h index 4e6db156b2..7e4422eddf 100644 --- a/clutter/clutter/clutter-pick-context-private.h +++ b/clutter/clutter/clutter-pick-context-private.h @@ -20,6 +20,7 @@ #include "clutter-pick-context.h" -ClutterPickContext * clutter_pick_context_new_for_view (ClutterStageView *view); +ClutterPickContext * clutter_pick_context_new_for_view (ClutterStageView *view, + ClutterPickMode mode); #endif /* CLUTTER_PICK_CONTEXT_PRIVATE_H */ diff --git a/clutter/clutter/clutter-pick-context.c b/clutter/clutter/clutter-pick-context.c index 5ecb907b09..6209e58cee 100644 --- a/clutter/clutter/clutter-pick-context.c +++ b/clutter/clutter/clutter-pick-context.c @@ -23,6 +23,7 @@ struct _ClutterPickContext { grefcount ref_count; + ClutterPickMode mode; CoglFramebuffer *framebuffer; }; @@ -31,12 +32,14 @@ G_DEFINE_BOXED_TYPE (ClutterPickContext, clutter_pick_context, clutter_pick_context_unref) ClutterPickContext * -clutter_pick_context_new_for_view (ClutterStageView *view) +clutter_pick_context_new_for_view (ClutterStageView *view, + ClutterPickMode mode) { ClutterPickContext *pick_context; pick_context = g_new0 (ClutterPickContext, 1); g_ref_count_init (&pick_context->ref_count); + pick_context->mode = mode; pick_context->framebuffer = g_object_ref (clutter_stage_view_get_framebuffer (view)); @@ -81,3 +84,12 @@ clutter_pick_context_get_framebuffer (ClutterPickContext *pick_context) { return pick_context->framebuffer; } + +/** + * clutter_pick_context_get_mode: (skip) + */ +ClutterPickMode +clutter_pick_context_get_mode (ClutterPickContext *pick_context) +{ + return pick_context->mode; +} diff --git a/clutter/clutter/clutter-pick-context.h b/clutter/clutter/clutter-pick-context.h index a1f8dec41b..d420d0a57a 100644 --- a/clutter/clutter/clutter-pick-context.h +++ b/clutter/clutter/clutter-pick-context.h @@ -46,4 +46,7 @@ void clutter_pick_context_destroy (ClutterPickContext *pick_context); CLUTTER_EXPORT CoglFramebuffer * clutter_pick_context_get_framebuffer (ClutterPickContext *pick_context); +CLUTTER_EXPORT +ClutterPickMode clutter_pick_context_get_mode (ClutterPickContext *pick_context); + #endif /* CLUTTER_PICK_CONTEXT_H */ diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c index 9d1211076d..7df016dfa2 100644 --- a/clutter/clutter/clutter-stage.c +++ b/clutter/clutter/clutter-stage.c @@ -1405,7 +1405,7 @@ _clutter_stage_do_pick_on_view (ClutterStage *stage, _clutter_stage_clear_pick_stack (stage); - pick_context = clutter_pick_context_new_for_view (view); + pick_context = clutter_pick_context_new_for_view (view, mode); context->pick_mode = mode; priv->cached_pick_mode = CLUTTER_PICK_NONE; -- GitLab From 05b6b6a88de246dcdde177b3dc0da763b413fb99 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Fri, 16 Oct 2020 10:02:56 -0300 Subject: [PATCH 03/12] clutter/actor: Use pick context in clutter_actor_should_pick_paint() Pass the ClutterPickContext to clutter_actor_should_pick_paint() and check the pick mode from it. https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1509 --- clutter/clutter/clutter-actor.c | 8 +++++--- clutter/clutter/clutter-actor.h | 3 ++- src/compositor/meta-surface-actor.c | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/clutter/clutter/clutter-actor.c b/clutter/clutter/clutter-actor.c index 08031cb4a6..c94dab8a21 100644 --- a/clutter/clutter/clutter-actor.c +++ b/clutter/clutter/clutter-actor.c @@ -2311,7 +2311,7 @@ static void clutter_actor_real_pick (ClutterActor *self, ClutterPickContext *pick_context) { - if (clutter_actor_should_pick_paint (self)) + if (clutter_actor_should_pick_paint (self, pick_context)) { ClutterActorBox box = { .x1 = 0, @@ -2344,6 +2344,7 @@ clutter_actor_real_pick (ClutterActor *self, /** * clutter_actor_should_pick_paint: * @self: A #ClutterActor + * @pick_context: a #ClutterPickContext * * Should be called inside the implementation of the * #ClutterActor::pick virtual function in order to check whether @@ -2355,13 +2356,14 @@ clutter_actor_real_pick (ClutterActor *self, * %FALSE otherwise */ gboolean -clutter_actor_should_pick_paint (ClutterActor *self) +clutter_actor_should_pick_paint (ClutterActor *self, + ClutterPickContext *pick_context) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE); if (CLUTTER_ACTOR_IS_MAPPED (self) && clutter_actor_has_allocation (self) && - (_clutter_context_get_pick_mode () == CLUTTER_PICK_ALL || + (clutter_pick_context_get_mode (pick_context) == CLUTTER_PICK_ALL || CLUTTER_ACTOR_IS_REACTIVE (self))) return TRUE; diff --git a/clutter/clutter/clutter-actor.h b/clutter/clutter/clutter-actor.h index 3ffd98f2cb..8bb7bd1745 100644 --- a/clutter/clutter/clutter-actor.h +++ b/clutter/clutter/clutter-actor.h @@ -591,7 +591,8 @@ void clutter_actor_set_offscreen_redirect CLUTTER_EXPORT ClutterOffscreenRedirect clutter_actor_get_offscreen_redirect (ClutterActor *self); CLUTTER_EXPORT -gboolean clutter_actor_should_pick_paint (ClutterActor *self); +gboolean clutter_actor_should_pick_paint (ClutterActor *self, + ClutterPickContext *pick_context); CLUTTER_EXPORT gboolean clutter_actor_is_in_clone_paint (ClutterActor *self); CLUTTER_EXPORT diff --git a/src/compositor/meta-surface-actor.c b/src/compositor/meta-surface-actor.c index 7d5f2bc5d8..89cd7b0146 100644 --- a/src/compositor/meta-surface-actor.c +++ b/src/compositor/meta-surface-actor.c @@ -184,7 +184,7 @@ meta_surface_actor_pick (ClutterActor *actor, ClutterActorIter iter; ClutterActor *child; - if (!clutter_actor_should_pick_paint (actor)) + if (!clutter_actor_should_pick_paint (actor, pick_context)) return; /* If there is no region then use the regular pick */ -- GitLab From 8d47f79b2c56080763a2e4478ededc364996178d Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Fri, 16 Oct 2020 10:06:39 -0300 Subject: [PATCH 04/12] clutter/actor: Remove '_paint' suffix from clutter_actor_should_pick_paint We're not using paint to pick anymore. https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1509 --- clutter/clutter/clutter-actor.c | 13 ++++++------- clutter/clutter/clutter-actor.h | 2 +- src/compositor/meta-surface-actor.c | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/clutter/clutter/clutter-actor.c b/clutter/clutter/clutter-actor.c index c94dab8a21..4a92d6ffd2 100644 --- a/clutter/clutter/clutter-actor.c +++ b/clutter/clutter/clutter-actor.c @@ -2311,7 +2311,7 @@ static void clutter_actor_real_pick (ClutterActor *self, ClutterPickContext *pick_context) { - if (clutter_actor_should_pick_paint (self, pick_context)) + if (clutter_actor_should_pick (self, pick_context)) { ClutterActorBox box = { .x1 = 0, @@ -2342,22 +2342,21 @@ clutter_actor_real_pick (ClutterActor *self, } /** - * clutter_actor_should_pick_paint: + * clutter_actor_should_pick: * @self: A #ClutterActor * @pick_context: a #ClutterPickContext * * Should be called inside the implementation of the * #ClutterActor::pick virtual function in order to check whether - * the actor should paint itself in pick mode or not. + * the actor should be picked or not. * * This function should never be called directly by applications. * - * Return value: %TRUE if the actor should paint its silhouette, - * %FALSE otherwise + * Return value: %TRUE if the actor should be picked, %FALSE otherwise */ gboolean -clutter_actor_should_pick_paint (ClutterActor *self, - ClutterPickContext *pick_context) +clutter_actor_should_pick (ClutterActor *self, + ClutterPickContext *pick_context) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE); diff --git a/clutter/clutter/clutter-actor.h b/clutter/clutter/clutter-actor.h index 8bb7bd1745..bc858fb5a7 100644 --- a/clutter/clutter/clutter-actor.h +++ b/clutter/clutter/clutter-actor.h @@ -591,7 +591,7 @@ void clutter_actor_set_offscreen_redirect CLUTTER_EXPORT ClutterOffscreenRedirect clutter_actor_get_offscreen_redirect (ClutterActor *self); CLUTTER_EXPORT -gboolean clutter_actor_should_pick_paint (ClutterActor *self, +gboolean clutter_actor_should_pick (ClutterActor *self, ClutterPickContext *pick_context); CLUTTER_EXPORT gboolean clutter_actor_is_in_clone_paint (ClutterActor *self); diff --git a/src/compositor/meta-surface-actor.c b/src/compositor/meta-surface-actor.c index 89cd7b0146..97202b8eb4 100644 --- a/src/compositor/meta-surface-actor.c +++ b/src/compositor/meta-surface-actor.c @@ -184,7 +184,7 @@ meta_surface_actor_pick (ClutterActor *actor, ClutterActorIter iter; ClutterActor *child; - if (!clutter_actor_should_pick_paint (actor, pick_context)) + if (!clutter_actor_should_pick (actor, pick_context)) return; /* If there is no region then use the regular pick */ -- GitLab From 56ed0bf00118b23dfd21c0d834987ef8408e4d38 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Fri, 16 Oct 2020 10:11:37 -0300 Subject: [PATCH 05/12] clutter: Remove pick mode from context It is not used anywhere now. https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1509 --- clutter/clutter/clutter-main.c | 8 -------- clutter/clutter/clutter-private.h | 3 --- clutter/clutter/clutter-stage.c | 5 ----- 3 files changed, 16 deletions(-) diff --git a/clutter/clutter/clutter-main.c b/clutter/clutter/clutter-main.c index 0a21d1f254..c258f676d0 100644 --- a/clutter/clutter/clutter-main.c +++ b/clutter/clutter/clutter-main.c @@ -2311,14 +2311,6 @@ _clutter_clear_events_queue (void) } } -ClutterPickMode -_clutter_context_get_pick_mode (void) -{ - ClutterMainContext *context = _clutter_context_get_default (); - - return context->pick_mode; -} - /** * clutter_add_debug_flags: (skip) * diff --git a/clutter/clutter/clutter-private.h b/clutter/clutter/clutter-private.h index cc310edb26..08a8c3f8c2 100644 --- a/clutter/clutter/clutter-private.h +++ b/clutter/clutter/clutter-private.h @@ -127,8 +127,6 @@ struct _ClutterMainContext * ordered from least recently added to most recently added */ GList *event_filters; - ClutterPickMode pick_mode; - /* default FPS; this is only used if we cannot sync to vblank */ guint frame_rate; @@ -176,7 +174,6 @@ ClutterMainContext * _clutter_context_get_default (void); void _clutter_context_lock (void); void _clutter_context_unlock (void); gboolean _clutter_context_is_initialized (void); -ClutterPickMode _clutter_context_get_pick_mode (void); gboolean _clutter_context_get_show_fps (void); gboolean _clutter_feature_init (GError **error); diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c index 7df016dfa2..1b3e9ad97f 100644 --- a/clutter/clutter/clutter-stage.c +++ b/clutter/clutter/clutter-stage.c @@ -1391,12 +1391,9 @@ _clutter_stage_do_pick_on_view (ClutterStage *stage, ClutterPickMode mode, ClutterStageView *view) { - ClutterMainContext *context = _clutter_context_get_default (); ClutterStagePrivate *priv = stage->priv; int i; - g_assert (context->pick_mode == CLUTTER_PICK_NONE); - COGL_TRACE_BEGIN_SCOPED (ClutterStagePickView, "Pick (view)"); if (mode != priv->cached_pick_mode) @@ -1407,10 +1404,8 @@ _clutter_stage_do_pick_on_view (ClutterStage *stage, pick_context = clutter_pick_context_new_for_view (view, mode); - context->pick_mode = mode; priv->cached_pick_mode = CLUTTER_PICK_NONE; clutter_actor_pick (CLUTTER_ACTOR (stage), pick_context); - context->pick_mode = CLUTTER_PICK_NONE; priv->cached_pick_mode = mode; clutter_pick_context_destroy (pick_context); -- GitLab From 0d79a0faf89644088bc3a50bbb9d23dc50a45791 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Fri, 16 Oct 2020 17:40:04 -0300 Subject: [PATCH 06/12] clutter/stage: Remove useless cached pick mode reset It happens a couple of lines above, in _clutter_stage_clear_pick_stack(). https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1509 --- clutter/clutter/clutter-stage.c | 1 - 1 file changed, 1 deletion(-) diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c index 1b3e9ad97f..53a8ec9b8c 100644 --- a/clutter/clutter/clutter-stage.c +++ b/clutter/clutter/clutter-stage.c @@ -1404,7 +1404,6 @@ _clutter_stage_do_pick_on_view (ClutterStage *stage, pick_context = clutter_pick_context_new_for_view (view, mode); - priv->cached_pick_mode = CLUTTER_PICK_NONE; clutter_actor_pick (CLUTTER_ACTOR (stage), pick_context); priv->cached_pick_mode = mode; -- GitLab From f411834d42a11172ac8fd090051ed174f437e09f Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Fri, 16 Oct 2020 19:13:12 -0300 Subject: [PATCH 07/12] Introduce ClutterPickStack ClutterPickStack is a new boxed type that stores the vertices and clip rectangles. It is meant to be a byproduct of picking, and takes over most of what ClutterStage currently does. It introduces a 'seal' system, inspired by MetaKmsUpdate. After the pick operation is done, and the rectangles are collected, the pick stack is sealed, and is not allowed to be externally modified anymore. Internally, it still can invalidate pick records when an actor is destroyed. For now, it handles both the clip rectangles, and the matrix stack, separatedly. Future commits will rearrange this. https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1509 --- clutter/clutter/clutter-actor.c | 30 +- .../clutter/clutter-pick-context-private.h | 4 + clutter/clutter/clutter-pick-context.c | 101 +++++ clutter/clutter/clutter-pick-context.h | 23 ++ clutter/clutter/clutter-pick-stack-private.h | 65 +++ clutter/clutter/clutter-pick-stack.c | 386 ++++++++++++++++++ clutter/clutter/clutter-stage-private.h | 9 - clutter/clutter/clutter-stage.c | 285 +------------ clutter/clutter/meson.build | 1 + 9 files changed, 595 insertions(+), 309 deletions(-) create mode 100644 clutter/clutter/clutter-pick-stack-private.h create mode 100644 clutter/clutter/clutter-pick-stack.c diff --git a/clutter/clutter/clutter-actor.c b/clutter/clutter/clutter-actor.c index 4a92d6ffd2..867b54c477 100644 --- a/clutter/clutter/clutter-actor.c +++ b/clutter/clutter/clutter-actor.c @@ -1252,8 +1252,6 @@ _clutter_actor_transform_local_box_to_stage (ClutterActor *self, { ClutterActor *stage_actor = CLUTTER_ACTOR (stage); ClutterActorPrivate *stage_priv = stage_actor->priv; - CoglFramebuffer *fb = - clutter_pick_context_get_framebuffer (pick_context); graphene_matrix_t modelview, transform_to_stage; int v; @@ -1261,7 +1259,7 @@ _clutter_actor_transform_local_box_to_stage (ClutterActor *self, if (!stage_priv->has_inverse_transform) return FALSE; - cogl_framebuffer_get_modelview_matrix (fb, &modelview); + clutter_pick_context_get_transform (pick_context, &modelview); graphene_matrix_multiply (&modelview, &stage_priv->inverse_transform, &transform_to_stage); @@ -1326,7 +1324,7 @@ clutter_actor_pick_box (ClutterActor *self, if (_clutter_actor_transform_local_box_to_stage (self, stage, pick_context, box, vertices)) - clutter_stage_log_pick (stage, vertices, self); + clutter_pick_context_log_pick (pick_context, vertices, self); } static gboolean @@ -1343,19 +1341,10 @@ _clutter_actor_push_pick_clip (ClutterActor *self, clip, vertices)) return FALSE; - clutter_stage_push_pick_clip (stage, vertices); + clutter_pick_context_push_clip (pick_context, vertices); return TRUE; } -static void -_clutter_actor_pop_pick_clip (ClutterActor *self) -{ - ClutterActor *stage; - - stage = _clutter_actor_get_stage_internal (self); - clutter_stage_pop_pick_clip (CLUTTER_STAGE (stage)); -} - static void clutter_actor_set_mapped (ClutterActor *self, gboolean mapped) @@ -4021,7 +4010,6 @@ clutter_actor_pick (ClutterActor *actor, ClutterPickContext *pick_context) { ClutterActorPrivate *priv; - CoglFramebuffer *framebuffer; ClutterActorBox clip; gboolean clip_set = FALSE; @@ -4039,16 +4027,13 @@ clutter_actor_pick (ClutterActor *actor, /* mark that we are in the paint process */ CLUTTER_SET_PRIVATE_FLAGS (actor, CLUTTER_IN_PICK); - framebuffer = clutter_pick_context_get_framebuffer (pick_context); - cogl_framebuffer_push_matrix (framebuffer); - if (priv->enable_model_view_transform) { graphene_matrix_t matrix; - cogl_framebuffer_get_modelview_matrix (framebuffer, &matrix); + graphene_matrix_init_identity (&matrix); _clutter_actor_apply_modelview_transform (actor, &matrix); - cogl_framebuffer_set_modelview_matrix (framebuffer, &matrix); + clutter_pick_context_push_transform (pick_context, &matrix); } if (priv->has_clip) @@ -4081,9 +4066,10 @@ clutter_actor_pick (ClutterActor *actor, clutter_actor_continue_pick (actor, pick_context); if (clip_set) - _clutter_actor_pop_pick_clip (actor); + clutter_pick_context_pop_clip (pick_context); - cogl_framebuffer_pop_matrix (framebuffer); + if (priv->enable_model_view_transform) + clutter_pick_context_pop_transform (pick_context); /* paint sequence complete */ CLUTTER_UNSET_PRIVATE_FLAGS (actor, CLUTTER_IN_PICK); diff --git a/clutter/clutter/clutter-pick-context-private.h b/clutter/clutter/clutter-pick-context-private.h index 7e4422eddf..cfbfce6eb4 100644 --- a/clutter/clutter/clutter-pick-context-private.h +++ b/clutter/clutter/clutter-pick-context-private.h @@ -19,8 +19,12 @@ #define CLUTTER_PICK_CONTEXT_PRIVATE_H #include "clutter-pick-context.h" +#include "clutter-pick-stack-private.h" ClutterPickContext * clutter_pick_context_new_for_view (ClutterStageView *view, ClutterPickMode mode); +ClutterPickStack * +clutter_pick_context_steal_stack (ClutterPickContext *pick_context); + #endif /* CLUTTER_PICK_CONTEXT_PRIVATE_H */ diff --git a/clutter/clutter/clutter-pick-context.c b/clutter/clutter/clutter-pick-context.c index 6209e58cee..29027284aa 100644 --- a/clutter/clutter/clutter-pick-context.c +++ b/clutter/clutter/clutter-pick-context.c @@ -25,6 +25,7 @@ struct _ClutterPickContext ClutterPickMode mode; CoglFramebuffer *framebuffer; + ClutterPickStack *pick_stack; }; G_DEFINE_BOXED_TYPE (ClutterPickContext, clutter_pick_context, @@ -36,6 +37,7 @@ clutter_pick_context_new_for_view (ClutterStageView *view, ClutterPickMode mode) { ClutterPickContext *pick_context; + CoglContext *context; pick_context = g_new0 (ClutterPickContext, 1); g_ref_count_init (&pick_context->ref_count); @@ -43,6 +45,9 @@ clutter_pick_context_new_for_view (ClutterStageView *view, pick_context->framebuffer = g_object_ref (clutter_stage_view_get_framebuffer (view)); + context = cogl_framebuffer_get_context (pick_context->framebuffer); + pick_context->pick_stack = clutter_pick_stack_new (context); + return pick_context; } @@ -56,6 +61,7 @@ clutter_pick_context_ref (ClutterPickContext *pick_context) static void clutter_pick_context_dispose (ClutterPickContext *pick_context) { + g_clear_pointer (&pick_context->pick_stack, clutter_pick_stack_unref); g_clear_object (&pick_context->framebuffer); } @@ -93,3 +99,98 @@ clutter_pick_context_get_mode (ClutterPickContext *pick_context) { return pick_context->mode; } + +ClutterPickStack * +clutter_pick_context_steal_stack (ClutterPickContext *pick_context) +{ + clutter_pick_stack_seal (pick_context->pick_stack); + return g_steal_pointer (&pick_context->pick_stack); +} + +/** + * clutter_pick_context_log_pick: + * @pick_context: a #ClutterPickContext + * @vertices: (array fixed-size=4): array of #graphene_point_t + * @actor: a #ClutterActor + * + * Logs a pick rectangle into the pick stack. + */ +void +clutter_pick_context_log_pick (ClutterPickContext *pick_context, + const graphene_point_t vertices[4], + ClutterActor *actor) +{ + clutter_pick_stack_log_pick (pick_context->pick_stack, vertices, actor); +} + +/** + * clutter_pick_context_push_clip: + * @pick_context: a #ClutterPickContext + * @vertices: (array fixed-size=4): array of #graphene_point_t + * + * Pushes a clip rectangle defined by @vertices into the pick stack. + * Pop with clutter_pick_context_pop_clip() when done. + */ +void +clutter_pick_context_push_clip (ClutterPickContext *pick_context, + const graphene_point_t vertices[4]) +{ + clutter_pick_stack_push_clip (pick_context->pick_stack, vertices); +} + +/** + * clutter_pick_context_pop_clip: + * @pick_context: a #ClutterPickContext + * + * Pops the current clip rectangle from the clip stack. It is a programming + * error to call this without a corresponding clutter_pick_context_push_clip() + * call first. + */ +void +clutter_pick_context_pop_clip (ClutterPickContext *pick_context) +{ + clutter_pick_stack_pop_clip (pick_context->pick_stack); +} + +/** + * clutter_pick_context_push_transform: + * @pick_context: a #ClutterPickContext + * @transform: a #graphene_matrix_t + * + * Pushes @transform into the pick stack. Pop with + * clutter_pick_context_pop_transform() when done. + */ +void +clutter_pick_context_push_transform (ClutterPickContext *pick_context, + const graphene_matrix_t *transform) +{ + clutter_pick_stack_push_transform (pick_context->pick_stack, transform); +} + +/** + * clutter_pick_context_get_transform: + * @pick_context: a #ClutterPickContext + * @out_matrix: (out): a #graphene_matrix_t + * + * Retrieves the current transform of the pick stack. + */ +void +clutter_pick_context_get_transform (ClutterPickContext *pick_context, + graphene_matrix_t *out_transform) +{ + clutter_pick_stack_get_transform (pick_context->pick_stack, out_transform); +} + +/** + * clutter_pick_context_pop_transform: + * @pick_context: a #ClutterPickContext + * + * Pops the current transform from the clip stack. It is a programming error + * to call this without a corresponding clutter_pick_context_push_transform() + * call first. + */ +void +clutter_pick_context_pop_transform (ClutterPickContext *pick_context) +{ + clutter_pick_stack_pop_transform (pick_context->pick_stack); +} diff --git a/clutter/clutter/clutter-pick-context.h b/clutter/clutter/clutter-pick-context.h index d420d0a57a..4afa25af71 100644 --- a/clutter/clutter/clutter-pick-context.h +++ b/clutter/clutter/clutter-pick-context.h @@ -49,4 +49,27 @@ CoglFramebuffer * clutter_pick_context_get_framebuffer (ClutterPickContext *pick CLUTTER_EXPORT ClutterPickMode clutter_pick_context_get_mode (ClutterPickContext *pick_context); +CLUTTER_EXPORT +void clutter_pick_context_log_pick (ClutterPickContext *pick_context, + const graphene_point_t vertices[4], + ClutterActor *actor); + +CLUTTER_EXPORT +void clutter_pick_context_push_clip (ClutterPickContext *pick_context, + const graphene_point_t vertices[4]); + +CLUTTER_EXPORT +void clutter_pick_context_pop_clip (ClutterPickContext *pick_context); + +CLUTTER_EXPORT +void clutter_pick_context_push_transform (ClutterPickContext *pick_context, + const graphene_matrix_t *transform); + +CLUTTER_EXPORT +void clutter_pick_context_get_transform (ClutterPickContext *pick_context, + graphene_matrix_t *out_matrix); + +CLUTTER_EXPORT +void clutter_pick_context_pop_transform (ClutterPickContext *pick_context); + #endif /* CLUTTER_PICK_CONTEXT_H */ diff --git a/clutter/clutter/clutter-pick-stack-private.h b/clutter/clutter/clutter-pick-stack-private.h new file mode 100644 index 0000000000..1be943aaea --- /dev/null +++ b/clutter/clutter/clutter-pick-stack-private.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2020 Endless OS Foundation, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#ifndef CLUTTER_PICK_STACK_PRIVATE_H +#define CLUTTER_PICK_STACK_PRIVATE_H + +#include + +#include "clutter-macros.h" +#include "clutter-stage-view.h" + +G_BEGIN_DECLS + +#define CLUTTER_TYPE_PICK_STACK (clutter_pick_stack_get_type ()) + +typedef struct _ClutterPickStack ClutterPickStack; + +GType clutter_pick_stack_get_type (void) G_GNUC_CONST; + +ClutterPickStack * clutter_pick_stack_new (CoglContext *context); + +ClutterPickStack * clutter_pick_stack_ref (ClutterPickStack *pick_stack); + +void clutter_pick_stack_unref (ClutterPickStack *pick_stack); + +void clutter_pick_stack_seal (ClutterPickStack *pick_stack); + +void clutter_pick_stack_log_pick (ClutterPickStack *pick_stack, + const graphene_point_t vertices[4], + ClutterActor *actor); + +void clutter_pick_stack_push_clip (ClutterPickStack *pick_stack, + const graphene_point_t vertices[4]); + +void clutter_pick_stack_pop_clip (ClutterPickStack *pick_stack); + +void clutter_pick_stack_push_transform (ClutterPickStack *pick_stack, + const graphene_matrix_t *transform); + +void clutter_pick_stack_get_transform (ClutterPickStack *pick_stack, + graphene_matrix_t *out_transform); + +void clutter_pick_stack_pop_transform (ClutterPickStack *pick_stack); + +ClutterActor * clutter_pick_stack_find_actor_at (ClutterPickStack *pick_stack, + float x, + float y); + +G_END_DECLS + +#endif /* CLUTTER_PICK_STACK_PRIVATE_H */ diff --git a/clutter/clutter/clutter-pick-stack.c b/clutter/clutter/clutter-pick-stack.c new file mode 100644 index 0000000000..1f0c47a214 --- /dev/null +++ b/clutter/clutter/clutter-pick-stack.c @@ -0,0 +1,386 @@ +/* + * Copyright (C) 2020 Endless OS Foundation, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#include "clutter-pick-stack-private.h" +#include "clutter-private.h" + +typedef struct +{ + graphene_point_t vertices[4]; + ClutterActor *actor; + int clip_index; +} PickRecord; + +typedef struct +{ + int prev; + graphene_point_t vertices[4]; +} PickClipRecord; + +struct _ClutterPickStack +{ + grefcount ref_count; + + CoglMatrixStack *matrix_stack; + GArray *vertices_stack; + GArray *clip_stack; + int current_clip_stack_top; + + gboolean sealed : 1; +}; + +G_DEFINE_BOXED_TYPE (ClutterPickStack, clutter_pick_stack, + clutter_pick_stack_ref, clutter_pick_stack_unref) + +static gboolean +is_quadrilateral_axis_aligned_rectangle (const graphene_point_t vertices[4]) +{ + int i; + + for (i = 0; i < 4; i++) + { + if (!G_APPROX_VALUE (vertices[i].x, + vertices[(i + 1) % 4].x, + FLT_EPSILON) && + !G_APPROX_VALUE (vertices[i].y, + vertices[(i + 1) % 4].y, + FLT_EPSILON)) + return FALSE; + } + return TRUE; +} + +static gboolean +is_inside_axis_aligned_rectangle (const graphene_point_t *point, + const graphene_point_t vertices[4]) +{ + float min_x = FLT_MAX; + float max_x = -FLT_MAX; + float min_y = FLT_MAX; + float max_y = -FLT_MAX; + int i; + + for (i = 0; i < 4; i++) + { + min_x = MIN (min_x, vertices[i].x); + min_y = MIN (min_y, vertices[i].y); + max_x = MAX (max_x, vertices[i].x); + max_y = MAX (max_y, vertices[i].y); + } + + return (point->x >= min_x && + point->y >= min_y && + point->x < max_x && + point->y < max_y); +} + +static int +clutter_point_compare_line (const graphene_point_t *p, + const graphene_point_t *a, + const graphene_point_t *b) +{ + graphene_vec3_t vec_pa; + graphene_vec3_t vec_pb; + graphene_vec3_t cross; + float cross_z; + + graphene_vec3_init (&vec_pa, p->x - a->x, p->y - a->y, 0.f); + graphene_vec3_init (&vec_pb, p->x - b->x, p->y - b->y, 0.f); + graphene_vec3_cross (&vec_pa, &vec_pb, &cross); + cross_z = graphene_vec3_get_z (&cross); + + if (cross_z > 0.f) + return 1; + else if (cross_z < 0.f) + return -1; + else + return 0; +} + +static gboolean +is_inside_unaligned_rectangle (const graphene_point_t *point, + const graphene_point_t vertices[4]) +{ + unsigned int i; + int first_side; + + first_side = 0; + + for (i = 0; i < 4; i++) + { + int side; + + side = clutter_point_compare_line (point, + &vertices[i], + &vertices[(i + 1) % 4]); + + if (side) + { + if (first_side == 0) + first_side = side; + else if (side != first_side) + return FALSE; + } + } + + if (first_side == 0) + return FALSE; + + return TRUE; +} + +static gboolean +is_inside_input_region (const graphene_point_t *point, + const graphene_point_t vertices[4]) +{ + + if (is_quadrilateral_axis_aligned_rectangle (vertices)) + return is_inside_axis_aligned_rectangle (point, vertices); + else + return is_inside_unaligned_rectangle (point, vertices); +} + +static gboolean +pick_record_contains_point (ClutterPickStack *pick_stack, + const PickRecord *rec, + float x, + float y) +{ + const graphene_point_t point = GRAPHENE_POINT_INIT (x, y); + int clip_index; + + if (!is_inside_input_region (&point, rec->vertices)) + return FALSE; + + clip_index = rec->clip_index; + while (clip_index >= 0) + { + const PickClipRecord *clip = + &g_array_index (pick_stack->clip_stack, PickClipRecord, clip_index); + + if (!is_inside_input_region (&point, clip->vertices)) + return FALSE; + + clip_index = clip->prev; + } + + return TRUE; +} + +static void +add_pick_stack_weak_refs (ClutterPickStack *pick_stack) +{ + int i; + + g_assert (!pick_stack->sealed); + + for (i = 0; i < pick_stack->vertices_stack->len; i++) + { + PickRecord *rec = + &g_array_index (pick_stack->vertices_stack, PickRecord, i); + + if (rec->actor) + g_object_add_weak_pointer (G_OBJECT (rec->actor), + (gpointer) &rec->actor); + } +} + +static void +remove_pick_stack_weak_refs (ClutterPickStack *pick_stack) +{ + int i; + + for (i = 0; i < pick_stack->vertices_stack->len; i++) + { + PickRecord *rec = + &g_array_index (pick_stack->vertices_stack, PickRecord, i); + + if (rec->actor) + g_object_remove_weak_pointer (G_OBJECT (rec->actor), + (gpointer) &rec->actor); + } +} + +static void +clutter_pick_stack_dispose (ClutterPickStack *pick_stack) +{ + remove_pick_stack_weak_refs (pick_stack); + g_clear_pointer (&pick_stack->matrix_stack, cogl_object_unref); + g_clear_pointer (&pick_stack->vertices_stack, g_array_unref); + g_clear_pointer (&pick_stack->clip_stack, g_array_unref); +} + +/** + * clutter_pick_stack_new: + * @context: a #CoglContext + * + * Creates a new #ClutterPickStack. + * + * Returns: (transfer full): A newly created #ClutterPickStack + */ +ClutterPickStack * +clutter_pick_stack_new (CoglContext *context) +{ + ClutterPickStack *pick_stack; + + pick_stack = g_new0 (ClutterPickStack, 1); + g_ref_count_init (&pick_stack->ref_count); + pick_stack->matrix_stack = cogl_matrix_stack_new (context); + pick_stack->vertices_stack = g_array_new (FALSE, FALSE, sizeof (PickRecord)); + pick_stack->clip_stack = g_array_new (FALSE, FALSE, sizeof (PickClipRecord)); + pick_stack->current_clip_stack_top = -1; + + return pick_stack; +} + +/** + * clutter_pick_stack_ref: + * @pick_stack: A #ClutterPickStack + * + * Increments the reference count of @pick_stack by one. + * + * Returns: (transfer full): @pick_stack + */ +ClutterPickStack * +clutter_pick_stack_ref (ClutterPickStack *pick_stack) +{ + g_ref_count_inc (&pick_stack->ref_count); + return pick_stack; +} + +/** + * clutter_pick_stack_unref: + * @pick_stack: A #ClutterPickStack + * + * Decrements the reference count of @pick_stack by one, freeing the structure + * when the reference count reaches zero. + */ +void +clutter_pick_stack_unref (ClutterPickStack *pick_stack) +{ + if (g_ref_count_dec (&pick_stack->ref_count)) + { + clutter_pick_stack_dispose (pick_stack); + g_free (pick_stack); + } +} + +void +clutter_pick_stack_seal (ClutterPickStack *pick_stack) +{ + g_assert (!pick_stack->sealed); + add_pick_stack_weak_refs (pick_stack); + pick_stack->sealed = TRUE; +} + +void +clutter_pick_stack_log_pick (ClutterPickStack *pick_stack, + const graphene_point_t vertices[4], + ClutterActor *actor) +{ + PickRecord rec; + + g_return_if_fail (actor != NULL); + + g_assert (!pick_stack->sealed); + + memcpy (rec.vertices, vertices, 4 * sizeof (graphene_point_t)); + rec.actor = actor; + rec.clip_index = pick_stack->current_clip_stack_top; + + g_array_append_val (pick_stack->vertices_stack, rec); +} + +void +clutter_pick_stack_push_clip (ClutterPickStack *pick_stack, + const graphene_point_t vertices[4]) +{ + PickClipRecord clip; + + g_assert (!pick_stack->sealed); + + clip.prev = pick_stack->current_clip_stack_top; + memcpy (clip.vertices, vertices, 4 * sizeof (graphene_point_t)); + + g_array_append_val (pick_stack->clip_stack, clip); + pick_stack->current_clip_stack_top = pick_stack->clip_stack->len - 1; +} + +void +clutter_pick_stack_pop_clip (ClutterPickStack *pick_stack) +{ + const PickClipRecord *top; + + g_assert (!pick_stack->sealed); + g_assert (pick_stack->current_clip_stack_top >= 0); + + /* Individual elements of clip_stack are not freed. This is so they can + * be shared as part of a tree of different stacks used by different + * actors in the pick_stack. The whole clip_stack does however get + * freed later in clutter_pick_stack_dispose. + */ + + top = &g_array_index (pick_stack->clip_stack, + PickClipRecord, + pick_stack->current_clip_stack_top); + + pick_stack->current_clip_stack_top = top->prev; +} + +void +clutter_pick_stack_push_transform (ClutterPickStack *pick_stack, + const graphene_matrix_t *transform) +{ + cogl_matrix_stack_push (pick_stack->matrix_stack); + cogl_matrix_stack_multiply (pick_stack->matrix_stack, transform); +} + +void +clutter_pick_stack_get_transform (ClutterPickStack *pick_stack, + graphene_matrix_t *out_transform) +{ + cogl_matrix_stack_get (pick_stack->matrix_stack, out_transform); +} + +void +clutter_pick_stack_pop_transform (ClutterPickStack *pick_stack) +{ + cogl_matrix_stack_pop (pick_stack->matrix_stack); +} + +ClutterActor * +clutter_pick_stack_find_actor_at (ClutterPickStack *pick_stack, + float x, + float y) +{ + int i; + + /* Search all "painted" pickable actors from front to back. A linear search + * is required, and also performs fine since there is typically only + * on the order of dozens of actors in the list (on screen) at a time. + */ + for (i = pick_stack->vertices_stack->len - 1; i >= 0; i--) + { + const PickRecord *rec = + &g_array_index (pick_stack->vertices_stack, PickRecord, i); + + if (rec->actor && pick_record_contains_point (pick_stack, rec, x, y)) + return rec->actor; + } + + return NULL; +} diff --git a/clutter/clutter/clutter-stage-private.h b/clutter/clutter/clutter-stage-private.h index a80539666b..85f54e4976 100644 --- a/clutter/clutter/clutter-stage-private.h +++ b/clutter/clutter/clutter-stage-private.h @@ -81,15 +81,6 @@ void _clutter_stage_process_queued_events (ClutterStage *stage); void _clutter_stage_update_input_devices (ClutterStage *stage); gboolean _clutter_stage_has_full_redraw_queued (ClutterStage *stage); -void clutter_stage_log_pick (ClutterStage *stage, - const graphene_point_t *vertices, - ClutterActor *actor); - -void clutter_stage_push_pick_clip (ClutterStage *stage, - const graphene_point_t *vertices); - -void clutter_stage_pop_pick_clip (ClutterStage *stage); - ClutterActor *_clutter_stage_do_pick (ClutterStage *stage, float x, float y, diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c index 53a8ec9b8c..350b1b4a3e 100644 --- a/clutter/clutter/clutter-stage.c +++ b/clutter/clutter/clutter-stage.c @@ -125,10 +125,7 @@ struct _ClutterStagePrivate GTimer *fps_timer; gint32 timer_n_frames; - GArray *pick_stack; - GArray *pick_clip_stack; - int pick_clip_stack_top; - gboolean pick_stack_frozen; + ClutterPickStack *pick_stack; ClutterPickMode cached_pick_mode; #ifdef CLUTTER_ENABLE_DEBUG @@ -237,266 +234,15 @@ clutter_stage_get_preferred_height (ClutterActor *self, *natural_height_p = geom.height; } -static void -add_pick_stack_weak_refs (ClutterStage *stage) -{ - ClutterStagePrivate *priv = stage->priv; - int i; - - if (priv->pick_stack_frozen) - return; - - for (i = 0; i < priv->pick_stack->len; i++) - { - PickRecord *rec = &g_array_index (priv->pick_stack, PickRecord, i); - - if (rec->actor) - g_object_add_weak_pointer (G_OBJECT (rec->actor), - (gpointer) &rec->actor); - } - - priv->pick_stack_frozen = TRUE; -} - -static void -remove_pick_stack_weak_refs (ClutterStage *stage) -{ - ClutterStagePrivate *priv = stage->priv; - int i; - - if (!priv->pick_stack_frozen) - return; - - for (i = 0; i < priv->pick_stack->len; i++) - { - PickRecord *rec = &g_array_index (priv->pick_stack, PickRecord, i); - - if (rec->actor) - g_object_remove_weak_pointer (G_OBJECT (rec->actor), - (gpointer) &rec->actor); - } - - priv->pick_stack_frozen = FALSE; -} - static void _clutter_stage_clear_pick_stack (ClutterStage *stage) { ClutterStagePrivate *priv = stage->priv; - remove_pick_stack_weak_refs (stage); - g_array_set_size (priv->pick_stack, 0); - g_array_set_size (priv->pick_clip_stack, 0); - priv->pick_clip_stack_top = -1; + g_clear_pointer (&priv->pick_stack, clutter_pick_stack_unref); priv->cached_pick_mode = CLUTTER_PICK_NONE; } -void -clutter_stage_log_pick (ClutterStage *stage, - const graphene_point_t *vertices, - ClutterActor *actor) -{ - ClutterStagePrivate *priv; - PickRecord rec; - - g_return_if_fail (CLUTTER_IS_STAGE (stage)); - g_return_if_fail (actor != NULL); - - priv = stage->priv; - - g_assert (!priv->pick_stack_frozen); - - memcpy (rec.vertex, vertices, 4 * sizeof (graphene_point_t)); - rec.actor = actor; - rec.clip_stack_top = priv->pick_clip_stack_top; - - g_array_append_val (priv->pick_stack, rec); -} - -void -clutter_stage_push_pick_clip (ClutterStage *stage, - const graphene_point_t *vertices) -{ - ClutterStagePrivate *priv; - PickClipRecord clip; - - g_return_if_fail (CLUTTER_IS_STAGE (stage)); - - priv = stage->priv; - - g_assert (!priv->pick_stack_frozen); - - clip.prev = priv->pick_clip_stack_top; - memcpy (clip.vertex, vertices, 4 * sizeof (graphene_point_t)); - - g_array_append_val (priv->pick_clip_stack, clip); - priv->pick_clip_stack_top = priv->pick_clip_stack->len - 1; -} - -void -clutter_stage_pop_pick_clip (ClutterStage *stage) -{ - ClutterStagePrivate *priv; - const PickClipRecord *top; - - g_return_if_fail (CLUTTER_IS_STAGE (stage)); - - priv = stage->priv; - - g_assert (!priv->pick_stack_frozen); - g_assert (priv->pick_clip_stack_top >= 0); - - /* Individual elements of pick_clip_stack are not freed. This is so they - * can be shared as part of a tree of different stacks used by different - * actors in the pick_stack. The whole pick_clip_stack does however get - * freed later in _clutter_stage_clear_pick_stack. - */ - - top = &g_array_index (priv->pick_clip_stack, - PickClipRecord, - priv->pick_clip_stack_top); - - priv->pick_clip_stack_top = top->prev; -} - -static gboolean -is_quadrilateral_axis_aligned_rectangle (const graphene_point_t *vertices) -{ - int i; - - for (i = 0; i < 4; i++) - { - if (!G_APPROX_VALUE (vertices[i].x, - vertices[(i + 1) % 4].x, - FLT_EPSILON) && - !G_APPROX_VALUE (vertices[i].y, - vertices[(i + 1) % 4].y, - FLT_EPSILON)) - return FALSE; - } - return TRUE; -} - -static gboolean -is_inside_axis_aligned_rectangle (const graphene_point_t *point, - const graphene_point_t *vertices) -{ - float min_x = FLT_MAX; - float max_x = -FLT_MAX; - float min_y = FLT_MAX; - float max_y = -FLT_MAX; - int i; - - for (i = 0; i < 3; i++) - { - min_x = MIN (min_x, vertices[i].x); - min_y = MIN (min_y, vertices[i].y); - max_x = MAX (max_x, vertices[i].x); - max_y = MAX (max_y, vertices[i].y); - } - - return (point->x >= min_x && - point->y >= min_y && - point->x < max_x && - point->y < max_y); -} - -static int -clutter_point_compare_line (const graphene_point_t *p, - const graphene_point_t *a, - const graphene_point_t *b) -{ - graphene_vec3_t vec_pa; - graphene_vec3_t vec_pb; - graphene_vec3_t cross; - float cross_z; - - graphene_vec3_init (&vec_pa, p->x - a->x, p->y - a->y, 0.f); - graphene_vec3_init (&vec_pb, p->x - b->x, p->y - b->y, 0.f); - graphene_vec3_cross (&vec_pa, &vec_pb, &cross); - cross_z = graphene_vec3_get_z (&cross); - - if (cross_z > 0.f) - return 1; - else if (cross_z < 0.f) - return -1; - else - return 0; -} - -static gboolean -is_inside_unaligned_rectangle (const graphene_point_t *point, - const graphene_point_t *vertices) -{ - unsigned int i; - int first_side; - - first_side = 0; - - for (i = 0; i < 4; i++) - { - int side; - - side = clutter_point_compare_line (point, - &vertices[i], - &vertices[(i + 1) % 4]); - - if (side) - { - if (first_side == 0) - first_side = side; - else if (side != first_side) - return FALSE; - } - } - - if (first_side == 0) - return FALSE; - - return TRUE; -} - -static gboolean -is_inside_input_region (const graphene_point_t *point, - const graphene_point_t *vertices) -{ - - if (is_quadrilateral_axis_aligned_rectangle (vertices)) - return is_inside_axis_aligned_rectangle (point, vertices); - else - return is_inside_unaligned_rectangle (point, vertices); -} - -static gboolean -pick_record_contains_point (ClutterStage *stage, - const PickRecord *rec, - float x, - float y) -{ - const graphene_point_t point = GRAPHENE_POINT_INIT (x, y); - ClutterStagePrivate *priv; - int clip_index; - - if (!is_inside_input_region (&point, rec->vertex)) - return FALSE; - - priv = stage->priv; - clip_index = rec->clip_stack_top; - while (clip_index >= 0) - { - const PickClipRecord *clip = &g_array_index (priv->pick_clip_stack, - PickClipRecord, - clip_index); - - if (!is_inside_input_region (&point, clip->vertex)) - return FALSE; - - clip_index = clip->prev; - } - - return TRUE; -} - static void clutter_stage_add_redraw_clip (ClutterStage *stage, cairo_rectangle_int_t *clip) @@ -1392,11 +1138,11 @@ _clutter_stage_do_pick_on_view (ClutterStage *stage, ClutterStageView *view) { ClutterStagePrivate *priv = stage->priv; - int i; + ClutterActor *actor; COGL_TRACE_BEGIN_SCOPED (ClutterStagePickView, "Pick (view)"); - if (mode != priv->cached_pick_mode) + if (!priv->pick_stack || mode != priv->cached_pick_mode) { ClutterPickContext *pick_context; @@ -1405,26 +1151,14 @@ _clutter_stage_do_pick_on_view (ClutterStage *stage, pick_context = clutter_pick_context_new_for_view (view, mode); clutter_actor_pick (CLUTTER_ACTOR (stage), pick_context); + priv->pick_stack = clutter_pick_context_steal_stack (pick_context); priv->cached_pick_mode = mode; clutter_pick_context_destroy (pick_context); - - add_pick_stack_weak_refs (stage); - } - - /* Search all "painted" pickable actors from front to back. A linear search - * is required, and also performs fine since there is typically only - * on the order of dozens of actors in the list (on screen) at a time. - */ - for (i = priv->pick_stack->len - 1; i >= 0; i--) - { - const PickRecord *rec = &g_array_index (priv->pick_stack, PickRecord, i); - - if (rec->actor && pick_record_contains_point (stage, rec, x, y)) - return rec->actor; } - return CLUTTER_ACTOR (stage); + actor = clutter_pick_stack_find_actor_at (priv->pick_stack, x, y); + return actor ? actor : CLUTTER_ACTOR (stage); } /** @@ -1636,8 +1370,6 @@ clutter_stage_finalize (GObject *object) g_array_free (priv->paint_volume_stack, TRUE); _clutter_stage_clear_pick_stack (stage); - g_array_free (priv->pick_clip_stack, TRUE); - g_array_free (priv->pick_stack, TRUE); if (priv->fps_timer != NULL) g_timer_destroy (priv->fps_timer); @@ -1953,9 +1685,6 @@ clutter_stage_init (ClutterStage *self) priv->paint_volume_stack = g_array_new (FALSE, FALSE, sizeof (ClutterPaintVolume)); - priv->pick_stack = g_array_new (FALSE, FALSE, sizeof (PickRecord)); - priv->pick_clip_stack = g_array_new (FALSE, FALSE, sizeof (PickClipRecord)); - priv->pick_clip_stack_top = -1; priv->cached_pick_mode = CLUTTER_PICK_NONE; } diff --git a/clutter/clutter/meson.build b/clutter/clutter/meson.build index 18994df8d1..f914370ec4 100644 --- a/clutter/clutter/meson.build +++ b/clutter/clutter/meson.build @@ -150,6 +150,7 @@ clutter_sources = [ 'clutter-path-constraint.c', 'clutter-path.c', 'clutter-pick-context.c', + 'clutter-pick-stack.c', 'clutter-property-transition.c', 'clutter-rotate-action.c', 'clutter-script.c', -- GitLab From 620f0ad74bee99b977f991aaf96585ff51e6f2bc Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Sat, 17 Oct 2020 09:37:39 -0300 Subject: [PATCH 08/12] clutter/pick-stack: Store current matrix entry This is the beginning of the preparations to passing unprojected rectangles to the clip stack. Store a ref to the current tip of the matrix entry. https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1509 --- clutter/clutter/clutter-pick-stack.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/clutter/clutter/clutter-pick-stack.c b/clutter/clutter/clutter-pick-stack.c index 1f0c47a214..4fd279d37b 100644 --- a/clutter/clutter/clutter-pick-stack.c +++ b/clutter/clutter/clutter-pick-stack.c @@ -23,12 +23,14 @@ typedef struct graphene_point_t vertices[4]; ClutterActor *actor; int clip_index; + CoglMatrixEntry *matrix_entry; } PickRecord; typedef struct { int prev; graphene_point_t vertices[4]; + CoglMatrixEntry *matrix_entry; } PickClipRecord; struct _ClutterPickStack @@ -224,6 +226,20 @@ clutter_pick_stack_dispose (ClutterPickStack *pick_stack) g_clear_pointer (&pick_stack->clip_stack, g_array_unref); } +static void +clear_pick_record (gpointer data) +{ + PickRecord *rec = data; + g_clear_pointer (&rec->matrix_entry, cogl_matrix_entry_unref); +} + +static void +clear_clip_record (gpointer data) +{ + PickClipRecord *clip = data; + g_clear_pointer (&clip->matrix_entry, cogl_matrix_entry_unref); +} + /** * clutter_pick_stack_new: * @context: a #CoglContext @@ -244,6 +260,9 @@ clutter_pick_stack_new (CoglContext *context) pick_stack->clip_stack = g_array_new (FALSE, FALSE, sizeof (PickClipRecord)); pick_stack->current_clip_stack_top = -1; + g_array_set_clear_func (pick_stack->vertices_stack, clear_pick_record); + g_array_set_clear_func (pick_stack->clip_stack, clear_clip_record); + return pick_stack; } @@ -301,6 +320,8 @@ clutter_pick_stack_log_pick (ClutterPickStack *pick_stack, memcpy (rec.vertices, vertices, 4 * sizeof (graphene_point_t)); rec.actor = actor; rec.clip_index = pick_stack->current_clip_stack_top; + rec.matrix_entry = cogl_matrix_stack_get_entry (pick_stack->matrix_stack); + cogl_matrix_entry_ref (rec.matrix_entry); g_array_append_val (pick_stack->vertices_stack, rec); } @@ -315,6 +336,8 @@ clutter_pick_stack_push_clip (ClutterPickStack *pick_stack, clip.prev = pick_stack->current_clip_stack_top; memcpy (clip.vertices, vertices, 4 * sizeof (graphene_point_t)); + clip.matrix_entry = cogl_matrix_stack_get_entry (pick_stack->matrix_stack); + cogl_matrix_entry_ref (clip.matrix_entry); g_array_append_val (pick_stack->clip_stack, clip); pick_stack->current_clip_stack_top = pick_stack->clip_stack->len - 1; -- GitLab From 7d25cbe87fdf8473445120ad650be9ea09eaa692 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Sat, 17 Oct 2020 11:52:45 -0300 Subject: [PATCH 09/12] Use graphene_ray_t to pick actors This commit introduces a few important changes in order to acommodate graphene_ray_t. Most of them are positive changes, so don't panic :) The first very visible change is that neither the actor box nor the clip rectangles are projected before being pushed. This required changing the parameters of the related functions at both ClutterPickContext, and ClutterPickStack, to receive boxes instead of vertices. These rectangles are projected on demand now, so in the best case (first actor picked) only one projection happens; and in the worst case, it projects as much as it does now. The second important change is that there are no more checks for axis-alignment anymore. That's because picking now happens in 3D space, using triangles. Talking about triangles in 3D space, this is what is used now for picking. We break down each actor rectangle in 2 triangles, and check if the projected pick point is inside any one of them, of if the ray intersects any one of them. The same check happens for the clip rectangles. Checking the projected pick point is both an optimization for the 2D case, and a workaround to graphene_ray_t problems with float precision, which is specially visible on edges such as the top bar. https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1509 --- clutter/clutter/clutter-actor.c | 80 +------ clutter/clutter/clutter-pick-context.c | 22 +- clutter/clutter/clutter-pick-context.h | 10 +- clutter/clutter/clutter-pick-stack-private.h | 17 +- clutter/clutter/clutter-pick-stack.c | 206 ++++++++----------- clutter/clutter/clutter-stage.c | 37 +++- 6 files changed, 154 insertions(+), 218 deletions(-) diff --git a/clutter/clutter/clutter-actor.c b/clutter/clutter/clutter-actor.c index 867b54c477..1c28ec6073 100644 --- a/clutter/clutter/clutter-actor.c +++ b/clutter/clutter/clutter-actor.c @@ -1243,57 +1243,6 @@ clutter_actor_verify_map_state (ClutterActor *self) #endif /* CLUTTER_ENABLE_DEBUG */ -static gboolean -_clutter_actor_transform_local_box_to_stage (ClutterActor *self, - ClutterStage *stage, - ClutterPickContext *pick_context, - const ClutterActorBox *box, - graphene_point_t vertices[4]) -{ - ClutterActor *stage_actor = CLUTTER_ACTOR (stage); - ClutterActorPrivate *stage_priv = stage_actor->priv; - graphene_matrix_t modelview, transform_to_stage; - int v; - - ensure_valid_actor_transform (stage_actor); - - if (!stage_priv->has_inverse_transform) - return FALSE; - clutter_pick_context_get_transform (pick_context, &modelview); - graphene_matrix_multiply (&modelview, - &stage_priv->inverse_transform, - &transform_to_stage); - - vertices[0].x = box->x1; - vertices[0].y = box->y1; - - vertices[1].x = box->x2; - vertices[1].y = box->y1; - - vertices[2].x = box->x2; - vertices[2].y = box->y2; - - vertices[3].x = box->x1; - vertices[3].y = box->y2; - - for (v = 0; v < 4; v++) - { - float z = 0.f; - float w = 1.f; - - cogl_graphene_matrix_project_point (&transform_to_stage, - &vertices[v].x, - &vertices[v].y, - &z, - &w); - - clutter_round_to_256ths (&vertices[v].x); - clutter_round_to_256ths (&vertices[v].y); - } - - return TRUE; -} - /** * clutter_actor_pick_box: * @self: The #ClutterActor being "pick" painted. @@ -1311,38 +1260,13 @@ clutter_actor_pick_box (ClutterActor *self, ClutterPickContext *pick_context, const ClutterActorBox *box) { - ClutterStage *stage; - graphene_point_t vertices[4]; - g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (box != NULL); if (box->x1 >= box->x2 || box->y1 >= box->y2) return; - stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (self)); - - if (_clutter_actor_transform_local_box_to_stage (self, stage, pick_context, - box, vertices)) - clutter_pick_context_log_pick (pick_context, vertices, self); -} - -static gboolean -_clutter_actor_push_pick_clip (ClutterActor *self, - ClutterPickContext *pick_context, - const ClutterActorBox *clip) -{ - ClutterStage *stage; - graphene_point_t vertices[4]; - - stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (self)); - - if (!_clutter_actor_transform_local_box_to_stage (self, stage, pick_context, - clip, vertices)) - return FALSE; - - clutter_pick_context_push_clip (pick_context, vertices); - return TRUE; + clutter_pick_context_log_pick (pick_context, box, self); } static void @@ -4054,7 +3978,7 @@ clutter_actor_pick (ClutterActor *actor, } if (clip_set) - clip_set = _clutter_actor_push_pick_clip (actor, pick_context, &clip); + clutter_pick_context_push_clip (pick_context, &clip); priv->next_effect_to_paint = NULL; if (priv->effects) diff --git a/clutter/clutter/clutter-pick-context.c b/clutter/clutter/clutter-pick-context.c index 29027284aa..7348ec57c6 100644 --- a/clutter/clutter/clutter-pick-context.c +++ b/clutter/clutter/clutter-pick-context.c @@ -110,32 +110,32 @@ clutter_pick_context_steal_stack (ClutterPickContext *pick_context) /** * clutter_pick_context_log_pick: * @pick_context: a #ClutterPickContext - * @vertices: (array fixed-size=4): array of #graphene_point_t + * @box: a #ClutterActorBox * @actor: a #ClutterActor * * Logs a pick rectangle into the pick stack. */ void -clutter_pick_context_log_pick (ClutterPickContext *pick_context, - const graphene_point_t vertices[4], - ClutterActor *actor) +clutter_pick_context_log_pick (ClutterPickContext *pick_context, + const ClutterActorBox *box, + ClutterActor *actor) { - clutter_pick_stack_log_pick (pick_context->pick_stack, vertices, actor); + clutter_pick_stack_log_pick (pick_context->pick_stack, box, actor); } /** * clutter_pick_context_push_clip: * @pick_context: a #ClutterPickContext - * @vertices: (array fixed-size=4): array of #graphene_point_t + * @box: a #ClutterActorBox * - * Pushes a clip rectangle defined by @vertices into the pick stack. - * Pop with clutter_pick_context_pop_clip() when done. + * Pushes a clip rectangle defined by @box into the pick stack. Pop with + * clutter_pick_context_pop_clip() when done. */ void -clutter_pick_context_push_clip (ClutterPickContext *pick_context, - const graphene_point_t vertices[4]) +clutter_pick_context_push_clip (ClutterPickContext *pick_context, + const ClutterActorBox *box) { - clutter_pick_stack_push_clip (pick_context->pick_stack, vertices); + clutter_pick_stack_push_clip (pick_context->pick_stack, box); } /** diff --git a/clutter/clutter/clutter-pick-context.h b/clutter/clutter/clutter-pick-context.h index 4afa25af71..0faf028256 100644 --- a/clutter/clutter/clutter-pick-context.h +++ b/clutter/clutter/clutter-pick-context.h @@ -50,13 +50,13 @@ CLUTTER_EXPORT ClutterPickMode clutter_pick_context_get_mode (ClutterPickContext *pick_context); CLUTTER_EXPORT -void clutter_pick_context_log_pick (ClutterPickContext *pick_context, - const graphene_point_t vertices[4], - ClutterActor *actor); +void clutter_pick_context_log_pick (ClutterPickContext *pick_context, + const ClutterActorBox *box, + ClutterActor *actor); CLUTTER_EXPORT -void clutter_pick_context_push_clip (ClutterPickContext *pick_context, - const graphene_point_t vertices[4]); +void clutter_pick_context_push_clip (ClutterPickContext *pick_context, + const ClutterActorBox *box); CLUTTER_EXPORT void clutter_pick_context_pop_clip (ClutterPickContext *pick_context); diff --git a/clutter/clutter/clutter-pick-stack-private.h b/clutter/clutter/clutter-pick-stack-private.h index 1be943aaea..159b8c4ebb 100644 --- a/clutter/clutter/clutter-pick-stack-private.h +++ b/clutter/clutter/clutter-pick-stack-private.h @@ -39,12 +39,12 @@ void clutter_pick_stack_unref (ClutterPickStack *pick_stack); void clutter_pick_stack_seal (ClutterPickStack *pick_stack); -void clutter_pick_stack_log_pick (ClutterPickStack *pick_stack, - const graphene_point_t vertices[4], - ClutterActor *actor); +void clutter_pick_stack_log_pick (ClutterPickStack *pick_stack, + const ClutterActorBox *box, + ClutterActor *actor); -void clutter_pick_stack_push_clip (ClutterPickStack *pick_stack, - const graphene_point_t vertices[4]); +void clutter_pick_stack_push_clip (ClutterPickStack *pick_stack, + const ClutterActorBox *box); void clutter_pick_stack_pop_clip (ClutterPickStack *pick_stack); @@ -56,9 +56,10 @@ void clutter_pick_stack_get_transform (ClutterPickStack *pick_stack, void clutter_pick_stack_pop_transform (ClutterPickStack *pick_stack); -ClutterActor * clutter_pick_stack_find_actor_at (ClutterPickStack *pick_stack, - float x, - float y); +ClutterActor * +clutter_pick_stack_search_actor (ClutterPickStack *pick_stack, + const graphene_point3d_t *point, + const graphene_ray_t *ray); G_END_DECLS diff --git a/clutter/clutter/clutter-pick-stack.c b/clutter/clutter/clutter-pick-stack.c index 4fd279d37b..6a0b13e58b 100644 --- a/clutter/clutter/clutter-pick-stack.c +++ b/clutter/clutter/clutter-pick-stack.c @@ -20,17 +20,23 @@ typedef struct { - graphene_point_t vertices[4]; + graphene_point3d_t vertices[4]; + CoglMatrixEntry *matrix_entry; + ClutterActorBox rect; + gboolean projected; +} Record; + +typedef struct +{ + Record base; ClutterActor *actor; int clip_index; - CoglMatrixEntry *matrix_entry; } PickRecord; typedef struct { + Record base; int prev; - graphene_point_t vertices[4]; - CoglMatrixEntry *matrix_entry; } PickClipRecord; struct _ClutterPickStack @@ -48,133 +54,101 @@ struct _ClutterPickStack G_DEFINE_BOXED_TYPE (ClutterPickStack, clutter_pick_stack, clutter_pick_stack_ref, clutter_pick_stack_unref) -static gboolean -is_quadrilateral_axis_aligned_rectangle (const graphene_point_t vertices[4]) +static void +project_vertices (CoglMatrixEntry *matrix_entry, + const ClutterActorBox *box, + graphene_point3d_t vertices[4]) { + graphene_matrix_t m; int i; - for (i = 0; i < 4; i++) - { - if (!G_APPROX_VALUE (vertices[i].x, - vertices[(i + 1) % 4].x, - FLT_EPSILON) && - !G_APPROX_VALUE (vertices[i].y, - vertices[(i + 1) % 4].y, - FLT_EPSILON)) - return FALSE; - } - return TRUE; -} + cogl_matrix_entry_get (matrix_entry, &m); -static gboolean -is_inside_axis_aligned_rectangle (const graphene_point_t *point, - const graphene_point_t vertices[4]) -{ - float min_x = FLT_MAX; - float max_x = -FLT_MAX; - float min_y = FLT_MAX; - float max_y = -FLT_MAX; - int i; + graphene_point3d_init (&vertices[0], box->x1, box->y1, 0.f); + graphene_point3d_init (&vertices[1], box->x2, box->y1, 0.f); + graphene_point3d_init (&vertices[2], box->x2, box->y2, 0.f); + graphene_point3d_init (&vertices[3], box->x1, box->y2, 0.f); for (i = 0; i < 4; i++) { - min_x = MIN (min_x, vertices[i].x); - min_y = MIN (min_y, vertices[i].y); - max_x = MAX (max_x, vertices[i].x); - max_y = MAX (max_y, vertices[i].y); - } + float w = 1.f; - return (point->x >= min_x && - point->y >= min_y && - point->x < max_x && - point->y < max_y); + cogl_graphene_matrix_project_point (&m, + &vertices[i].x, + &vertices[i].y, + &vertices[i].z, + &w); + } } -static int -clutter_point_compare_line (const graphene_point_t *p, - const graphene_point_t *a, - const graphene_point_t *b) +static void +maybe_project_record (Record *rec) { - graphene_vec3_t vec_pa; - graphene_vec3_t vec_pb; - graphene_vec3_t cross; - float cross_z; - - graphene_vec3_init (&vec_pa, p->x - a->x, p->y - a->y, 0.f); - graphene_vec3_init (&vec_pb, p->x - b->x, p->y - b->y, 0.f); - graphene_vec3_cross (&vec_pa, &vec_pb, &cross); - cross_z = graphene_vec3_get_z (&cross); - - if (cross_z > 0.f) - return 1; - else if (cross_z < 0.f) - return -1; - else - return 0; + if (!rec->projected) + { + project_vertices (rec->matrix_entry, &rec->rect, rec->vertices); + rec->projected = TRUE; + } } static gboolean -is_inside_unaligned_rectangle (const graphene_point_t *point, - const graphene_point_t vertices[4]) +ray_intersects_input_region (Record *rec, + const graphene_ray_t *ray, + const graphene_point3d_t *point) { - unsigned int i; - int first_side; - - first_side = 0; - - for (i = 0; i < 4; i++) - { - int side; - - side = clutter_point_compare_line (point, - &vertices[i], - &vertices[(i + 1) % 4]); - - if (side) - { - if (first_side == 0) - first_side = side; - else if (side != first_side) - return FALSE; - } - } + graphene_triangle_t t0, t1; + + maybe_project_record (rec); + + /* + * Degrade the projected quad into the following triangles: + * + * 0 -------------- 1 + * | • | + * | • t0 | + * | • | + * | t1 • | + * | • | + * 3 -------------- 2 + */ - if (first_side == 0) - return FALSE; + graphene_triangle_init_from_point3d (&t0, + &rec->vertices[0], + &rec->vertices[1], + &rec->vertices[2]); - return TRUE; -} + graphene_triangle_init_from_point3d (&t1, + &rec->vertices[0], + &rec->vertices[2], + &rec->vertices[3]); -static gboolean -is_inside_input_region (const graphene_point_t *point, - const graphene_point_t vertices[4]) -{ + if (graphene_triangle_contains_point (&t0, point) || + graphene_triangle_contains_point (&t1, point) || + graphene_ray_intersects_triangle (ray, &t0) || + graphene_ray_intersects_triangle (ray, &t1)) + return TRUE; - if (is_quadrilateral_axis_aligned_rectangle (vertices)) - return is_inside_axis_aligned_rectangle (point, vertices); - else - return is_inside_unaligned_rectangle (point, vertices); + return FALSE; } static gboolean -pick_record_contains_point (ClutterPickStack *pick_stack, - const PickRecord *rec, - float x, - float y) +ray_intersects_record (ClutterPickStack *pick_stack, + PickRecord *rec, + const graphene_point3d_t *point, + const graphene_ray_t *ray) { - const graphene_point_t point = GRAPHENE_POINT_INIT (x, y); int clip_index; - if (!is_inside_input_region (&point, rec->vertices)) + if (!ray_intersects_input_region (&rec->base, ray, point)) return FALSE; clip_index = rec->clip_index; while (clip_index >= 0) { - const PickClipRecord *clip = + PickClipRecord *clip = &g_array_index (pick_stack->clip_stack, PickClipRecord, clip_index); - if (!is_inside_input_region (&point, clip->vertices)) + if (!ray_intersects_input_region (&clip->base, ray, point)) return FALSE; clip_index = clip->prev; @@ -230,14 +204,14 @@ static void clear_pick_record (gpointer data) { PickRecord *rec = data; - g_clear_pointer (&rec->matrix_entry, cogl_matrix_entry_unref); + g_clear_pointer (&rec->base.matrix_entry, cogl_matrix_entry_unref); } static void clear_clip_record (gpointer data) { PickClipRecord *clip = data; - g_clear_pointer (&clip->matrix_entry, cogl_matrix_entry_unref); + g_clear_pointer (&clip->base.matrix_entry, cogl_matrix_entry_unref); } /** @@ -308,7 +282,7 @@ clutter_pick_stack_seal (ClutterPickStack *pick_stack) void clutter_pick_stack_log_pick (ClutterPickStack *pick_stack, - const graphene_point_t vertices[4], + const ClutterActorBox *box, ClutterActor *actor) { PickRecord rec; @@ -317,27 +291,29 @@ clutter_pick_stack_log_pick (ClutterPickStack *pick_stack, g_assert (!pick_stack->sealed); - memcpy (rec.vertices, vertices, 4 * sizeof (graphene_point_t)); rec.actor = actor; rec.clip_index = pick_stack->current_clip_stack_top; - rec.matrix_entry = cogl_matrix_stack_get_entry (pick_stack->matrix_stack); - cogl_matrix_entry_ref (rec.matrix_entry); + rec.base.rect = *box; + rec.base.projected = FALSE; + rec.base.matrix_entry = cogl_matrix_stack_get_entry (pick_stack->matrix_stack); + cogl_matrix_entry_ref (rec.base.matrix_entry); g_array_append_val (pick_stack->vertices_stack, rec); } void -clutter_pick_stack_push_clip (ClutterPickStack *pick_stack, - const graphene_point_t vertices[4]) +clutter_pick_stack_push_clip (ClutterPickStack *pick_stack, + const ClutterActorBox *box) { PickClipRecord clip; g_assert (!pick_stack->sealed); clip.prev = pick_stack->current_clip_stack_top; - memcpy (clip.vertices, vertices, 4 * sizeof (graphene_point_t)); - clip.matrix_entry = cogl_matrix_stack_get_entry (pick_stack->matrix_stack); - cogl_matrix_entry_ref (clip.matrix_entry); + clip.base.rect = *box; + clip.base.projected = FALSE; + clip.base.matrix_entry = cogl_matrix_stack_get_entry (pick_stack->matrix_stack); + cogl_matrix_entry_ref (clip.base.matrix_entry); g_array_append_val (pick_stack->clip_stack, clip); pick_stack->current_clip_stack_top = pick_stack->clip_stack->len - 1; @@ -386,9 +362,9 @@ clutter_pick_stack_pop_transform (ClutterPickStack *pick_stack) } ClutterActor * -clutter_pick_stack_find_actor_at (ClutterPickStack *pick_stack, - float x, - float y) +clutter_pick_stack_search_actor (ClutterPickStack *pick_stack, + const graphene_point3d_t *point, + const graphene_ray_t *ray) { int i; @@ -398,10 +374,10 @@ clutter_pick_stack_find_actor_at (ClutterPickStack *pick_stack, */ for (i = pick_stack->vertices_stack->len - 1; i >= 0; i--) { - const PickRecord *rec = + PickRecord *rec = &g_array_index (pick_stack->vertices_stack, PickRecord, i); - if (rec->actor && pick_record_contains_point (pick_stack, rec, x, y)) + if (rec->actor && ray_intersects_record (pick_stack, rec, point, ray)) return rec->actor; } diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c index 350b1b4a3e..e70bd1846d 100644 --- a/clutter/clutter/clutter-stage.c +++ b/clutter/clutter/clutter-stage.c @@ -1130,6 +1130,37 @@ _clutter_stage_has_full_redraw_queued (ClutterStage *stage) return is_full_stage_redraw_queued (stage); } +static void +setup_ray_for_coordinates (ClutterStage *stage, + float x, + float y, + graphene_point3d_t *point, + graphene_ray_t *ray) +{ + ClutterStagePrivate *priv = stage->priv; + graphene_point3d_t camera_position; + graphene_point3d_t p; + graphene_vec3_t direction; + graphene_vec3_t cv; + graphene_vec3_t v; + + camera_position = GRAPHENE_POINT3D_INIT_ZERO; + graphene_vec3_init (&cv, + camera_position.x, + camera_position.y, + camera_position.z); + + p = GRAPHENE_POINT3D_INIT (x, y, 0.f); + graphene_matrix_transform_point3d (&priv->view, &p, &p); + + graphene_vec3_init (&v, p.x, p.y, p.z); + graphene_vec3_subtract (&v, &cv, &direction); + graphene_vec3_normalize (&direction, &direction); + + graphene_ray_init (ray, &camera_position, &direction); + graphene_point3d_init_from_point (point, &p); +} + static ClutterActor * _clutter_stage_do_pick_on_view (ClutterStage *stage, float x, @@ -1138,6 +1169,8 @@ _clutter_stage_do_pick_on_view (ClutterStage *stage, ClutterStageView *view) { ClutterStagePrivate *priv = stage->priv; + graphene_point3d_t p; + graphene_ray_t ray; ClutterActor *actor; COGL_TRACE_BEGIN_SCOPED (ClutterStagePickView, "Pick (view)"); @@ -1157,7 +1190,9 @@ _clutter_stage_do_pick_on_view (ClutterStage *stage, clutter_pick_context_destroy (pick_context); } - actor = clutter_pick_stack_find_actor_at (priv->pick_stack, x, y); + setup_ray_for_coordinates (stage, x, y, &p, &ray); + + actor = clutter_pick_stack_search_actor (priv->pick_stack, &p, &ray); return actor ? actor : CLUTTER_ACTOR (stage); } -- GitLab From 54db1b2fa2f8e4ec5a65033150d1989f5ef22361 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Sat, 17 Oct 2020 12:43:00 -0300 Subject: [PATCH 10/12] clutter/actor: Remove cached inverse transform It is not used anymore. https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1509 --- clutter/clutter/clutter-actor.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/clutter/clutter/clutter-actor.c b/clutter/clutter/clutter-actor.c index 1c28ec6073..79f4565edb 100644 --- a/clutter/clutter/clutter-actor.c +++ b/clutter/clutter/clutter-actor.c @@ -692,7 +692,6 @@ struct _ClutterActorPrivate /* the cached transformation matrix; see apply_transform() */ graphene_matrix_t transform; - graphene_matrix_t inverse_transform; float resource_scale; @@ -847,7 +846,6 @@ struct _ClutterActorPrivate guint needs_paint_volume_update : 1; guint had_effects_on_last_paint_volume_update : 1; guint needs_update_stage_views : 1; - guint has_inverse_transform : 1; }; enum @@ -3057,8 +3055,6 @@ ensure_valid_actor_transform (ClutterActor *actor) graphene_matrix_init_identity (&priv->transform); CLUTTER_ACTOR_GET_CLASS (actor)->apply_transform (actor, &priv->transform); - priv->has_inverse_transform = graphene_matrix_inverse (&priv->transform, - &priv->inverse_transform); priv->transform_valid = TRUE; } -- GitLab From ce6f13357af3a2a87d32b64348b36f51f615eb72 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Sat, 17 Oct 2020 13:18:37 -0300 Subject: [PATCH 11/12] clutter/pick-context: Remove clutter_pick_context_get_framebuffer() It is not used anymore. https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1509 --- clutter/clutter/clutter-pick-context.c | 16 ++-------------- clutter/clutter/clutter-pick-context.h | 3 --- 2 files changed, 2 insertions(+), 17 deletions(-) diff --git a/clutter/clutter/clutter-pick-context.c b/clutter/clutter/clutter-pick-context.c index 7348ec57c6..4e6fb7a882 100644 --- a/clutter/clutter/clutter-pick-context.c +++ b/clutter/clutter/clutter-pick-context.c @@ -17,6 +17,7 @@ #include "clutter-build-config.h" +#include "clutter-backend.h" #include "clutter-pick-context-private.h" struct _ClutterPickContext @@ -24,7 +25,6 @@ struct _ClutterPickContext grefcount ref_count; ClutterPickMode mode; - CoglFramebuffer *framebuffer; ClutterPickStack *pick_stack; }; @@ -42,10 +42,8 @@ clutter_pick_context_new_for_view (ClutterStageView *view, pick_context = g_new0 (ClutterPickContext, 1); g_ref_count_init (&pick_context->ref_count); pick_context->mode = mode; - pick_context->framebuffer = - g_object_ref (clutter_stage_view_get_framebuffer (view)); - context = cogl_framebuffer_get_context (pick_context->framebuffer); + context = clutter_backend_get_cogl_context (clutter_get_default_backend ()); pick_context->pick_stack = clutter_pick_stack_new (context); return pick_context; @@ -62,7 +60,6 @@ static void clutter_pick_context_dispose (ClutterPickContext *pick_context) { g_clear_pointer (&pick_context->pick_stack, clutter_pick_stack_unref); - g_clear_object (&pick_context->framebuffer); } void @@ -82,15 +79,6 @@ clutter_pick_context_destroy (ClutterPickContext *pick_context) clutter_pick_context_unref (pick_context); } -/** - * clutter_pick_context_get_framebuffer: (skip) - */ -CoglFramebuffer * -clutter_pick_context_get_framebuffer (ClutterPickContext *pick_context) -{ - return pick_context->framebuffer; -} - /** * clutter_pick_context_get_mode: (skip) */ diff --git a/clutter/clutter/clutter-pick-context.h b/clutter/clutter/clutter-pick-context.h index 0faf028256..5792be6b58 100644 --- a/clutter/clutter/clutter-pick-context.h +++ b/clutter/clutter/clutter-pick-context.h @@ -43,9 +43,6 @@ void clutter_pick_context_unref (ClutterPickContext *pick_context); CLUTTER_EXPORT void clutter_pick_context_destroy (ClutterPickContext *pick_context); -CLUTTER_EXPORT -CoglFramebuffer * clutter_pick_context_get_framebuffer (ClutterPickContext *pick_context); - CLUTTER_EXPORT ClutterPickMode clutter_pick_context_get_mode (ClutterPickContext *pick_context); -- GitLab From da7671b547ba9fa407fb7e0b313048354d92e840 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Sun, 18 Oct 2020 14:29:13 -0300 Subject: [PATCH 12/12] clutter/actor: Don't push identity transforms It is useless, and needlessly increases the matrix stack and does more multiplications than necessary. Don't push identity transforms to the pick stack. https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1509 --- clutter/clutter/clutter-actor.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/clutter/clutter/clutter-actor.c b/clutter/clutter/clutter-actor.c index 79f4565edb..6044382699 100644 --- a/clutter/clutter/clutter-actor.c +++ b/clutter/clutter/clutter-actor.c @@ -3931,6 +3931,7 @@ clutter_actor_pick (ClutterActor *actor, { ClutterActorPrivate *priv; ClutterActorBox clip; + gboolean transform_pushed = FALSE; gboolean clip_set = FALSE; if (CLUTTER_ACTOR_IN_DESTRUCTION (actor)) @@ -3953,7 +3954,11 @@ clutter_actor_pick (ClutterActor *actor, graphene_matrix_init_identity (&matrix); _clutter_actor_apply_modelview_transform (actor, &matrix); - clutter_pick_context_push_transform (pick_context, &matrix); + if (!graphene_matrix_is_identity (&matrix)) + { + clutter_pick_context_push_transform (pick_context, &matrix); + transform_pushed = TRUE; + } } if (priv->has_clip) @@ -3988,7 +3993,7 @@ clutter_actor_pick (ClutterActor *actor, if (clip_set) clutter_pick_context_pop_clip (pick_context); - if (priv->enable_model_view_transform) + if (transform_pushed) clutter_pick_context_pop_transform (pick_context); /* paint sequence complete */ -- GitLab