[arch-commits] Commit in mutter/trunk (4 files)
Jan Steffens
heftig at archlinux.org
Thu Aug 29 22:46:42 UTC 2019
Date: Thursday, August 29, 2019 @ 22:46:42
Author: heftig
Revision: 361400
replace orphaned cherry-pick with a patch
Added:
mutter/trunk/0001-Add-point-polygon-testing-API.patch
mutter/trunk/0002-Geometric-OpenGL-less-picking.patch
(from rev 361399, mutter/trunk/0001-Geometric-OpenGL-less-picking.patch)
Modified:
mutter/trunk/PKGBUILD
Deleted:
mutter/trunk/0001-Geometric-OpenGL-less-picking.patch
------------------------------------------+
0001-Add-point-polygon-testing-API.patch | 282 ++++
0001-Geometric-OpenGL-less-picking.patch | 1751 -----------------------------
0002-Geometric-OpenGL-less-picking.patch | 1714 ++++++++++++++++++++++++++++
PKGBUILD | 9
4 files changed, 2002 insertions(+), 1754 deletions(-)
Added: 0001-Add-point-polygon-testing-API.patch
===================================================================
--- 0001-Add-point-polygon-testing-API.patch (rev 0)
+++ 0001-Add-point-polygon-testing-API.patch 2019-08-29 22:46:42 UTC (rev 361400)
@@ -0,0 +1,282 @@
+From 37be90384d807c811785f51b5566e8b3b4db3ed3 Mon Sep 17 00:00:00 2001
+From: Daniel van Vugt <daniel.van.vugt at canonical.com>
+Date: Thu, 18 Jul 2019 16:56:41 +0800
+Subject: [PATCH 1/2] clutter-types: Add ClutterPoint polygon testing API
+
+https://gitlab.gnome.org/GNOME/mutter/merge_requests/189
+---
+ clutter/clutter/clutter-base-types.c | 113 +++++++++++++++++++++++++++
+ clutter/clutter/clutter-types.h | 8 ++
+ clutter/tests/conform/meson.build | 1 +
+ clutter/tests/conform/point.c | 104 ++++++++++++++++++++++++
+ 4 files changed, 226 insertions(+)
+ create mode 100644 clutter/tests/conform/point.c
+
+diff --git a/clutter/clutter/clutter-base-types.c b/clutter/clutter/clutter-base-types.c
+index aeb25c90e..0d98c4814 100644
+--- a/clutter/clutter/clutter-base-types.c
++++ b/clutter/clutter/clutter-base-types.c
+@@ -570,6 +570,119 @@ G_DEFINE_BOXED_TYPE_WITH_CODE (ClutterPoint, clutter_point,
+ clutter_point_free,
+ CLUTTER_REGISTER_INTERVAL_PROGRESS (clutter_point_progress))
+
++static int
++clutter_point_compare_line (const ClutterPoint *p,
++ const ClutterPoint *a,
++ const ClutterPoint *b)
++{
++ /*
++ * Given two vectors ab and ap:
++ * ab = (x1, y1, 0)
++ * ap = (x2, y2, 0)
++ * their cross product is a vector:
++ * ab x ap = (0, 0, x1 * y2 - y1 * x2)
++ */
++ float x1 = b->x - a->x;
++ float y1 = b->y - a->y;
++ float x2 = p->x - a->x;
++ float y2 = p->y - a->y;
++ float cross_z = x1 * y2 - y1 * x2;
++
++ /*
++ * The cross product is also proportional to the sine of the angle between
++ * the vectors, so its sign tells us the sign of the angle between the two
++ * vectors. That is whether p is left of or right of line ab.
++ */
++ if (cross_z > 0.f)
++ return +1;
++ else if (cross_z < 0.f)
++ return -1;
++ else
++ return 0;
++}
++
++static int
++clutter_point_compare_polygon (const ClutterPoint *point,
++ const ClutterPoint *vertices,
++ unsigned int nvertices)
++{
++ unsigned int i;
++ int first_side = 0;
++ gboolean on_an_edge = FALSE;
++
++ g_return_val_if_fail (nvertices >= 3, +1);
++
++ for (i = 0; i < nvertices; i++)
++ {
++ int side = clutter_point_compare_line (point,
++ &vertices[i],
++ &vertices[(i + 1) % nvertices]);
++
++ if (side)
++ {
++ if (!first_side)
++ first_side = side;
++ else if (side != first_side)
++ return +1; /* outside */
++ }
++ else
++ {
++ on_an_edge = TRUE;
++ }
++ }
++
++ if (first_side)
++ {
++ return on_an_edge ? 0 : -1; /* on an edge or completely inside */
++ }
++ else
++ {
++ /* The point was neither inside nor outside any edges so we have an empty
++ * polygon. Therefore if the vertices match the point we'll at least call
++ * that touching. Otherwise the point is outside.
++ */
++ return point->x == vertices[0].x && point->y == vertices[0].y ? 0 : +1;
++ }
++}
++
++/**
++ * clutter_point_inside_polygon:
++ * @point: a #ClutterPoint to test
++ * @vertices: array of vertices of the polygon
++ * @nvertices: number of vertices in array @vertices
++ *
++ * Determines whether a point is inside the convex polygon provided, and not
++ * on any of its edges or vertices.
++ *
++ * Return value: true if @point is inside the polygon
++ */
++gboolean
++clutter_point_inside_polygon (const ClutterPoint *point,
++ const ClutterPoint *vertices,
++ unsigned int nvertices)
++{
++ return clutter_point_compare_polygon (point, vertices, nvertices) < 0;
++}
++
++/**
++ * clutter_point_touches_polygon:
++ * @point: a #ClutterPoint to test
++ * @vertices: array of vertices of the polygon
++ * @nvertices: number of vertices in array @vertices
++ *
++ * Determines whether a point is on the convex polygon provided, including
++ * on any of its edges or vertices.
++ *
++ * Return value: true if @point is on the polygon
++ */
++gboolean
++clutter_point_touches_polygon (const ClutterPoint *point,
++ const ClutterPoint *vertices,
++ unsigned int nvertices)
++{
++ return clutter_point_compare_polygon (point, vertices, nvertices) <= 0;
++}
++
+
+
+ /*
+diff --git a/clutter/clutter/clutter-types.h b/clutter/clutter/clutter-types.h
+index 0f0fb1c2a..e3683184b 100644
+--- a/clutter/clutter/clutter-types.h
++++ b/clutter/clutter/clutter-types.h
+@@ -200,6 +200,14 @@ float clutter_point_distance (const ClutterPoint *a,
+ const ClutterPoint *b,
+ float *x_distance,
+ float *y_distance);
++CLUTTER_EXPORT
++gboolean clutter_point_inside_polygon (const ClutterPoint *point,
++ const ClutterPoint *vertices,
++ unsigned int nvertices);
++CLUTTER_EXPORT
++gboolean clutter_point_touches_polygon (const ClutterPoint *point,
++ const ClutterPoint *vertices,
++ unsigned int nvertices);
+
+ /**
+ * ClutterSize:
+diff --git a/clutter/tests/conform/meson.build b/clutter/tests/conform/meson.build
+index a9f2d7e20..916f5c342 100644
+--- a/clutter/tests/conform/meson.build
++++ b/clutter/tests/conform/meson.build
+@@ -36,6 +36,7 @@ clutter_conform_tests_general_tests = [
+ 'interval',
+ 'script-parser',
+ 'units',
++ 'point',
+ ]
+
+ clutter_conform_tests_deprecated_tests = [
+diff --git a/clutter/tests/conform/point.c b/clutter/tests/conform/point.c
+new file mode 100644
+index 000000000..a95a3e440
+--- /dev/null
++++ b/clutter/tests/conform/point.c
+@@ -0,0 +1,104 @@
++#include <clutter/clutter.h>
++
++static void
++point_on_nonempty_polygon (void)
++{
++ int p;
++ static const ClutterPoint vertices[4] =
++ {
++ {1.f, 2.f},
++ {6.f, 3.f},
++ {7.f, 6.f},
++ {0.f, 5.f}
++ };
++ static const ClutterPoint points_inside[] =
++ {
++ {2.f, 3.f},
++ {1.f, 4.f},
++ {5.f, 5.f},
++ {4.f, 3.f},
++ };
++ static const ClutterPoint points_outside[] =
++ {
++ {3.f, 1.f},
++ {7.f, 4.f},
++ {4.f, 6.f},
++ {99.f, -77.f},
++ {-1.f, 3.f},
++ {-8.f, -8.f},
++ {11.f, 4.f},
++ {-7.f, 4.f},
++ };
++ static const ClutterPoint points_touching[] =
++ {
++ {1.f, 2.f},
++ {3.5f, 2.5f},
++ {6.f, 3.f},
++ {6.5f, 4.5f},
++ {7.f, 6.f},
++ {3.5f, 5.5f},
++ {0.f, 5.f}
++ };
++
++ for (p = 0; p < G_N_ELEMENTS (points_inside); p++)
++ {
++ const ClutterPoint *point = points_inside + p;
++
++ g_assert_true (clutter_point_inside_polygon (point, vertices, 4));
++ g_assert_true (clutter_point_touches_polygon (point, vertices, 4));
++ }
++
++ for (p = 0; p < G_N_ELEMENTS (points_outside); p++)
++ {
++ const ClutterPoint *point = points_outside + p;
++
++ g_assert_false (clutter_point_inside_polygon (point, vertices, 4));
++ g_assert_false (clutter_point_touches_polygon (point, vertices, 4));
++ }
++
++ for (p = 0; p < G_N_ELEMENTS (points_touching); p++)
++ {
++ const ClutterPoint *point = points_touching + p;
++
++ g_assert_false (clutter_point_inside_polygon (point, vertices, 4));
++ g_assert_true (clutter_point_touches_polygon (point, vertices, 4));
++ }
++}
++
++static void
++point_on_empty_polygon (void)
++{
++ int p;
++ static const ClutterPoint vertices[4] =
++ {
++ {5.f, 6.f},
++ {5.f, 6.f},
++ {5.f, 6.f},
++ {5.f, 6.f},
++ };
++ static const ClutterPoint points_outside[] =
++ {
++ {3.f, 1.f},
++ {7.f, 4.f},
++ {4.f, 6.f},
++ {99.f, -77.f},
++ {-1.f, 3.f},
++ {-8.f, -8.f},
++ };
++
++ for (p = 0; p < G_N_ELEMENTS (points_outside); p++)
++ {
++ const ClutterPoint *point = points_outside + p;
++
++ g_assert_false (clutter_point_inside_polygon (point, vertices, 4));
++ g_assert_false (clutter_point_touches_polygon (point, vertices, 4));
++ }
++
++ g_assert_false (clutter_point_inside_polygon (&vertices[0], vertices, 4));
++ g_assert_true (clutter_point_touches_polygon (&vertices[0], vertices, 4));
++}
++
++CLUTTER_TEST_SUITE (
++ CLUTTER_TEST_UNIT ("/point/on_nonempty_polygon", point_on_nonempty_polygon)
++ CLUTTER_TEST_UNIT ("/point/on_empty_polygon", point_on_empty_polygon)
++)
+--
+2.23.0
+
Deleted: 0001-Geometric-OpenGL-less-picking.patch
===================================================================
--- 0001-Geometric-OpenGL-less-picking.patch 2019-08-29 22:42:27 UTC (rev 361399)
+++ 0001-Geometric-OpenGL-less-picking.patch 2019-08-29 22:46:42 UTC (rev 361400)
@@ -1,1751 +0,0 @@
-From 6a18d760ed05b87af7aae43f1963eb32bc1d81e7 Mon Sep 17 00:00:00 2001
-From: Daniel van Vugt <daniel.van.vugt at canonical.com>
-Date: Thu, 2 Aug 2018 19:03:30 +0800
-Subject: [PATCH] Geometric (OpenGL-less) picking
-
-Cherry picked from commit b9170a69314739638db7f471b87ba55a906bdf03.
-
-Redesign picking to avoid using OpenGL. Previously picking involved
-several GL operations per mouse movement, but now the GPU/GL isn't
-used at all.
-
-By avoiding OpenGL and the graphics driver we also reduce CPU usage.
-Despite reimplementing the logic on the CPU, it still takes less CPU
-time than going through GL did.
-
-This new approach also dramatically reduces the number of picking paint
-cycles required for cursor movement since the pickability of the entire
-screen is calculated and cached. The cache is only invalidated when the
-screen contents change so for typical desktop usage where the screen is
-mostly idle, cursor movement doesn't incur anywhere near as many paint
-cycles as it used to.
-
-CPU usage on an Intel i7-7700, tested with two different GPUs/drivers:
-
- | | Intel | Nvidia |
- | ------: | --------: | -----: |
- | Moving the mouse: |
- | Before | 10% | 10% |
- | After | 6% | 6% |
- | Moving a window: |
- | Before | 23% | 81% |
- | After | 19% | 40% |
-
-Closes: https://gitlab.gnome.org/GNOME/mutter/issues/154,
- https://gitlab.gnome.org/GNOME/mutter/issues/691
-
-Helps significantly with: https://gitlab.gnome.org/GNOME/mutter/issues/283,
- https://gitlab.gnome.org/GNOME/mutter/issues/590,
- https://gitlab.gnome.org/GNOME/mutter/issues/700
-
-https://gitlab.gnome.org/GNOME/mutter/merge_requests/189
----
- clutter/clutter/clutter-actor-private.h | 2 -
- clutter/clutter/clutter-actor.c | 256 +++++++----
- clutter/clutter/clutter-actor.h | 4 +
- clutter/clutter/clutter-debug.h | 3 +-
- clutter/clutter/clutter-main.c | 120 -----
- clutter/clutter/clutter-private.h | 5 -
- clutter/clutter/clutter-stage-private.h | 16 +-
- clutter/clutter/clutter-stage-window.c | 18 -
- clutter/clutter/clutter-stage-window.h | 8 -
- clutter/clutter/clutter-stage.c | 435 +++++++++++--------
- clutter/clutter/cogl/clutter-stage-cogl.c | 50 ---
- clutter/clutter/deprecated/clutter-texture.c | 93 +---
- clutter/tests/conform/actor-pick.c | 99 +----
- clutter/tests/conform/meson.build | 1 -
- clutter/tests/conform/texture.c | 84 ----
- src/compositor/meta-surface-actor.c | 27 +-
- 16 files changed, 436 insertions(+), 785 deletions(-)
- delete mode 100644 clutter/tests/conform/texture.c
-
-diff --git a/clutter/clutter/clutter-actor-private.h b/clutter/clutter/clutter-actor-private.h
-index c44f6342f..9bf1a3049 100644
---- a/clutter/clutter/clutter-actor-private.h
-+++ b/clutter/clutter/clutter-actor-private.h
-@@ -297,8 +297,6 @@ const gchar * _clutter_actor_get_debug_name
- void _clutter_actor_push_clone_paint (void);
- void _clutter_actor_pop_clone_paint (void);
-
--guint32 _clutter_actor_get_pick_id (ClutterActor *self);
--
- void _clutter_actor_shader_pre_paint (ClutterActor *actor,
- gboolean repeat);
- void _clutter_actor_shader_post_paint (ClutterActor *actor);
-diff --git a/clutter/clutter/clutter-actor.c b/clutter/clutter/clutter-actor.c
-index a7fecb830..5feae6574 100644
---- a/clutter/clutter/clutter-actor.c
-+++ b/clutter/clutter/clutter-actor.c
-@@ -729,8 +729,6 @@ struct _ClutterActorPrivate
-
- gchar *name; /* a non-unique name, used for debugging */
-
-- gint32 pick_id; /* per-stage unique id, used for picking */
--
- /* a back-pointer to the Pango context that we can use
- * to create pre-configured PangoLayout
- */
-@@ -1282,6 +1280,129 @@ clutter_actor_verify_map_state (ClutterActor *self)
-
- #endif /* CLUTTER_ENABLE_DEBUG */
-
-+static gboolean
-+_clutter_actor_transform_local_box_to_stage (ClutterActor *self,
-+ const ClutterActorBox *box,
-+ ClutterPoint vertices[4])
-+{
-+ ClutterActor *stage = _clutter_actor_get_stage_internal (self);
-+ CoglMatrix stage_transform, inv_stage_transform;
-+ CoglMatrix modelview, transform_to_stage;
-+ int v;
-+
-+ g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
-+ g_return_val_if_fail (box != NULL, FALSE);
-+
-+ g_return_val_if_fail (stage != NULL, FALSE);
-+ g_return_val_if_fail (box->x1 <= box->x2, FALSE);
-+ g_return_val_if_fail (box->y1 <= box->y2, FALSE);
-+
-+ /* Below is generally equivalent to:
-+ *
-+ * _clutter_actor_get_relative_transformation_matrix (self,
-+ * stage,
-+ * &transform_to_stage);
-+ *
-+ * but we do it the hard way here instead so as to accurately include any
-+ * cogl transformations that an actor's paint function might have added in.
-+ * Those additional transformations are only known to cogl matrices and not
-+ * known to the clutter getter like above. So this way we more accurately
-+ * represent what's really getting painted.
-+ */
-+ clutter_actor_get_transform (stage, &stage_transform);
-+ if (!cogl_matrix_get_inverse (&stage_transform, &inv_stage_transform))
-+ return FALSE;
-+ cogl_get_modelview_matrix (&modelview);
-+ cogl_matrix_multiply (&transform_to_stage, &inv_stage_transform, &modelview);
-+
-+ 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++)
-+ {
-+ gfloat z = 0.f;
-+ gfloat w = 1.f;
-+
-+ cogl_matrix_transform_point (&transform_to_stage,
-+ &vertices[v].x,
-+ &vertices[v].y,
-+ &z,
-+ &w);
-+ }
-+
-+ return TRUE;
-+}
-+
-+/**
-+ * clutter_actor_pick_box:
-+ * @self: The #ClutterActor being "pick" painted.
-+ * @box: A rectangle in the actor's own local coordinates.
-+ *
-+ * Logs (does a virtual paint of) a rectangle for picking. Note that @box is
-+ * in the actor's own local coordinates, so is usually {0,0,width,height}
-+ * to include the whole actor. That is unless the actor has a shaped input
-+ * region in which case you may wish to log the (multiple) smaller rectangles
-+ * that make up the input region.
-+ */
-+void
-+clutter_actor_pick_box (ClutterActor *self,
-+ const ClutterActorBox *box)
-+{
-+ ClutterActor *stage;
-+ ClutterPoint vertices[4];
-+
-+ g_return_if_fail (CLUTTER_IS_ACTOR (self));
-+ g_return_if_fail (box != NULL);
-+
-+ /* An empty box to a "pick" paint means to paint nothing at all. */
-+ if (box->x1 >= box->x2 || box->y1 >= box->y2)
-+ return;
-+
-+ stage = _clutter_actor_get_stage_internal (self);
-+
-+ if (_clutter_actor_transform_local_box_to_stage (self, box, vertices))
-+ _clutter_stage_log_pick (CLUTTER_STAGE (stage), vertices, self);
-+}
-+
-+static gboolean
-+_clutter_actor_push_pick_clip (ClutterActor *self,
-+ const ClutterActorBox *clip)
-+{
-+ ClutterActor *stage;
-+ ClutterPoint vertices[4];
-+
-+ g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
-+ g_return_val_if_fail (clip != NULL, FALSE);
-+
-+ stage = _clutter_actor_get_stage_internal (self);
-+
-+ if (!_clutter_actor_transform_local_box_to_stage (self, clip, vertices))
-+ return FALSE;
-+
-+ _clutter_stage_push_pick_clip (CLUTTER_STAGE (stage), vertices);
-+ return TRUE;
-+}
-+
-+static void
-+_clutter_actor_pop_pick_clip (ClutterActor *self)
-+{
-+ ClutterActor *stage;
-+
-+ g_return_if_fail (CLUTTER_IS_ACTOR (self));
-+
-+ 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)
-@@ -1510,25 +1631,17 @@ clutter_actor_update_map_state (ClutterActor *self,
- static void
- clutter_actor_real_map (ClutterActor *self)
- {
-- ClutterActorPrivate *priv = self->priv;
-- ClutterActor *stage, *iter;
-+ ClutterActor *iter;
-
- g_assert (!CLUTTER_ACTOR_IS_MAPPED (self));
-
- CLUTTER_NOTE (ACTOR, "Mapping actor '%s'",
- _clutter_actor_get_debug_name (self));
-
- CLUTTER_ACTOR_SET_FLAGS (self, CLUTTER_ACTOR_MAPPED);
-
- self->priv->needs_paint_volume_update = TRUE;
-
-- stage = _clutter_actor_get_stage_internal (self);
-- priv->pick_id = _clutter_stage_acquire_pick_id (CLUTTER_STAGE (stage), self);
--
-- CLUTTER_NOTE (ACTOR, "Pick id '%d' for actor '%s'",
-- priv->pick_id,
-- _clutter_actor_get_debug_name (self));
--
- clutter_actor_ensure_resource_scale (self);
-
- /* notify on parent mapped before potentially mapping
-@@ -1633,11 +1746,6 @@ clutter_actor_real_unmap (ClutterActor *self)
-
- stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (self));
-
-- if (stage != NULL)
-- _clutter_stage_release_pick_id (stage, priv->pick_id);
--
-- priv->pick_id = -1;
--
- if (stage != NULL &&
- clutter_stage_get_key_focus (stage) == self)
- {
-@@ -2256,46 +2364,14 @@ static void
- clutter_actor_real_pick (ClutterActor *self,
- const ClutterColor *color)
- {
-- CoglFramebuffer *framebuffer = cogl_get_draw_framebuffer ();
--
-- /* the default implementation is just to paint a rectangle
-- * with the same size of the actor using the passed color
-- */
- if (clutter_actor_should_pick_paint (self))
- {
-- static CoglPipeline *default_pick_pipeline = NULL;
-- ClutterActorBox box = { 0, };
-- CoglPipeline *pick_pipeline;
-- float width, height;
-+ ClutterActorBox box = {0,
-+ 0,
-+ clutter_actor_get_width (self),
-+ clutter_actor_get_height (self)};
-
-- if (G_UNLIKELY (default_pick_pipeline == NULL))
-- {
-- CoglContext *ctx =
-- clutter_backend_get_cogl_context (clutter_get_default_backend ());
--
-- default_pick_pipeline = cogl_pipeline_new (ctx);
-- }
--
-- g_assert (default_pick_pipeline != NULL);
-- pick_pipeline = cogl_pipeline_copy (default_pick_pipeline);
--
-- clutter_actor_get_allocation_box (self, &box);
--
-- width = box.x2 - box.x1;
-- height = box.y2 - box.y1;
--
-- cogl_pipeline_set_color4ub (pick_pipeline,
-- color->red,
-- color->green,
-- color->blue,
-- color->alpha);
--
-- cogl_framebuffer_draw_rectangle (framebuffer,
-- pick_pipeline,
-- 0, 0,
-- width, height);
--
-- cogl_object_unref (pick_pipeline);
-+ clutter_actor_pick_box (self, &box);
- }
-
- /* XXX - this thoroughly sucks, but we need to maintain compatibility
-@@ -3586,15 +3662,6 @@ _clutter_actor_update_last_paint_volume (ClutterActor *self)
- priv->last_paint_volume_valid = TRUE;
- }
-
--guint32
--_clutter_actor_get_pick_id (ClutterActor *self)
--{
-- if (self->priv->pick_id < 0)
-- return 0;
--
-- return self->priv->pick_id;
--}
--
- /* This is the same as clutter_actor_add_effect except that it doesn't
- queue a redraw and it doesn't notify on the effect property */
- static void
-@@ -3826,6 +3893,7 @@ clutter_actor_paint (ClutterActor *self)
- {
- ClutterActorPrivate *priv;
- ClutterPickMode pick_mode;
-+ ClutterActorBox clip;
- gboolean clip_set = FALSE;
- ClutterStage *stage;
-
-@@ -3919,26 +3987,40 @@ clutter_actor_paint (ClutterActor *self)
-
- if (priv->has_clip)
- {
-- CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage);
-- cogl_framebuffer_push_rectangle_clip (fb,
-- priv->clip.origin.x,
-- priv->clip.origin.y,
-- priv->clip.origin.x + priv->clip.size.width,
-- priv->clip.origin.y + priv->clip.size.height);
-+ clip.x1 = priv->clip.origin.x;
-+ clip.y1 = priv->clip.origin.y;
-+ clip.x2 = priv->clip.origin.x + priv->clip.size.width;
-+ clip.y2 = priv->clip.origin.y + priv->clip.size.height;
- clip_set = TRUE;
- }
- else if (priv->clip_to_allocation)
- {
-- CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage);
-- gfloat width, height;
--
-- width = priv->allocation.x2 - priv->allocation.x1;
-- height = priv->allocation.y2 - priv->allocation.y1;
--
-- cogl_framebuffer_push_rectangle_clip (fb, 0, 0, width, height);
-+ clip.x1 = 0.f;
-+ clip.y1 = 0.f;
-+ clip.x2 = priv->allocation.x2 - priv->allocation.x1;
-+ clip.y2 = priv->allocation.y2 - priv->allocation.y1;
- clip_set = TRUE;
- }
-
-+ if (clip_set)
-+ {
-+ if (pick_mode == CLUTTER_PICK_NONE)
-+ {
-+ CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage);
-+
-+ cogl_framebuffer_push_rectangle_clip (fb,
-+ clip.x1,
-+ clip.y1,
-+ clip.x2,
-+ clip.y2);
-+ }
-+ else
-+ {
-+ if (!_clutter_actor_push_pick_clip (self, &clip))
-+ clip_set = FALSE;
-+ }
-+ }
-+
- if (pick_mode == CLUTTER_PICK_NONE)
- {
- /* We check whether we need to add the flatten effect before
-@@ -4017,9 +4099,16 @@ clutter_actor_paint (ClutterActor *self)
- done:
- if (clip_set)
- {
-- CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage);
-+ if (pick_mode == CLUTTER_PICK_NONE)
-+ {
-+ CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage);
-
-- cogl_framebuffer_pop_clip (fb);
-+ cogl_framebuffer_pop_clip (fb);
-+ }
-+ else
-+ {
-+ _clutter_actor_pop_pick_clip (self);
-+ }
- }
-
- cogl_pop_matrix ();
-@@ -4090,11 +4179,12 @@ clutter_actor_continue_paint (ClutterActor *self)
- {
- 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.
-+ /* The actor will log a silhouette of itself to the stage pick log.
-+ * Note that the picking color is no longer used as the "log" instead
-+ * keeps a weak pointer to the actor itself. But we keep the color
-+ * parameter for now so as to maintain ABI compatibility. The color
-+ * parameter can be removed when someone feels like breaking the ABI
-+ * along with gnome-shell.
- *
- * XXX:2.0 - Call the pick() virtual directly
- */
-@@ -8602,8 +8692,6 @@ clutter_actor_init (ClutterActor *self)
-
- self->priv = priv = clutter_actor_get_instance_private (self);
-
-- priv->pick_id = -1;
--
- priv->opacity = 0xff;
- priv->show_on_set_parent = TRUE;
- priv->resource_scale = -1.0f;
-diff --git a/clutter/clutter/clutter-actor.h b/clutter/clutter/clutter-actor.h
-index 7d2168af1..b1abff86d 100644
---- a/clutter/clutter/clutter-actor.h
-+++ b/clutter/clutter/clutter-actor.h
-@@ -902,6 +902,10 @@ void clutter_actor_bind_model_with_properties
- const char *first_model_property,
- ...);
-
-+CLUTTER_EXPORT
-+void clutter_actor_pick_box (ClutterActor *self,
-+ const ClutterActorBox *box);
-+
- G_END_DECLS
-
- #endif /* __CLUTTER_ACTOR_H__ */
-diff --git a/clutter/clutter/clutter-debug.h b/clutter/clutter/clutter-debug.h
-index 2462385f6..3744c648f 100644
---- a/clutter/clutter/clutter-debug.h
-+++ b/clutter/clutter/clutter-debug.h
-@@ -29,8 +29,7 @@ typedef enum
-
- typedef enum
- {
-- CLUTTER_DEBUG_NOP_PICKING = 1 << 0,
-- CLUTTER_DEBUG_DUMP_PICK_BUFFERS = 1 << 1
-+ CLUTTER_DEBUG_NOP_PICKING = 1 << 0
- } ClutterPickDebugFlag;
-
- typedef enum
-diff --git a/clutter/clutter/clutter-main.c b/clutter/clutter/clutter-main.c
-index 71ec0d80c..c2ec82697 100644
---- a/clutter/clutter/clutter-main.c
-+++ b/clutter/clutter/clutter-main.c
-@@ -130,7 +130,6 @@ static const GDebugKey clutter_debug_keys[] = {
-
- static const GDebugKey clutter_pick_debug_keys[] = {
- { "nop-picking", CLUTTER_DEBUG_NOP_PICKING },
-- { "dump-pick-buffers", CLUTTER_DEBUG_DUMP_PICK_BUFFERS },
- };
-
- static const GDebugKey clutter_paint_debug_keys[] = {
-@@ -532,125 +531,6 @@ clutter_get_motion_events_enabled (void)
- return _clutter_context_get_motion_events_enabled ();
- }
-
--void
--_clutter_id_to_color (guint id_,
-- ClutterColor *col)
--{
-- ClutterMainContext *ctx;
-- gint red, green, blue;
--
-- ctx = _clutter_context_get_default ();
--
-- if (ctx->fb_g_mask == 0)
-- {
-- /* Figure out framebuffer masks used for pick */
-- cogl_get_bitmasks (&ctx->fb_r_mask,
-- &ctx->fb_g_mask,
-- &ctx->fb_b_mask, NULL);
--
-- ctx->fb_r_mask_used = ctx->fb_r_mask;
-- ctx->fb_g_mask_used = ctx->fb_g_mask;
-- ctx->fb_b_mask_used = ctx->fb_b_mask;
--
-- /* XXX - describe what "fuzzy picking" is */
-- if (clutter_use_fuzzy_picking)
-- {
-- ctx->fb_r_mask_used--;
-- ctx->fb_g_mask_used--;
-- ctx->fb_b_mask_used--;
-- }
-- }
--
-- /* compute the numbers we'll store in the components */
-- red = (id_ >> (ctx->fb_g_mask_used+ctx->fb_b_mask_used))
-- & (0xff >> (8-ctx->fb_r_mask_used));
-- green = (id_ >> ctx->fb_b_mask_used)
-- & (0xff >> (8-ctx->fb_g_mask_used));
-- blue = (id_)
-- & (0xff >> (8-ctx->fb_b_mask_used));
--
-- /* shift left bits a bit and add one, this circumvents
-- * at least some potential rounding errors in GL/GLES
-- * driver / hw implementation.
-- */
-- if (ctx->fb_r_mask_used != ctx->fb_r_mask)
-- red = red * 2;
-- if (ctx->fb_g_mask_used != ctx->fb_g_mask)
-- green = green * 2;
-- if (ctx->fb_b_mask_used != ctx->fb_b_mask)
-- blue = blue * 2;
--
-- /* shift up to be full 8bit values */
-- red = (red << (8 - ctx->fb_r_mask)) | (0x7f >> (ctx->fb_r_mask_used));
-- green = (green << (8 - ctx->fb_g_mask)) | (0x7f >> (ctx->fb_g_mask_used));
-- blue = (blue << (8 - ctx->fb_b_mask)) | (0x7f >> (ctx->fb_b_mask_used));
--
-- col->red = red;
-- col->green = green;
-- col->blue = blue;
-- col->alpha = 0xff;
--
-- /* XXX: We rotate the nibbles of the colors here so that there is a
-- * visible variation between colors of sequential actor identifiers;
-- * otherwise pick buffers dumped to an image will pretty much just look
-- * black.
-- */
-- if (G_UNLIKELY (clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS))
-- {
-- col->red = (col->red << 4) | (col->red >> 4);
-- col->green = (col->green << 4) | (col->green >> 4);
-- col->blue = (col->blue << 4) | (col->blue >> 4);
-- }
--}
--
--guint
--_clutter_pixel_to_id (guchar pixel[4])
--{
-- ClutterMainContext *ctx;
-- gint red, green, blue;
-- guint retval;
--
-- ctx = _clutter_context_get_default ();
--
-- /* reduce the pixel components to the number of bits actually used of the
-- * 8bits.
-- */
-- if (G_UNLIKELY (clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS))
-- {
-- guchar tmp;
--
-- /* XXX: In _clutter_id_to_color we rotated the nibbles of the colors so
-- * that there is a visible variation between colors of sequential actor
-- * identifiers (otherwise pick buffers dumped to an image will pretty
-- * much just look black.) Here we reverse that rotation.
-- */
-- tmp = ((pixel[0] << 4) | (pixel[0] >> 4));
-- red = tmp >> (8 - ctx->fb_r_mask);
-- tmp = ((pixel[1] << 4) | (pixel[1] >> 4));
-- green = tmp >> (8 - ctx->fb_g_mask);
-- tmp = ((pixel[2] << 4) | (pixel[2] >> 4));
-- blue = tmp >> (8 - ctx->fb_b_mask);
-- }
-- else
-- {
-- red = pixel[0] >> (8 - ctx->fb_r_mask);
-- green = pixel[1] >> (8 - ctx->fb_g_mask);
-- blue = pixel[2] >> (8 - ctx->fb_b_mask);
-- }
--
-- /* divide potentially by two if 'fuzzy' */
-- red = red >> (ctx->fb_r_mask - ctx->fb_r_mask_used);
-- green = green >> (ctx->fb_g_mask - ctx->fb_g_mask_used);
-- blue = blue >> (ctx->fb_b_mask - ctx->fb_b_mask_used);
--
-- /* combine the correct per component values into the final id */
-- retval = blue
-- + (green << ctx->fb_b_mask_used)
-- + (red << (ctx->fb_b_mask_used + ctx->fb_g_mask_used));
--
-- return retval;
--}
--
- static CoglPangoFontMap *
- clutter_context_get_pango_fontmap (void)
- {
-diff --git a/clutter/clutter/clutter-private.h b/clutter/clutter/clutter-private.h
-index a5cd1fa19..47bef4ac2 100644
---- a/clutter/clutter/clutter-private.h
-+++ b/clutter/clutter/clutter-private.h
-@@ -210,11 +210,6 @@ gboolean _clutter_feature_init (GError **error);
- gboolean _clutter_diagnostic_enabled (void);
- void _clutter_diagnostic_message (const char *fmt, ...) G_GNUC_PRINTF (1, 2);
-
--/* Picking code */
--guint _clutter_pixel_to_id (guchar pixel[4]);
--void _clutter_id_to_color (guint id,
-- ClutterColor *col);
--
- void _clutter_set_sync_to_vblank (gboolean sync_to_vblank);
-
- /* use this function as the accumulator if you have a signal with
-diff --git a/clutter/clutter/clutter-stage-private.h b/clutter/clutter/clutter-stage-private.h
-index 4799c29e1..f8591ddcb 100644
---- a/clutter/clutter/clutter-stage-private.h
-+++ b/clutter/clutter/clutter-stage-private.h
-@@ -74,31 +74,33 @@ gint64 _clutter_stage_get_update_time (ClutterStage *stage);
- void _clutter_stage_clear_update_time (ClutterStage *stage);
- gboolean _clutter_stage_has_full_redraw_queued (ClutterStage *stage);
-
-+void _clutter_stage_log_pick (ClutterStage *stage,
-+ const ClutterPoint vertices[4],
-+ ClutterActor *actor);
-+
-+void _clutter_stage_push_pick_clip (ClutterStage *stage,
-+ const ClutterPoint vertices[4]);
-+
-+void _clutter_stage_pop_pick_clip (ClutterStage *stage);
-+
- ClutterActor *_clutter_stage_do_pick (ClutterStage *stage,
- gint x,
- gint y,
- ClutterPickMode mode);
-
- ClutterPaintVolume *_clutter_stage_paint_volume_stack_allocate (ClutterStage *stage);
- void _clutter_stage_paint_volume_stack_free_all (ClutterStage *stage);
-
- const ClutterPlane *_clutter_stage_get_clip (ClutterStage *stage);
-
- ClutterStageQueueRedrawEntry *_clutter_stage_queue_actor_redraw (ClutterStage *stage,
- ClutterStageQueueRedrawEntry *entry,
- ClutterActor *actor,
- const ClutterPaintVolume *clip);
- void _clutter_stage_queue_redraw_entry_invalidate (ClutterStageQueueRedrawEntry *entry);
-
- CoglFramebuffer *_clutter_stage_get_active_framebuffer (ClutterStage *stage);
-
--gint32 _clutter_stage_acquire_pick_id (ClutterStage *stage,
-- ClutterActor *actor);
--void _clutter_stage_release_pick_id (ClutterStage *stage,
-- gint32 pick_id);
--ClutterActor * _clutter_stage_get_actor_by_pick_id (ClutterStage *stage,
-- gint32 pick_id);
--
- void _clutter_stage_add_pointer_drag_actor (ClutterStage *stage,
- ClutterInputDevice *device,
- ClutterActor *actor);
-diff --git a/clutter/clutter/clutter-stage-window.c b/clutter/clutter/clutter-stage-window.c
-index e8fa976a7..4c4ef9d64 100644
---- a/clutter/clutter/clutter-stage-window.c
-+++ b/clutter/clutter/clutter-stage-window.c
-@@ -293,24 +293,6 @@ _clutter_stage_window_redraw (ClutterStageWindow *window)
- iface->redraw (window);
- }
-
--
--void
--_clutter_stage_window_get_dirty_pixel (ClutterStageWindow *window,
-- ClutterStageView *view,
-- int *x, int *y)
--{
-- ClutterStageWindowInterface *iface;
--
-- *x = 0;
-- *y = 0;
--
-- g_return_if_fail (CLUTTER_IS_STAGE_WINDOW (window));
--
-- iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window);
-- if (iface->get_dirty_pixel)
-- iface->get_dirty_pixel (window, view, x, y);
--}
--
- gboolean
- _clutter_stage_window_can_clip_redraws (ClutterStageWindow *window)
- {
-diff --git a/clutter/clutter/clutter-stage-window.h b/clutter/clutter/clutter-stage-window.h
-index 6c3601745..aa0c5f71c 100644
---- a/clutter/clutter/clutter-stage-window.h
-+++ b/clutter/clutter/clutter-stage-window.h
-@@ -68,10 +68,6 @@ struct _ClutterStageWindowInterface
-
- void (* redraw) (ClutterStageWindow *stage_window);
-
-- void (* get_dirty_pixel) (ClutterStageWindow *stage_window,
-- ClutterStageView *view,
-- int *x, int *y);
--
- gboolean (* can_clip_redraws) (ClutterStageWindow *stage_window);
-
- GList *(* get_views) (ClutterStageWindow *stage_window);
-@@ -119,10 +115,6 @@ void _clutter_stage_window_set_accept_focus (ClutterStageWin
-
- void _clutter_stage_window_redraw (ClutterStageWindow *window);
-
--void _clutter_stage_window_get_dirty_pixel (ClutterStageWindow *window,
-- ClutterStageView *view,
-- int *x, int *y);
--
- gboolean _clutter_stage_window_can_clip_redraws (ClutterStageWindow *window);
-
- GList * _clutter_stage_window_get_views (ClutterStageWindow *window);
-diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c
-index 1eea5b305..bd0ac9ed2 100644
---- a/clutter/clutter/clutter-stage.c
-+++ b/clutter/clutter/clutter-stage.c
-@@ -103,6 +103,23 @@ struct _ClutterStageQueueRedrawEntry
- ClutterPaintVolume clip;
- };
-
-+struct _PickRecord
-+{
-+ ClutterPoint vertex[4];
-+ ClutterActor *actor;
-+ int clip_stack_top;
-+};
-+
-+typedef struct _PickRecord PickRecord;
-+
-+struct _PickClipRecord
-+{
-+ int prev;
-+ ClutterPoint vertex[4];
-+};
-+
-+typedef struct _PickClipRecord PickClipRecord;
-+
- struct _ClutterStagePrivate
- {
- /* the stage implementation */
-@@ -136,7 +153,11 @@ struct _ClutterStagePrivate
- GTimer *fps_timer;
- gint32 timer_n_frames;
-
-- ClutterIDPool *pick_id_pool;
-+ GArray *pick_stack;
-+ GArray *pick_clip_stack;
-+ int pick_clip_stack_top;
-+ gboolean pick_stack_frozen;
-+ ClutterPickMode cached_pick_mode;
-
- #ifdef CLUTTER_ENABLE_DEBUG
- gulong redraw_count;
-@@ -322,6 +343,177 @@ clutter_stage_get_preferred_height (ClutterActor *self,
- *natural_height_p = geom.height;
- }
-
-+/* In order to keep weak pointers valid between frames we need them to not
-+ * move in memory, so the stack is marked as "frozen".
-+ */
-+static void
-+_clutter_stage_freeze_pick_stack (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
-+_clutter_stage_thaw_pick_stack (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;
-+
-+ _clutter_stage_thaw_pick_stack (stage);
-+ g_array_set_size (priv->pick_stack, 0);
-+ g_array_set_size (priv->pick_clip_stack, 0);
-+ priv->pick_clip_stack_top = -1;
-+ priv->cached_pick_mode = CLUTTER_PICK_NONE;
-+}
-+
-+void
-+_clutter_stage_log_pick (ClutterStage *stage,
-+ const ClutterPoint vertices[4],
-+ 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 (ClutterPoint));
-+ 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 ClutterPoint vertices[4])
-+{
-+ 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 (ClutterPoint));
-+
-+ 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
-+pick_record_contains_pixel (ClutterStage *stage,
-+ const PickRecord *rec,
-+ int x,
-+ int y)
-+{
-+ ClutterStagePrivate *priv;
-+ int clip_index;
-+
-+ /* We test the centre point of the pixel so that we can use
-+ * clutter_point_inside_polygon and avoid any ambiguity about the boundaries
-+ * of pixel-aligned actors. If we were to use point (x,y) instead (which is
-+ * the top-left corner of the pixel) then we'd need to use
-+ * clutter_point_touches_polygon.
-+ */
-+ const ClutterPoint point = { x + 0.5f, y + 0.5f };
-+
-+ if (!clutter_point_inside_polygon (&point, rec->vertex, 4))
-+ return FALSE;
-+
-+ priv = stage->priv;
-+
-+ /* pick_clip_stack is actually a graph containing one or more trees. And the
-+ * clip_stack_top for a given PickRecord is the path from a leaf of such a
-+ * tree to its root. It's still a stack though, from the point of view of the
-+ * code that built it and the code that traverses it here.
-+ *
-+ * Allowing multiple stacks to overlap and merge into trees saves us time and
-+ * memory.
-+ */
-+ clip_index = rec->clip_stack_top;
-+ while (clip_index >= 0)
-+ {
-+ const PickClipRecord *clip = &g_array_index (priv->pick_clip_stack,
-+ PickClipRecord,
-+ clip_index);
-+
-+ if (!clutter_point_inside_polygon (&point, clip->vertex, 4))
-+ return FALSE;
-+
-+ clip_index = clip->prev;
-+ }
-+
-+ return TRUE;
-+}
-+
- static inline void
- queue_full_redraw (ClutterStage *stage)
- {
-@@ -632,6 +824,12 @@ clutter_stage_do_paint_view (ClutterStage *stage,
- float viewport[4];
- cairo_rectangle_int_t geom;
-
-+ /* Any mode of painting/picking invalidates the pick cache, unless we're
-+ * in the middle of building it. So we reset the cached flag but don't
-+ * completely clear the pick stack...
-+ */
-+ priv->cached_pick_mode = CLUTTER_PICK_NONE;
-+
- _clutter_stage_window_get_geometry (priv->impl, &geom);
-
- viewport[0] = priv->viewport[0];
-@@ -1397,181 +1595,54 @@ clutter_stage_get_redraw_clip_bounds (ClutterStage *stage,
- }
- }
-
--static void
--read_pixels_to_file (CoglFramebuffer *fb,
-- char *filename_stem,
-- int x,
-- int y,
-- int width,
-- int height)
--{
-- guint8 *data;
-- cairo_surface_t *surface;
-- static int read_count = 0;
-- char *filename = g_strdup_printf ("%s-%05d.png",
-- filename_stem,
-- read_count);
--
-- data = g_malloc (4 * width * height);
-- cogl_framebuffer_read_pixels (fb,
-- x, y, width, height,
-- CLUTTER_CAIRO_FORMAT_ARGB32,
-- data);
--
-- surface = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_RGB24,
-- width, height,
-- width * 4);
--
-- cairo_surface_write_to_png (surface, filename);
-- cairo_surface_destroy (surface);
--
-- g_free (data);
-- g_free (filename);
--
-- read_count++;
--}
--
- static ClutterActor *
- _clutter_stage_do_pick_on_view (ClutterStage *stage,
- gint x,
- gint y,
- ClutterPickMode mode,
- ClutterStageView *view)
- {
-- ClutterActor *actor = CLUTTER_ACTOR (stage);
-+ ClutterMainContext *context = _clutter_context_get_default ();
-+ int i;
- ClutterStagePrivate *priv = stage->priv;
- CoglFramebuffer *fb = clutter_stage_view_get_framebuffer (view);
-- cairo_rectangle_int_t view_layout;
-- ClutterMainContext *context;
-- guchar pixel[4] = { 0xff, 0xff, 0xff, 0xff };
-- CoglColor stage_pick_id;
-- gboolean dither_enabled_save;
-- ClutterActor *retval;
-- gint dirty_x;
-- gint dirty_y;
-- gint read_x;
-- gint read_y;
-- float fb_width, fb_height;
-- float fb_scale;
-- float viewport_offset_x;
-- float viewport_offset_y;
-
-- priv = stage->priv;
-+ /* We are not reentrant right now. If this proves to be a problem then we
-+ * would need to keep a stack of pick_stack's, or something... :(
-+ */
-+ g_assert (context->pick_mode == CLUTTER_PICK_NONE);
-
-- context = _clutter_context_get_default ();
-- fb_scale = clutter_stage_view_get_scale (view);
-- clutter_stage_view_get_layout (view, &view_layout);
--
-- fb_width = view_layout.width * fb_scale;
-- fb_height = view_layout.height * fb_scale;
-- cogl_push_framebuffer (fb);
--
-- /* needed for when a context switch happens */
-- _clutter_stage_maybe_setup_viewport (stage, view);
--
-- /* FIXME: For some reason leaving the cogl clip stack empty causes the
-- * picking to not work at all, so setting it the whole framebuffer content
-- * for now. */
-- cogl_framebuffer_push_scissor_clip (fb, 0, 0,
-- view_layout.width * fb_scale,
-- view_layout.height * fb_scale);
--
-- _clutter_stage_window_get_dirty_pixel (priv->impl, view, &dirty_x, &dirty_y);
--
-- if (G_LIKELY (!(clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS)))
-+ if (mode != priv->cached_pick_mode)
- {
-- CLUTTER_NOTE (PICK, "Pushing pick scissor clip x: %d, y: %d, 1x1",
-- (int) (dirty_x * fb_scale),
-- (int) (dirty_y * fb_scale));
-- cogl_framebuffer_push_scissor_clip (fb, dirty_x * fb_scale, dirty_y * fb_scale, 1, 1);
-+ _clutter_stage_clear_pick_stack (stage);
-+
-+ /* We don't render to the fb, but have to set one to stop the cogl matrix
-+ * operations from crashing in paint functions. Because the matrices are
-+ * stored relative to the current fb.
-+ */
-+ cogl_push_framebuffer (fb);
-+ context->pick_mode = mode;
-+ clutter_stage_do_paint_view (stage, view, NULL);
-+ context->pick_mode = CLUTTER_PICK_NONE;
-+ priv->cached_pick_mode = mode;
-+ cogl_pop_framebuffer ();
-+
-+ _clutter_stage_freeze_pick_stack (stage);
- }
-
-- viewport_offset_x = x * fb_scale - dirty_x * fb_scale;
-- viewport_offset_y = y * fb_scale - dirty_y * fb_scale;
-- CLUTTER_NOTE (PICK, "Setting viewport to %f, %f, %f, %f",
-- priv->viewport[0] * fb_scale - viewport_offset_x,
-- priv->viewport[1] * fb_scale - viewport_offset_y,
-- priv->viewport[2] * fb_scale,
-- priv->viewport[3] * fb_scale);
-- cogl_framebuffer_set_viewport (fb,
-- priv->viewport[0] * fb_scale - viewport_offset_x,
-- priv->viewport[1] * fb_scale - viewport_offset_y,
-- priv->viewport[2] * fb_scale,
-- priv->viewport[3] * fb_scale);
--
-- read_x = dirty_x * fb_scale;
-- read_y = dirty_y * fb_scale;
--
-- CLUTTER_NOTE (PICK, "Performing pick at %i,%i on view %dx%d+%d+%d s: %f",
-- x, y,
-- view_layout.width, view_layout.height,
-- view_layout.x, view_layout.y, fb_scale);
--
-- cogl_color_init_from_4ub (&stage_pick_id, 255, 255, 255, 255);
-- cogl_framebuffer_clear (fb, COGL_BUFFER_BIT_COLOR | COGL_BUFFER_BIT_DEPTH, &stage_pick_id);
--
-- /* Disable dithering (if any) when doing the painting in pick mode */
-- dither_enabled_save = cogl_framebuffer_get_dither_enabled (fb);
-- cogl_framebuffer_set_dither_enabled (fb, FALSE);
--
-- /* Render the entire scence in pick mode - just single colored silhouette's
-- * are drawn offscreen (as we never swap buffers)
-- */
-- context->pick_mode = mode;
--
-- clutter_stage_do_paint_view (stage, view, NULL);
-- context->pick_mode = CLUTTER_PICK_NONE;
--
-- /* Read the color of the screen co-ords pixel. RGBA_8888_PRE is used
-- even though we don't care about the alpha component because under
-- GLES this is the only format that is guaranteed to work so Cogl
-- will end up having to do a conversion if any other format is
-- used. The format is requested as pre-multiplied because Cogl
-- assumes that all pixels in the framebuffer are premultiplied so
-- it avoids a conversion. */
-- cogl_framebuffer_read_pixels (fb,
-- read_x, read_y, 1, 1,
-- COGL_PIXEL_FORMAT_RGBA_8888_PRE,
-- pixel);
--
-- if (G_UNLIKELY (clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS))
-+ /* 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--)
- {
-- char *file_name =
-- g_strdup_printf ("pick-buffer-%s-view-x-%d",
-- _clutter_actor_get_debug_name (actor),
-- view_layout.x);
-+ const PickRecord *rec = &g_array_index (priv->pick_stack, PickRecord, i);
-
-- read_pixels_to_file (fb, file_name, 0, 0, fb_width, fb_height);
--
-- g_free (file_name);
-+ if (rec->actor && pick_record_contains_pixel (stage, rec, x, y))
-+ return rec->actor;
- }
-
-- /* Restore whether GL_DITHER was enabled */
-- cogl_framebuffer_set_dither_enabled (fb, dither_enabled_save);
--
-- if (G_LIKELY (!(clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS)))
-- cogl_framebuffer_pop_clip (fb);
--
-- cogl_framebuffer_pop_clip (fb);
--
-- _clutter_stage_dirty_viewport (stage);
--
-- if (pixel[0] == 0xff && pixel[1] == 0xff && pixel[2] == 0xff)
-- retval = actor;
-- else
-- {
-- guint32 id_ = _clutter_pixel_to_id (pixel);
--
-- retval = _clutter_stage_get_actor_by_pick_id (stage, id_);
-- CLUTTER_NOTE (PICK, "Picking actor %s with id %u (pixel: 0x%x%x%x%x",
-- G_OBJECT_TYPE_NAME (retval),
-- id_,
-- pixel[0], pixel[1], pixel[2], pixel[3]);
-- }
--
-- cogl_pop_framebuffer ();
--
-- return retval;
-+ return CLUTTER_ACTOR (stage);
- }
-
- static ClutterStageView *
-@@ -1884,7 +1955,9 @@ clutter_stage_finalize (GObject *object)
-
- g_array_free (priv->paint_volume_stack, TRUE);
-
-- _clutter_id_pool_free (priv->pick_id_pool);
-+ _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);
-@@ -2384,7 +2457,10 @@ clutter_stage_init (ClutterStage *self)
- priv->paint_volume_stack =
- g_array_new (FALSE, FALSE, sizeof (ClutterPaintVolume));
-
-- priv->pick_id_pool = _clutter_id_pool_new (256);
-+ 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;
- }
-
- /**
-@@ -4208,6 +4284,12 @@ _clutter_stage_queue_actor_redraw (ClutterStage *stage,
- CLUTTER_NOTE (CLIPPING, "stage_queue_actor_redraw (actor=%s, clip=%p): ",
- _clutter_actor_get_debug_name (actor), clip);
-
-+ /* Queuing a redraw or clip change invalidates the pick cache, unless we're
-+ * in the middle of building it. So we reset the cached flag but don't
-+ * completely clear the pick stack...
-+ */
-+ priv->cached_pick_mode = CLUTTER_PICK_NONE;
-+
- if (!priv->redraw_pending)
- {
- ClutterMasterClock *master_clock;
-@@ -4468,39 +4550,6 @@ _clutter_stage_get_active_framebuffer (ClutterStage *stage)
- return stage->priv->active_framebuffer;
- }
-
--gint32
--_clutter_stage_acquire_pick_id (ClutterStage *stage,
-- ClutterActor *actor)
--{
-- ClutterStagePrivate *priv = stage->priv;
--
-- g_assert (priv->pick_id_pool != NULL);
--
-- return _clutter_id_pool_add (priv->pick_id_pool, actor);
--}
--
--void
--_clutter_stage_release_pick_id (ClutterStage *stage,
-- gint32 pick_id)
--{
-- ClutterStagePrivate *priv = stage->priv;
--
-- g_assert (priv->pick_id_pool != NULL);
--
-- _clutter_id_pool_remove (priv->pick_id_pool, pick_id);
--}
--
--ClutterActor *
--_clutter_stage_get_actor_by_pick_id (ClutterStage *stage,
-- gint32 pick_id)
--{
-- ClutterStagePrivate *priv = stage->priv;
--
-- g_assert (priv->pick_id_pool != NULL);
--
-- return _clutter_id_pool_lookup (priv->pick_id_pool, pick_id);
--}
--
- void
- _clutter_stage_add_pointer_drag_actor (ClutterStage *stage,
- ClutterInputDevice *device,
-diff --git a/clutter/clutter/cogl/clutter-stage-cogl.c b/clutter/clutter/cogl/clutter-stage-cogl.c
-index 4f8d6a650..f957d9840 100644
---- a/clutter/clutter/cogl/clutter-stage-cogl.c
-+++ b/clutter/clutter/cogl/clutter-stage-cogl.c
-@@ -983,73 +983,23 @@ clutter_stage_cogl_redraw (ClutterStageWindow *stage_window)
- stage_cogl->frame_count++;
- }
-
--static void
--clutter_stage_cogl_get_dirty_pixel (ClutterStageWindow *stage_window,
-- ClutterStageView *view,
-- int *x,
-- int *y)
--{
-- CoglFramebuffer *framebuffer = clutter_stage_view_get_framebuffer (view);
-- gboolean has_buffer_age =
-- cogl_is_onscreen (framebuffer) &&
-- is_buffer_age_enabled ();
-- float fb_scale;
-- gboolean scale_is_fractional;
--
-- fb_scale = clutter_stage_view_get_scale (view);
-- if (fb_scale != floorf (fb_scale))
-- scale_is_fractional = TRUE;
-- else
-- scale_is_fractional = FALSE;
--
-- /*
-- * Buffer damage is tracked in the framebuffer coordinate space
-- * using the damage history. When fractional scaling is used, a
-- * coordinate on the stage might not correspond to the exact position of any
-- * physical pixel, which causes issues when painting using the pick mode.
-- *
-- * For now, always use the (0, 0) pixel for picking when using fractional
-- * framebuffer scaling.
-- */
-- if (!has_buffer_age || scale_is_fractional)
-- {
-- *x = 0;
-- *y = 0;
-- }
-- else
-- {
-- ClutterStageViewCogl *view_cogl = CLUTTER_STAGE_VIEW_COGL (view);
-- ClutterStageViewCoglPrivate *view_priv =
-- clutter_stage_view_cogl_get_instance_private (view_cogl);
-- cairo_rectangle_int_t view_layout;
-- cairo_rectangle_int_t *fb_damage;
--
-- clutter_stage_view_get_layout (view, &view_layout);
--
-- fb_damage = &view_priv->damage_history[DAMAGE_HISTORY (view_priv->damage_index - 1)];
-- *x = fb_damage->x / fb_scale;
-- *y = fb_damage->y / fb_scale;
-- }
--}
--
- static void
- clutter_stage_window_iface_init (ClutterStageWindowInterface *iface)
- {
- iface->realize = clutter_stage_cogl_realize;
- iface->unrealize = clutter_stage_cogl_unrealize;
- iface->get_wrapper = clutter_stage_cogl_get_wrapper;
- iface->resize = clutter_stage_cogl_resize;
- iface->show = clutter_stage_cogl_show;
- iface->hide = clutter_stage_cogl_hide;
- iface->schedule_update = clutter_stage_cogl_schedule_update;
- iface->get_update_time = clutter_stage_cogl_get_update_time;
- iface->clear_update_time = clutter_stage_cogl_clear_update_time;
- iface->add_redraw_clip = clutter_stage_cogl_add_redraw_clip;
- iface->has_redraw_clips = clutter_stage_cogl_has_redraw_clips;
- iface->ignoring_redraw_clips = clutter_stage_cogl_ignoring_redraw_clips;
- iface->get_redraw_clip_bounds = clutter_stage_cogl_get_redraw_clip_bounds;
- iface->redraw = clutter_stage_cogl_redraw;
-- iface->get_dirty_pixel = clutter_stage_cogl_get_dirty_pixel;
- }
-
- static void
-diff --git a/clutter/clutter/deprecated/clutter-texture.c b/clutter/clutter/deprecated/clutter-texture.c
-index bea239f45..2c677b8a4 100644
---- a/clutter/clutter/deprecated/clutter-texture.c
-+++ b/clutter/clutter/deprecated/clutter-texture.c
-@@ -572,83 +572,6 @@ gen_texcoords_and_draw_cogl_rectangle (ClutterActor *self,
- 0, 0, t_w, t_h);
- }
-
--static CoglPipeline *
--create_pick_pipeline (ClutterActor *self)
--{
-- ClutterTexture *texture = CLUTTER_TEXTURE (self);
-- ClutterTexturePrivate *priv = texture->priv;
-- CoglPipeline *pick_pipeline = cogl_pipeline_copy (texture_template_pipeline);
-- GError *error = NULL;
--
-- if (!cogl_pipeline_set_layer_combine (pick_pipeline, 0,
-- "RGBA = "
-- " MODULATE (CONSTANT, TEXTURE[A])",
-- &error))
-- {
-- if (!priv->seen_create_pick_pipeline_warning)
-- g_warning ("Error setting up texture combine for shaped "
-- "texture picking: %s", error->message);
-- priv->seen_create_pick_pipeline_warning = TRUE;
-- g_error_free (error);
-- cogl_object_unref (pick_pipeline);
-- return NULL;
-- }
--
-- cogl_pipeline_set_blend (pick_pipeline,
-- "RGBA = ADD (SRC_COLOR[RGBA], 0)",
-- NULL);
--
-- cogl_pipeline_set_alpha_test_function (pick_pipeline,
-- COGL_PIPELINE_ALPHA_FUNC_EQUAL,
-- 1.0);
--
-- return pick_pipeline;
--}
--
--static void
--clutter_texture_pick (ClutterActor *self,
-- const ClutterColor *color)
--{
-- ClutterTexture *texture = CLUTTER_TEXTURE (self);
-- ClutterTexturePrivate *priv = texture->priv;
-- CoglFramebuffer *framebuffer = cogl_get_draw_framebuffer ();
--
-- if (!clutter_actor_should_pick_paint (self))
-- return;
--
-- if (G_LIKELY (priv->pick_with_alpha_supported) && priv->pick_with_alpha)
-- {
-- CoglColor pick_color;
--
-- if (priv->pick_pipeline == NULL)
-- priv->pick_pipeline = create_pick_pipeline (self);
--
-- if (priv->pick_pipeline == NULL)
-- {
-- priv->pick_with_alpha_supported = FALSE;
-- CLUTTER_ACTOR_CLASS (clutter_texture_parent_class)->pick (self,
-- color);
-- return;
-- }
--
-- if (priv->fbo_handle != NULL)
-- update_fbo (self);
--
-- cogl_color_init_from_4ub (&pick_color,
-- color->red,
-- color->green,
-- color->blue,
-- 0xff);
-- cogl_pipeline_set_layer_combine_constant (priv->pick_pipeline,
-- 0, &pick_color);
-- cogl_pipeline_set_layer_texture (priv->pick_pipeline, 0,
-- clutter_texture_get_cogl_texture (texture));
-- gen_texcoords_and_draw_cogl_rectangle (self, priv->pick_pipeline, framebuffer);
-- }
-- else
-- CLUTTER_ACTOR_CLASS (clutter_texture_parent_class)->pick (self, color);
--}
--
- static void
- clutter_texture_paint (ClutterActor *self)
- {
-@@ -767,12 +690,6 @@ clutter_texture_dispose (GObject *object)
- priv->pipeline = NULL;
- }
-
-- if (priv->pick_pipeline != NULL)
-- {
-- cogl_object_unref (priv->pick_pipeline);
-- priv->pick_pipeline = NULL;
-- }
--
- G_OBJECT_CLASS (clutter_texture_parent_class)->dispose (object);
- }
-
-@@ -944,7 +861,6 @@ clutter_texture_class_init (ClutterTextureClass *klass)
- GParamSpec *pspec;
-
- actor_class->paint = clutter_texture_paint;
-- actor_class->pick = clutter_texture_pick;
- actor_class->get_paint_volume = clutter_texture_get_paint_volume;
- actor_class->realize = clutter_texture_realize;
- actor_class->unrealize = clutter_texture_unrealize;
-@@ -1263,11 +1179,9 @@ clutter_texture_init (ClutterTexture *self)
- priv->repeat_y = FALSE;
- priv->sync_actor_size = TRUE;
- priv->fbo_handle = NULL;
-- priv->pick_pipeline = NULL;
- priv->keep_aspect_ratio = FALSE;
- priv->pick_with_alpha = FALSE;
- priv->pick_with_alpha_supported = TRUE;
-- priv->seen_create_pick_pipeline_warning = FALSE;
-
- if (G_UNLIKELY (texture_template_pipeline == NULL))
- {
-@@ -3052,13 +2966,8 @@ clutter_texture_set_pick_with_alpha (ClutterTexture *texture,
- if (priv->pick_with_alpha == pick_with_alpha)
- return;
-
-- if (!pick_with_alpha && priv->pick_pipeline != NULL)
-- {
-- cogl_object_unref (priv->pick_pipeline);
-- priv->pick_pipeline = NULL;
-- }
-+ g_assert (!pick_with_alpha); /* No longer supported */
-
-- /* NB: the pick pipeline is created lazily when we first pick */
- priv->pick_with_alpha = pick_with_alpha;
-
- /* NB: actors are expected to call clutter_actor_queue_redraw when
-diff --git a/clutter/tests/conform/actor-pick.c b/clutter/tests/conform/actor-pick.c
-index 969b4920a..2bf5954c7 100644
---- a/clutter/tests/conform/actor-pick.c
-+++ b/clutter/tests/conform/actor-pick.c
-@@ -5,99 +5,25 @@
- #define STAGE_HEIGHT 480
- #define ACTORS_X 12
- #define ACTORS_Y 16
--#define SHIFT_STEP STAGE_WIDTH / ACTORS_X
-
- typedef struct _State State;
-
- struct _State
- {
- ClutterActor *stage;
- int y, x;
- ClutterActor *actors[ACTORS_X * ACTORS_Y];
- guint actor_width, actor_height;
- guint failed_pass;
- guint failed_idx;
- 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 ())
--
--GType shift_effect_get_type (void);
--
--G_DEFINE_TYPE (ShiftEffect,
-- shift_effect,
-- CLUTTER_TYPE_SHADER_EFFECT);
--
--static void
--shader_paint (ClutterEffect *effect,
-- ClutterEffectPaintFlags flags)
--{
-- ClutterShaderEffect *shader = CLUTTER_SHADER_EFFECT (effect);
-- float tex_width;
-- ClutterActor *actor =
-- clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (effect));
--
-- if (g_test_verbose ())
-- g_debug ("shader_paint");
--
-- clutter_shader_effect_set_shader_source (shader,
-- "uniform sampler2D tex;\n"
-- "uniform float step;\n"
-- "void main (void)\n"
-- "{\n"
-- " cogl_color_out = texture2D(tex, vec2 (cogl_tex_coord_in[0].s + step,\n"
-- " cogl_tex_coord_in[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,
-- ClutterEffectPaintFlags 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 const char *test_passes[] = {
- "No covering actor",
- "Invisible covering actor",
- "Clipped covering actor",
- "Blur effect",
-- "Shift effect",
- };
-
- static gboolean
-@@ -165,42 +91,19 @@ on_timeout (gpointer data)
- if (g_test_verbose ())
- g_print ("With blur effect:\n");
- }
-- else if (test_num == 4)
-- {
-- if (!clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL))
-- continue;
--
-- 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++)
- {
-- if (test_num == 4)
-- x = 1;
-- else
-- x = 0;
-+ 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,
-diff --git a/clutter/tests/conform/meson.build b/clutter/tests/conform/meson.build
-index 916f5c342..e747c9b79 100644
---- a/clutter/tests/conform/meson.build
-+++ b/clutter/tests/conform/meson.build
-@@ -43,7 +43,6 @@ clutter_conform_tests_deprecated_tests = [
- 'behaviours',
- 'group',
- 'rectangle',
-- 'texture',
- ]
-
- clutter_conform_tests = []
-diff --git a/clutter/tests/conform/texture.c b/clutter/tests/conform/texture.c
-deleted file mode 100644
-index 392fd5c47..000000000
---- a/clutter/tests/conform/texture.c
-+++ /dev/null
-@@ -1,84 +0,0 @@
--#define CLUTTER_DISABLE_DEPRECATION_WARNINGS
--#include <clutter/clutter.h>
--#include <string.h>
--
--static CoglHandle
--make_texture (void)
--{
-- guint32 *data = g_malloc (100 * 100 * 4);
-- int x;
-- int y;
--
-- for (y = 0; y < 100; y ++)
-- for (x = 0; x < 100; x++)
-- {
-- if (x < 50 && y < 50)
-- data[y * 100 + x] = 0xff00ff00;
-- else
-- data[y * 100 + x] = 0xff00ffff;
-- }
-- return cogl_texture_new_from_data (100,
-- 100,
-- COGL_TEXTURE_NONE,
-- COGL_PIXEL_FORMAT_ARGB_8888,
-- COGL_PIXEL_FORMAT_ARGB_8888,
-- 400,
-- (guchar *)data);
--}
--
--static void
--texture_pick_with_alpha (void)
--{
-- ClutterTexture *tex = CLUTTER_TEXTURE (clutter_texture_new ());
-- ClutterStage *stage = CLUTTER_STAGE (clutter_test_get_stage ());
-- ClutterActor *actor;
--
-- clutter_texture_set_cogl_texture (tex, make_texture ());
--
-- clutter_actor_add_child (CLUTTER_ACTOR (stage), CLUTTER_ACTOR (tex));
--
-- clutter_actor_show (CLUTTER_ACTOR (stage));
--
-- if (g_test_verbose ())
-- {
-- g_print ("\nstage = %p\n", stage);
-- g_print ("texture = %p\n\n", tex);
-- }
--
-- clutter_texture_set_pick_with_alpha (tex, TRUE);
-- if (g_test_verbose ())
-- g_print ("Testing with pick-with-alpha enabled:\n");
--
-- /* This should fall through and hit the stage: */
-- actor = clutter_stage_get_actor_at_pos (stage, CLUTTER_PICK_ALL, 10, 10);
-- if (g_test_verbose ())
-- g_print ("actor @ (10, 10) = %p\n", actor);
-- g_assert (actor == CLUTTER_ACTOR (stage));
--
-- /* The rest should hit the texture */
-- actor = clutter_stage_get_actor_at_pos (stage, CLUTTER_PICK_ALL, 90, 10);
-- if (g_test_verbose ())
-- g_print ("actor @ (90, 10) = %p\n", actor);
-- g_assert (actor == CLUTTER_ACTOR (tex));
-- actor = clutter_stage_get_actor_at_pos (stage, CLUTTER_PICK_ALL, 90, 90);
-- if (g_test_verbose ())
-- g_print ("actor @ (90, 90) = %p\n", actor);
-- g_assert (actor == CLUTTER_ACTOR (tex));
-- actor = clutter_stage_get_actor_at_pos (stage, CLUTTER_PICK_ALL, 10, 90);
-- if (g_test_verbose ())
-- g_print ("actor @ (10, 90) = %p\n", actor);
-- g_assert (actor == CLUTTER_ACTOR (tex));
--
-- clutter_texture_set_pick_with_alpha (tex, FALSE);
-- if (g_test_verbose ())
-- g_print ("Testing with pick-with-alpha disabled:\n");
--
-- actor = clutter_stage_get_actor_at_pos (stage, CLUTTER_PICK_ALL, 10, 10);
-- if (g_test_verbose ())
-- g_print ("actor @ (10, 10) = %p\n", actor);
-- g_assert (actor == CLUTTER_ACTOR (tex));
--}
--
--CLUTTER_TEST_SUITE (
-- CLUTTER_TEST_UNIT ("/texture/pick-with-alpha", texture_pick_with_alpha)
--)
-diff --git a/src/compositor/meta-surface-actor.c b/src/compositor/meta-surface-actor.c
-index ca4ca19a9..814199145 100644
---- a/src/compositor/meta-surface-actor.c
-+++ b/src/compositor/meta-surface-actor.c
-@@ -70,38 +70,23 @@ meta_surface_actor_pick (ClutterActor *actor,
- else
- {
- int n_rects;
-- float *rectangles;
- int i;
-- CoglPipeline *pipeline;
-- CoglContext *ctx;
-- CoglFramebuffer *fb;
-- CoglColor cogl_color;
-
- n_rects = cairo_region_num_rectangles (priv->input_region);
-- rectangles = g_alloca (sizeof (float) * 4 * n_rects);
-
- for (i = 0; i < n_rects; i++)
- {
- cairo_rectangle_int_t rect;
-- int pos = i * 4;
-+ ClutterActorBox box;
-
- cairo_region_get_rectangle (priv->input_region, i, &rect);
-
-- rectangles[pos + 0] = rect.x;
-- rectangles[pos + 1] = rect.y;
-- rectangles[pos + 2] = rect.x + rect.width;
-- rectangles[pos + 3] = rect.y + rect.height;
-+ box.x1 = rect.x;
-+ box.y1 = rect.y;
-+ box.x2 = rect.x + rect.width;
-+ box.y2 = rect.y + rect.height;
-+ clutter_actor_pick_box (actor, &box);
- }
--
-- ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
-- fb = cogl_get_draw_framebuffer ();
--
-- cogl_color_init_from_4ub (&cogl_color, color->red, color->green, color->blue, color->alpha);
--
-- pipeline = cogl_pipeline_new (ctx);
-- cogl_pipeline_set_color (pipeline, &cogl_color);
-- cogl_framebuffer_draw_rectangles (fb, pipeline, rectangles, n_rects);
-- cogl_object_unref (pipeline);
- }
-
- clutter_actor_iter_init (&iter, actor);
---
-2.22.0
-
Copied: mutter/trunk/0002-Geometric-OpenGL-less-picking.patch (from rev 361399, mutter/trunk/0001-Geometric-OpenGL-less-picking.patch)
===================================================================
--- 0002-Geometric-OpenGL-less-picking.patch (rev 0)
+++ 0002-Geometric-OpenGL-less-picking.patch 2019-08-29 22:46:42 UTC (rev 361400)
@@ -0,0 +1,1714 @@
+From 6a18d760ed05b87af7aae43f1963eb32bc1d81e7 Mon Sep 17 00:00:00 2001
+From: Daniel van Vugt <daniel.van.vugt at canonical.com>
+Date: Thu, 2 Aug 2018 19:03:30 +0800
+Subject: [PATCH 2/2] Geometric (OpenGL-less) picking
+
+Cherry picked from commit b9170a69314739638db7f471b87ba55a906bdf03.
+
+Redesign picking to avoid using OpenGL. Previously picking involved
+several GL operations per mouse movement, but now the GPU/GL isn't
+used at all.
+
+By avoiding OpenGL and the graphics driver we also reduce CPU usage.
+Despite reimplementing the logic on the CPU, it still takes less CPU
+time than going through GL did.
+
+This new approach also dramatically reduces the number of picking paint
+cycles required for cursor movement since the pickability of the entire
+screen is calculated and cached. The cache is only invalidated when the
+screen contents change so for typical desktop usage where the screen is
+mostly idle, cursor movement doesn't incur anywhere near as many paint
+cycles as it used to.
+
+CPU usage on an Intel i7-7700, tested with two different GPUs/drivers:
+
+ | | Intel | Nvidia |
+ | ------: | --------: | -----: |
+ | Moving the mouse: |
+ | Before | 10% | 10% |
+ | After | 6% | 6% |
+ | Moving a window: |
+ | Before | 23% | 81% |
+ | After | 19% | 40% |
+
+Closes: https://gitlab.gnome.org/GNOME/mutter/issues/154,
+ https://gitlab.gnome.org/GNOME/mutter/issues/691
+
+Helps significantly with: https://gitlab.gnome.org/GNOME/mutter/issues/283,
+ https://gitlab.gnome.org/GNOME/mutter/issues/590,
+ https://gitlab.gnome.org/GNOME/mutter/issues/700
+
+https://gitlab.gnome.org/GNOME/mutter/merge_requests/189
+---
+ clutter/clutter/clutter-actor-private.h | 2 -
+ clutter/clutter/clutter-actor.c | 254 +++++++----
+ clutter/clutter/clutter-actor.h | 4 +
+ clutter/clutter/clutter-debug.h | 3 +-
+ clutter/clutter/clutter-main.c | 120 -----
+ clutter/clutter/clutter-private.h | 5 -
+ clutter/clutter/clutter-stage-private.h | 16 +-
+ clutter/clutter/clutter-stage-window.c | 18 -
+ clutter/clutter/clutter-stage-window.h | 8 -
+ clutter/clutter/clutter-stage.c | 433 +++++++++++--------
+ clutter/clutter/cogl/clutter-stage-cogl.c | 50 ---
+ clutter/clutter/deprecated/clutter-texture.c | 93 +---
+ clutter/tests/conform/actor-pick.c | 99 +----
+ clutter/tests/conform/meson.build | 1 -
+ clutter/tests/conform/texture.c | 84 ----
+ src/compositor/meta-surface-actor.c | 27 +-
+ 16 files changed, 434 insertions(+), 783 deletions(-)
+ delete mode 100644 clutter/tests/conform/texture.c
+
+diff --git a/clutter/clutter/clutter-actor-private.h b/clutter/clutter/clutter-actor-private.h
+index c44f6342f..9bf1a3049 100644
+--- a/clutter/clutter/clutter-actor-private.h
++++ b/clutter/clutter/clutter-actor-private.h
+@@ -297,8 +297,6 @@ const gchar * _clutter_actor_get_debug_name
+ void _clutter_actor_push_clone_paint (void);
+ void _clutter_actor_pop_clone_paint (void);
+
+-guint32 _clutter_actor_get_pick_id (ClutterActor *self);
+-
+ void _clutter_actor_shader_pre_paint (ClutterActor *actor,
+ gboolean repeat);
+ void _clutter_actor_shader_post_paint (ClutterActor *actor);
+diff --git a/clutter/clutter/clutter-actor.c b/clutter/clutter/clutter-actor.c
+index a7fecb830..5feae6574 100644
+--- a/clutter/clutter/clutter-actor.c
++++ b/clutter/clutter/clutter-actor.c
+@@ -729,8 +729,6 @@ struct _ClutterActorPrivate
+
+ gchar *name; /* a non-unique name, used for debugging */
+
+- gint32 pick_id; /* per-stage unique id, used for picking */
+-
+ /* a back-pointer to the Pango context that we can use
+ * to create pre-configured PangoLayout
+ */
+@@ -1282,6 +1280,129 @@ clutter_actor_verify_map_state (ClutterActor *self)
+
+ #endif /* CLUTTER_ENABLE_DEBUG */
+
++static gboolean
++_clutter_actor_transform_local_box_to_stage (ClutterActor *self,
++ const ClutterActorBox *box,
++ ClutterPoint vertices[4])
++{
++ ClutterActor *stage = _clutter_actor_get_stage_internal (self);
++ CoglMatrix stage_transform, inv_stage_transform;
++ CoglMatrix modelview, transform_to_stage;
++ int v;
++
++ g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
++ g_return_val_if_fail (box != NULL, FALSE);
++
++ g_return_val_if_fail (stage != NULL, FALSE);
++ g_return_val_if_fail (box->x1 <= box->x2, FALSE);
++ g_return_val_if_fail (box->y1 <= box->y2, FALSE);
++
++ /* Below is generally equivalent to:
++ *
++ * _clutter_actor_get_relative_transformation_matrix (self,
++ * stage,
++ * &transform_to_stage);
++ *
++ * but we do it the hard way here instead so as to accurately include any
++ * cogl transformations that an actor's paint function might have added in.
++ * Those additional transformations are only known to cogl matrices and not
++ * known to the clutter getter like above. So this way we more accurately
++ * represent what's really getting painted.
++ */
++ clutter_actor_get_transform (stage, &stage_transform);
++ if (!cogl_matrix_get_inverse (&stage_transform, &inv_stage_transform))
++ return FALSE;
++ cogl_get_modelview_matrix (&modelview);
++ cogl_matrix_multiply (&transform_to_stage, &inv_stage_transform, &modelview);
++
++ 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++)
++ {
++ gfloat z = 0.f;
++ gfloat w = 1.f;
++
++ cogl_matrix_transform_point (&transform_to_stage,
++ &vertices[v].x,
++ &vertices[v].y,
++ &z,
++ &w);
++ }
++
++ return TRUE;
++}
++
++/**
++ * clutter_actor_pick_box:
++ * @self: The #ClutterActor being "pick" painted.
++ * @box: A rectangle in the actor's own local coordinates.
++ *
++ * Logs (does a virtual paint of) a rectangle for picking. Note that @box is
++ * in the actor's own local coordinates, so is usually {0,0,width,height}
++ * to include the whole actor. That is unless the actor has a shaped input
++ * region in which case you may wish to log the (multiple) smaller rectangles
++ * that make up the input region.
++ */
++void
++clutter_actor_pick_box (ClutterActor *self,
++ const ClutterActorBox *box)
++{
++ ClutterActor *stage;
++ ClutterPoint vertices[4];
++
++ g_return_if_fail (CLUTTER_IS_ACTOR (self));
++ g_return_if_fail (box != NULL);
++
++ /* An empty box to a "pick" paint means to paint nothing at all. */
++ if (box->x1 >= box->x2 || box->y1 >= box->y2)
++ return;
++
++ stage = _clutter_actor_get_stage_internal (self);
++
++ if (_clutter_actor_transform_local_box_to_stage (self, box, vertices))
++ _clutter_stage_log_pick (CLUTTER_STAGE (stage), vertices, self);
++}
++
++static gboolean
++_clutter_actor_push_pick_clip (ClutterActor *self,
++ const ClutterActorBox *clip)
++{
++ ClutterActor *stage;
++ ClutterPoint vertices[4];
++
++ g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
++ g_return_val_if_fail (clip != NULL, FALSE);
++
++ stage = _clutter_actor_get_stage_internal (self);
++
++ if (!_clutter_actor_transform_local_box_to_stage (self, clip, vertices))
++ return FALSE;
++
++ _clutter_stage_push_pick_clip (CLUTTER_STAGE (stage), vertices);
++ return TRUE;
++}
++
++static void
++_clutter_actor_pop_pick_clip (ClutterActor *self)
++{
++ ClutterActor *stage;
++
++ g_return_if_fail (CLUTTER_IS_ACTOR (self));
++
++ 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)
+@@ -1510,8 +1631,7 @@ clutter_actor_update_map_state (ClutterActor *self,
+ static void
+ clutter_actor_real_map (ClutterActor *self)
+ {
+- ClutterActorPrivate *priv = self->priv;
+- ClutterActor *stage, *iter;
++ ClutterActor *iter;
+
+ g_assert (!CLUTTER_ACTOR_IS_MAPPED (self));
+
+@@ -1522,13 +1642,6 @@ clutter_actor_real_map (ClutterActor *self)
+
+ self->priv->needs_paint_volume_update = TRUE;
+
+- stage = _clutter_actor_get_stage_internal (self);
+- priv->pick_id = _clutter_stage_acquire_pick_id (CLUTTER_STAGE (stage), self);
+-
+- CLUTTER_NOTE (ACTOR, "Pick id '%d' for actor '%s'",
+- priv->pick_id,
+- _clutter_actor_get_debug_name (self));
+-
+ clutter_actor_ensure_resource_scale (self);
+
+ /* notify on parent mapped before potentially mapping
+@@ -1633,11 +1746,6 @@ clutter_actor_real_unmap (ClutterActor *self)
+
+ stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (self));
+
+- if (stage != NULL)
+- _clutter_stage_release_pick_id (stage, priv->pick_id);
+-
+- priv->pick_id = -1;
+-
+ if (stage != NULL &&
+ clutter_stage_get_key_focus (stage) == self)
+ {
+@@ -2256,46 +2364,14 @@ static void
+ clutter_actor_real_pick (ClutterActor *self,
+ const ClutterColor *color)
+ {
+- CoglFramebuffer *framebuffer = cogl_get_draw_framebuffer ();
+-
+- /* the default implementation is just to paint a rectangle
+- * with the same size of the actor using the passed color
+- */
+ if (clutter_actor_should_pick_paint (self))
+ {
+- static CoglPipeline *default_pick_pipeline = NULL;
+- ClutterActorBox box = { 0, };
+- CoglPipeline *pick_pipeline;
+- float width, height;
+-
+- if (G_UNLIKELY (default_pick_pipeline == NULL))
+- {
+- CoglContext *ctx =
+- clutter_backend_get_cogl_context (clutter_get_default_backend ());
+-
+- default_pick_pipeline = cogl_pipeline_new (ctx);
+- }
+-
+- g_assert (default_pick_pipeline != NULL);
+- pick_pipeline = cogl_pipeline_copy (default_pick_pipeline);
+-
+- clutter_actor_get_allocation_box (self, &box);
+-
+- width = box.x2 - box.x1;
+- height = box.y2 - box.y1;
+-
+- cogl_pipeline_set_color4ub (pick_pipeline,
+- color->red,
+- color->green,
+- color->blue,
+- color->alpha);
++ ClutterActorBox box = {0,
++ 0,
++ clutter_actor_get_width (self),
++ clutter_actor_get_height (self)};
+
+- cogl_framebuffer_draw_rectangle (framebuffer,
+- pick_pipeline,
+- 0, 0,
+- width, height);
+-
+- cogl_object_unref (pick_pipeline);
++ clutter_actor_pick_box (self, &box);
+ }
+
+ /* XXX - this thoroughly sucks, but we need to maintain compatibility
+@@ -3586,15 +3662,6 @@ _clutter_actor_update_last_paint_volume (ClutterActor *self)
+ priv->last_paint_volume_valid = TRUE;
+ }
+
+-guint32
+-_clutter_actor_get_pick_id (ClutterActor *self)
+-{
+- if (self->priv->pick_id < 0)
+- return 0;
+-
+- return self->priv->pick_id;
+-}
+-
+ /* This is the same as clutter_actor_add_effect except that it doesn't
+ queue a redraw and it doesn't notify on the effect property */
+ static void
+@@ -3826,6 +3893,7 @@ clutter_actor_paint (ClutterActor *self)
+ {
+ ClutterActorPrivate *priv;
+ ClutterPickMode pick_mode;
++ ClutterActorBox clip;
+ gboolean clip_set = FALSE;
+ ClutterStage *stage;
+
+@@ -3919,24 +3987,38 @@ clutter_actor_paint (ClutterActor *self)
+
+ if (priv->has_clip)
+ {
+- CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage);
+- cogl_framebuffer_push_rectangle_clip (fb,
+- priv->clip.origin.x,
+- priv->clip.origin.y,
+- priv->clip.origin.x + priv->clip.size.width,
+- priv->clip.origin.y + priv->clip.size.height);
++ clip.x1 = priv->clip.origin.x;
++ clip.y1 = priv->clip.origin.y;
++ clip.x2 = priv->clip.origin.x + priv->clip.size.width;
++ clip.y2 = priv->clip.origin.y + priv->clip.size.height;
+ clip_set = TRUE;
+ }
+ else if (priv->clip_to_allocation)
+ {
+- CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage);
+- gfloat width, height;
++ clip.x1 = 0.f;
++ clip.y1 = 0.f;
++ clip.x2 = priv->allocation.x2 - priv->allocation.x1;
++ clip.y2 = priv->allocation.y2 - priv->allocation.y1;
++ clip_set = TRUE;
++ }
+
+- width = priv->allocation.x2 - priv->allocation.x1;
+- height = priv->allocation.y2 - priv->allocation.y1;
++ if (clip_set)
++ {
++ if (pick_mode == CLUTTER_PICK_NONE)
++ {
++ CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage);
+
+- cogl_framebuffer_push_rectangle_clip (fb, 0, 0, width, height);
+- clip_set = TRUE;
++ cogl_framebuffer_push_rectangle_clip (fb,
++ clip.x1,
++ clip.y1,
++ clip.x2,
++ clip.y2);
++ }
++ else
++ {
++ if (!_clutter_actor_push_pick_clip (self, &clip))
++ clip_set = FALSE;
++ }
+ }
+
+ if (pick_mode == CLUTTER_PICK_NONE)
+@@ -4017,9 +4099,16 @@ clutter_actor_paint (ClutterActor *self)
+ done:
+ if (clip_set)
+ {
+- CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage);
++ if (pick_mode == CLUTTER_PICK_NONE)
++ {
++ CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage);
+
+- cogl_framebuffer_pop_clip (fb);
++ cogl_framebuffer_pop_clip (fb);
++ }
++ else
++ {
++ _clutter_actor_pop_pick_clip (self);
++ }
+ }
+
+ cogl_pop_matrix ();
+@@ -4090,11 +4179,12 @@ clutter_actor_continue_paint (ClutterActor *self)
+ {
+ 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.
++ /* The actor will log a silhouette of itself to the stage pick log.
++ * Note that the picking color is no longer used as the "log" instead
++ * keeps a weak pointer to the actor itself. But we keep the color
++ * parameter for now so as to maintain ABI compatibility. The color
++ * parameter can be removed when someone feels like breaking the ABI
++ * along with gnome-shell.
+ *
+ * XXX:2.0 - Call the pick() virtual directly
+ */
+@@ -8602,8 +8692,6 @@ clutter_actor_init (ClutterActor *self)
+
+ self->priv = priv = clutter_actor_get_instance_private (self);
+
+- priv->pick_id = -1;
+-
+ priv->opacity = 0xff;
+ priv->show_on_set_parent = TRUE;
+ priv->resource_scale = -1.0f;
+diff --git a/clutter/clutter/clutter-actor.h b/clutter/clutter/clutter-actor.h
+index 7d2168af1..b1abff86d 100644
+--- a/clutter/clutter/clutter-actor.h
++++ b/clutter/clutter/clutter-actor.h
+@@ -902,6 +902,10 @@ void clutter_actor_bind_model_with_properties
+ const char *first_model_property,
+ ...);
+
++CLUTTER_EXPORT
++void clutter_actor_pick_box (ClutterActor *self,
++ const ClutterActorBox *box);
++
+ G_END_DECLS
+
+ #endif /* __CLUTTER_ACTOR_H__ */
+diff --git a/clutter/clutter/clutter-debug.h b/clutter/clutter/clutter-debug.h
+index 2462385f6..3744c648f 100644
+--- a/clutter/clutter/clutter-debug.h
++++ b/clutter/clutter/clutter-debug.h
+@@ -29,8 +29,7 @@ typedef enum
+
+ typedef enum
+ {
+- CLUTTER_DEBUG_NOP_PICKING = 1 << 0,
+- CLUTTER_DEBUG_DUMP_PICK_BUFFERS = 1 << 1
++ CLUTTER_DEBUG_NOP_PICKING = 1 << 0
+ } ClutterPickDebugFlag;
+
+ typedef enum
+diff --git a/clutter/clutter/clutter-main.c b/clutter/clutter/clutter-main.c
+index 71ec0d80c..c2ec82697 100644
+--- a/clutter/clutter/clutter-main.c
++++ b/clutter/clutter/clutter-main.c
+@@ -130,7 +130,6 @@ static const GDebugKey clutter_debug_keys[] = {
+
+ static const GDebugKey clutter_pick_debug_keys[] = {
+ { "nop-picking", CLUTTER_DEBUG_NOP_PICKING },
+- { "dump-pick-buffers", CLUTTER_DEBUG_DUMP_PICK_BUFFERS },
+ };
+
+ static const GDebugKey clutter_paint_debug_keys[] = {
+@@ -532,125 +531,6 @@ clutter_get_motion_events_enabled (void)
+ return _clutter_context_get_motion_events_enabled ();
+ }
+
+-void
+-_clutter_id_to_color (guint id_,
+- ClutterColor *col)
+-{
+- ClutterMainContext *ctx;
+- gint red, green, blue;
+-
+- ctx = _clutter_context_get_default ();
+-
+- if (ctx->fb_g_mask == 0)
+- {
+- /* Figure out framebuffer masks used for pick */
+- cogl_get_bitmasks (&ctx->fb_r_mask,
+- &ctx->fb_g_mask,
+- &ctx->fb_b_mask, NULL);
+-
+- ctx->fb_r_mask_used = ctx->fb_r_mask;
+- ctx->fb_g_mask_used = ctx->fb_g_mask;
+- ctx->fb_b_mask_used = ctx->fb_b_mask;
+-
+- /* XXX - describe what "fuzzy picking" is */
+- if (clutter_use_fuzzy_picking)
+- {
+- ctx->fb_r_mask_used--;
+- ctx->fb_g_mask_used--;
+- ctx->fb_b_mask_used--;
+- }
+- }
+-
+- /* compute the numbers we'll store in the components */
+- red = (id_ >> (ctx->fb_g_mask_used+ctx->fb_b_mask_used))
+- & (0xff >> (8-ctx->fb_r_mask_used));
+- green = (id_ >> ctx->fb_b_mask_used)
+- & (0xff >> (8-ctx->fb_g_mask_used));
+- blue = (id_)
+- & (0xff >> (8-ctx->fb_b_mask_used));
+-
+- /* shift left bits a bit and add one, this circumvents
+- * at least some potential rounding errors in GL/GLES
+- * driver / hw implementation.
+- */
+- if (ctx->fb_r_mask_used != ctx->fb_r_mask)
+- red = red * 2;
+- if (ctx->fb_g_mask_used != ctx->fb_g_mask)
+- green = green * 2;
+- if (ctx->fb_b_mask_used != ctx->fb_b_mask)
+- blue = blue * 2;
+-
+- /* shift up to be full 8bit values */
+- red = (red << (8 - ctx->fb_r_mask)) | (0x7f >> (ctx->fb_r_mask_used));
+- green = (green << (8 - ctx->fb_g_mask)) | (0x7f >> (ctx->fb_g_mask_used));
+- blue = (blue << (8 - ctx->fb_b_mask)) | (0x7f >> (ctx->fb_b_mask_used));
+-
+- col->red = red;
+- col->green = green;
+- col->blue = blue;
+- col->alpha = 0xff;
+-
+- /* XXX: We rotate the nibbles of the colors here so that there is a
+- * visible variation between colors of sequential actor identifiers;
+- * otherwise pick buffers dumped to an image will pretty much just look
+- * black.
+- */
+- if (G_UNLIKELY (clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS))
+- {
+- col->red = (col->red << 4) | (col->red >> 4);
+- col->green = (col->green << 4) | (col->green >> 4);
+- col->blue = (col->blue << 4) | (col->blue >> 4);
+- }
+-}
+-
+-guint
+-_clutter_pixel_to_id (guchar pixel[4])
+-{
+- ClutterMainContext *ctx;
+- gint red, green, blue;
+- guint retval;
+-
+- ctx = _clutter_context_get_default ();
+-
+- /* reduce the pixel components to the number of bits actually used of the
+- * 8bits.
+- */
+- if (G_UNLIKELY (clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS))
+- {
+- guchar tmp;
+-
+- /* XXX: In _clutter_id_to_color we rotated the nibbles of the colors so
+- * that there is a visible variation between colors of sequential actor
+- * identifiers (otherwise pick buffers dumped to an image will pretty
+- * much just look black.) Here we reverse that rotation.
+- */
+- tmp = ((pixel[0] << 4) | (pixel[0] >> 4));
+- red = tmp >> (8 - ctx->fb_r_mask);
+- tmp = ((pixel[1] << 4) | (pixel[1] >> 4));
+- green = tmp >> (8 - ctx->fb_g_mask);
+- tmp = ((pixel[2] << 4) | (pixel[2] >> 4));
+- blue = tmp >> (8 - ctx->fb_b_mask);
+- }
+- else
+- {
+- red = pixel[0] >> (8 - ctx->fb_r_mask);
+- green = pixel[1] >> (8 - ctx->fb_g_mask);
+- blue = pixel[2] >> (8 - ctx->fb_b_mask);
+- }
+-
+- /* divide potentially by two if 'fuzzy' */
+- red = red >> (ctx->fb_r_mask - ctx->fb_r_mask_used);
+- green = green >> (ctx->fb_g_mask - ctx->fb_g_mask_used);
+- blue = blue >> (ctx->fb_b_mask - ctx->fb_b_mask_used);
+-
+- /* combine the correct per component values into the final id */
+- retval = blue
+- + (green << ctx->fb_b_mask_used)
+- + (red << (ctx->fb_b_mask_used + ctx->fb_g_mask_used));
+-
+- return retval;
+-}
+-
+ static CoglPangoFontMap *
+ clutter_context_get_pango_fontmap (void)
+ {
+diff --git a/clutter/clutter/clutter-private.h b/clutter/clutter/clutter-private.h
+index a5cd1fa19..47bef4ac2 100644
+--- a/clutter/clutter/clutter-private.h
++++ b/clutter/clutter/clutter-private.h
+@@ -210,11 +210,6 @@ gboolean _clutter_feature_init (GError **error);
+ gboolean _clutter_diagnostic_enabled (void);
+ void _clutter_diagnostic_message (const char *fmt, ...) G_GNUC_PRINTF (1, 2);
+
+-/* Picking code */
+-guint _clutter_pixel_to_id (guchar pixel[4]);
+-void _clutter_id_to_color (guint id,
+- ClutterColor *col);
+-
+ void _clutter_set_sync_to_vblank (gboolean sync_to_vblank);
+
+ /* use this function as the accumulator if you have a signal with
+diff --git a/clutter/clutter/clutter-stage-private.h b/clutter/clutter/clutter-stage-private.h
+index 4799c29e1..f8591ddcb 100644
+--- a/clutter/clutter/clutter-stage-private.h
++++ b/clutter/clutter/clutter-stage-private.h
+@@ -74,6 +74,15 @@ gint64 _clutter_stage_get_update_time (ClutterStage *stage);
+ void _clutter_stage_clear_update_time (ClutterStage *stage);
+ gboolean _clutter_stage_has_full_redraw_queued (ClutterStage *stage);
+
++void _clutter_stage_log_pick (ClutterStage *stage,
++ const ClutterPoint vertices[4],
++ ClutterActor *actor);
++
++void _clutter_stage_push_pick_clip (ClutterStage *stage,
++ const ClutterPoint vertices[4]);
++
++void _clutter_stage_pop_pick_clip (ClutterStage *stage);
++
+ ClutterActor *_clutter_stage_do_pick (ClutterStage *stage,
+ gint x,
+ gint y,
+@@ -92,13 +101,6 @@ void _clutter_stage_queue_redraw_entry_invalidate (Clut
+
+ CoglFramebuffer *_clutter_stage_get_active_framebuffer (ClutterStage *stage);
+
+-gint32 _clutter_stage_acquire_pick_id (ClutterStage *stage,
+- ClutterActor *actor);
+-void _clutter_stage_release_pick_id (ClutterStage *stage,
+- gint32 pick_id);
+-ClutterActor * _clutter_stage_get_actor_by_pick_id (ClutterStage *stage,
+- gint32 pick_id);
+-
+ void _clutter_stage_add_pointer_drag_actor (ClutterStage *stage,
+ ClutterInputDevice *device,
+ ClutterActor *actor);
+diff --git a/clutter/clutter/clutter-stage-window.c b/clutter/clutter/clutter-stage-window.c
+index e8fa976a7..4c4ef9d64 100644
+--- a/clutter/clutter/clutter-stage-window.c
++++ b/clutter/clutter/clutter-stage-window.c
+@@ -293,24 +293,6 @@ _clutter_stage_window_redraw (ClutterStageWindow *window)
+ iface->redraw (window);
+ }
+
+-
+-void
+-_clutter_stage_window_get_dirty_pixel (ClutterStageWindow *window,
+- ClutterStageView *view,
+- int *x, int *y)
+-{
+- ClutterStageWindowInterface *iface;
+-
+- *x = 0;
+- *y = 0;
+-
+- g_return_if_fail (CLUTTER_IS_STAGE_WINDOW (window));
+-
+- iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window);
+- if (iface->get_dirty_pixel)
+- iface->get_dirty_pixel (window, view, x, y);
+-}
+-
+ gboolean
+ _clutter_stage_window_can_clip_redraws (ClutterStageWindow *window)
+ {
+diff --git a/clutter/clutter/clutter-stage-window.h b/clutter/clutter/clutter-stage-window.h
+index 6c3601745..aa0c5f71c 100644
+--- a/clutter/clutter/clutter-stage-window.h
++++ b/clutter/clutter/clutter-stage-window.h
+@@ -68,10 +68,6 @@ struct _ClutterStageWindowInterface
+
+ void (* redraw) (ClutterStageWindow *stage_window);
+
+- void (* get_dirty_pixel) (ClutterStageWindow *stage_window,
+- ClutterStageView *view,
+- int *x, int *y);
+-
+ gboolean (* can_clip_redraws) (ClutterStageWindow *stage_window);
+
+ GList *(* get_views) (ClutterStageWindow *stage_window);
+@@ -119,10 +115,6 @@ void _clutter_stage_window_set_accept_focus (ClutterStageWin
+
+ void _clutter_stage_window_redraw (ClutterStageWindow *window);
+
+-void _clutter_stage_window_get_dirty_pixel (ClutterStageWindow *window,
+- ClutterStageView *view,
+- int *x, int *y);
+-
+ gboolean _clutter_stage_window_can_clip_redraws (ClutterStageWindow *window);
+
+ GList * _clutter_stage_window_get_views (ClutterStageWindow *window);
+diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c
+index 1eea5b305..bd0ac9ed2 100644
+--- a/clutter/clutter/clutter-stage.c
++++ b/clutter/clutter/clutter-stage.c
+@@ -103,6 +103,23 @@ struct _ClutterStageQueueRedrawEntry
+ ClutterPaintVolume clip;
+ };
+
++struct _PickRecord
++{
++ ClutterPoint vertex[4];
++ ClutterActor *actor;
++ int clip_stack_top;
++};
++
++typedef struct _PickRecord PickRecord;
++
++struct _PickClipRecord
++{
++ int prev;
++ ClutterPoint vertex[4];
++};
++
++typedef struct _PickClipRecord PickClipRecord;
++
+ struct _ClutterStagePrivate
+ {
+ /* the stage implementation */
+@@ -136,7 +153,11 @@ struct _ClutterStagePrivate
+ GTimer *fps_timer;
+ gint32 timer_n_frames;
+
+- ClutterIDPool *pick_id_pool;
++ GArray *pick_stack;
++ GArray *pick_clip_stack;
++ int pick_clip_stack_top;
++ gboolean pick_stack_frozen;
++ ClutterPickMode cached_pick_mode;
+
+ #ifdef CLUTTER_ENABLE_DEBUG
+ gulong redraw_count;
+@@ -322,6 +343,177 @@ clutter_stage_get_preferred_height (ClutterActor *self,
+ *natural_height_p = geom.height;
+ }
+
++/* In order to keep weak pointers valid between frames we need them to not
++ * move in memory, so the stack is marked as "frozen".
++ */
++static void
++_clutter_stage_freeze_pick_stack (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
++_clutter_stage_thaw_pick_stack (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;
++
++ _clutter_stage_thaw_pick_stack (stage);
++ g_array_set_size (priv->pick_stack, 0);
++ g_array_set_size (priv->pick_clip_stack, 0);
++ priv->pick_clip_stack_top = -1;
++ priv->cached_pick_mode = CLUTTER_PICK_NONE;
++}
++
++void
++_clutter_stage_log_pick (ClutterStage *stage,
++ const ClutterPoint vertices[4],
++ 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 (ClutterPoint));
++ 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 ClutterPoint vertices[4])
++{
++ 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 (ClutterPoint));
++
++ 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
++pick_record_contains_pixel (ClutterStage *stage,
++ const PickRecord *rec,
++ int x,
++ int y)
++{
++ ClutterStagePrivate *priv;
++ int clip_index;
++
++ /* We test the centre point of the pixel so that we can use
++ * clutter_point_inside_polygon and avoid any ambiguity about the boundaries
++ * of pixel-aligned actors. If we were to use point (x,y) instead (which is
++ * the top-left corner of the pixel) then we'd need to use
++ * clutter_point_touches_polygon.
++ */
++ const ClutterPoint point = { x + 0.5f, y + 0.5f };
++
++ if (!clutter_point_inside_polygon (&point, rec->vertex, 4))
++ return FALSE;
++
++ priv = stage->priv;
++
++ /* pick_clip_stack is actually a graph containing one or more trees. And the
++ * clip_stack_top for a given PickRecord is the path from a leaf of such a
++ * tree to its root. It's still a stack though, from the point of view of the
++ * code that built it and the code that traverses it here.
++ *
++ * Allowing multiple stacks to overlap and merge into trees saves us time and
++ * memory.
++ */
++ clip_index = rec->clip_stack_top;
++ while (clip_index >= 0)
++ {
++ const PickClipRecord *clip = &g_array_index (priv->pick_clip_stack,
++ PickClipRecord,
++ clip_index);
++
++ if (!clutter_point_inside_polygon (&point, clip->vertex, 4))
++ return FALSE;
++
++ clip_index = clip->prev;
++ }
++
++ return TRUE;
++}
++
+ static inline void
+ queue_full_redraw (ClutterStage *stage)
+ {
+@@ -632,6 +824,12 @@ clutter_stage_do_paint_view (ClutterStage *stage,
+ float viewport[4];
+ cairo_rectangle_int_t geom;
+
++ /* Any mode of painting/picking invalidates the pick cache, unless we're
++ * in the middle of building it. So we reset the cached flag but don't
++ * completely clear the pick stack...
++ */
++ priv->cached_pick_mode = CLUTTER_PICK_NONE;
++
+ _clutter_stage_window_get_geometry (priv->impl, &geom);
+
+ viewport[0] = priv->viewport[0];
+@@ -1397,40 +1595,6 @@ clutter_stage_get_redraw_clip_bounds (ClutterStage *stage,
+ }
+ }
+
+-static void
+-read_pixels_to_file (CoglFramebuffer *fb,
+- char *filename_stem,
+- int x,
+- int y,
+- int width,
+- int height)
+-{
+- guint8 *data;
+- cairo_surface_t *surface;
+- static int read_count = 0;
+- char *filename = g_strdup_printf ("%s-%05d.png",
+- filename_stem,
+- read_count);
+-
+- data = g_malloc (4 * width * height);
+- cogl_framebuffer_read_pixels (fb,
+- x, y, width, height,
+- CLUTTER_CAIRO_FORMAT_ARGB32,
+- data);
+-
+- surface = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_RGB24,
+- width, height,
+- width * 4);
+-
+- cairo_surface_write_to_png (surface, filename);
+- cairo_surface_destroy (surface);
+-
+- g_free (data);
+- g_free (filename);
+-
+- read_count++;
+-}
+-
+ static ClutterActor *
+ _clutter_stage_do_pick_on_view (ClutterStage *stage,
+ gint x,
+@@ -1438,140 +1602,47 @@ _clutter_stage_do_pick_on_view (ClutterStage *stage,
+ ClutterPickMode mode,
+ ClutterStageView *view)
+ {
+- ClutterActor *actor = CLUTTER_ACTOR (stage);
++ ClutterMainContext *context = _clutter_context_get_default ();
++ int i;
+ ClutterStagePrivate *priv = stage->priv;
+ CoglFramebuffer *fb = clutter_stage_view_get_framebuffer (view);
+- cairo_rectangle_int_t view_layout;
+- ClutterMainContext *context;
+- guchar pixel[4] = { 0xff, 0xff, 0xff, 0xff };
+- CoglColor stage_pick_id;
+- gboolean dither_enabled_save;
+- ClutterActor *retval;
+- gint dirty_x;
+- gint dirty_y;
+- gint read_x;
+- gint read_y;
+- float fb_width, fb_height;
+- float fb_scale;
+- float viewport_offset_x;
+- float viewport_offset_y;
+-
+- priv = stage->priv;
+-
+- context = _clutter_context_get_default ();
+- fb_scale = clutter_stage_view_get_scale (view);
+- clutter_stage_view_get_layout (view, &view_layout);
+-
+- fb_width = view_layout.width * fb_scale;
+- fb_height = view_layout.height * fb_scale;
+- cogl_push_framebuffer (fb);
+
+- /* needed for when a context switch happens */
+- _clutter_stage_maybe_setup_viewport (stage, view);
+-
+- /* FIXME: For some reason leaving the cogl clip stack empty causes the
+- * picking to not work at all, so setting it the whole framebuffer content
+- * for now. */
+- cogl_framebuffer_push_scissor_clip (fb, 0, 0,
+- view_layout.width * fb_scale,
+- view_layout.height * fb_scale);
+-
+- _clutter_stage_window_get_dirty_pixel (priv->impl, view, &dirty_x, &dirty_y);
+-
+- if (G_LIKELY (!(clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS)))
+- {
+- CLUTTER_NOTE (PICK, "Pushing pick scissor clip x: %d, y: %d, 1x1",
+- (int) (dirty_x * fb_scale),
+- (int) (dirty_y * fb_scale));
+- cogl_framebuffer_push_scissor_clip (fb, dirty_x * fb_scale, dirty_y * fb_scale, 1, 1);
+- }
++ /* We are not reentrant right now. If this proves to be a problem then we
++ * would need to keep a stack of pick_stack's, or something... :(
++ */
++ g_assert (context->pick_mode == CLUTTER_PICK_NONE);
+
+- viewport_offset_x = x * fb_scale - dirty_x * fb_scale;
+- viewport_offset_y = y * fb_scale - dirty_y * fb_scale;
+- CLUTTER_NOTE (PICK, "Setting viewport to %f, %f, %f, %f",
+- priv->viewport[0] * fb_scale - viewport_offset_x,
+- priv->viewport[1] * fb_scale - viewport_offset_y,
+- priv->viewport[2] * fb_scale,
+- priv->viewport[3] * fb_scale);
+- cogl_framebuffer_set_viewport (fb,
+- priv->viewport[0] * fb_scale - viewport_offset_x,
+- priv->viewport[1] * fb_scale - viewport_offset_y,
+- priv->viewport[2] * fb_scale,
+- priv->viewport[3] * fb_scale);
+-
+- read_x = dirty_x * fb_scale;
+- read_y = dirty_y * fb_scale;
+-
+- CLUTTER_NOTE (PICK, "Performing pick at %i,%i on view %dx%d+%d+%d s: %f",
+- x, y,
+- view_layout.width, view_layout.height,
+- view_layout.x, view_layout.y, fb_scale);
+-
+- cogl_color_init_from_4ub (&stage_pick_id, 255, 255, 255, 255);
+- cogl_framebuffer_clear (fb, COGL_BUFFER_BIT_COLOR | COGL_BUFFER_BIT_DEPTH, &stage_pick_id);
+-
+- /* Disable dithering (if any) when doing the painting in pick mode */
+- dither_enabled_save = cogl_framebuffer_get_dither_enabled (fb);
+- cogl_framebuffer_set_dither_enabled (fb, FALSE);
+-
+- /* Render the entire scence in pick mode - just single colored silhouette's
+- * are drawn offscreen (as we never swap buffers)
+- */
+- context->pick_mode = mode;
+-
+- clutter_stage_do_paint_view (stage, view, NULL);
+- context->pick_mode = CLUTTER_PICK_NONE;
+-
+- /* Read the color of the screen co-ords pixel. RGBA_8888_PRE is used
+- even though we don't care about the alpha component because under
+- GLES this is the only format that is guaranteed to work so Cogl
+- will end up having to do a conversion if any other format is
+- used. The format is requested as pre-multiplied because Cogl
+- assumes that all pixels in the framebuffer are premultiplied so
+- it avoids a conversion. */
+- cogl_framebuffer_read_pixels (fb,
+- read_x, read_y, 1, 1,
+- COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+- pixel);
+-
+- if (G_UNLIKELY (clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS))
++ if (mode != priv->cached_pick_mode)
+ {
+- char *file_name =
+- g_strdup_printf ("pick-buffer-%s-view-x-%d",
+- _clutter_actor_get_debug_name (actor),
+- view_layout.x);
+-
+- read_pixels_to_file (fb, file_name, 0, 0, fb_width, fb_height);
++ _clutter_stage_clear_pick_stack (stage);
+
+- g_free (file_name);
++ /* We don't render to the fb, but have to set one to stop the cogl matrix
++ * operations from crashing in paint functions. Because the matrices are
++ * stored relative to the current fb.
++ */
++ cogl_push_framebuffer (fb);
++ context->pick_mode = mode;
++ clutter_stage_do_paint_view (stage, view, NULL);
++ context->pick_mode = CLUTTER_PICK_NONE;
++ priv->cached_pick_mode = mode;
++ cogl_pop_framebuffer ();
++
++ _clutter_stage_freeze_pick_stack (stage);
+ }
+
+- /* Restore whether GL_DITHER was enabled */
+- cogl_framebuffer_set_dither_enabled (fb, dither_enabled_save);
+-
+- if (G_LIKELY (!(clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS)))
+- cogl_framebuffer_pop_clip (fb);
+-
+- cogl_framebuffer_pop_clip (fb);
+-
+- _clutter_stage_dirty_viewport (stage);
+-
+- if (pixel[0] == 0xff && pixel[1] == 0xff && pixel[2] == 0xff)
+- retval = actor;
+- else
++ /* 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--)
+ {
+- guint32 id_ = _clutter_pixel_to_id (pixel);
++ const PickRecord *rec = &g_array_index (priv->pick_stack, PickRecord, i);
+
+- retval = _clutter_stage_get_actor_by_pick_id (stage, id_);
+- CLUTTER_NOTE (PICK, "Picking actor %s with id %u (pixel: 0x%x%x%x%x",
+- G_OBJECT_TYPE_NAME (retval),
+- id_,
+- pixel[0], pixel[1], pixel[2], pixel[3]);
++ if (rec->actor && pick_record_contains_pixel (stage, rec, x, y))
++ return rec->actor;
+ }
+
+- cogl_pop_framebuffer ();
+-
+- return retval;
++ return CLUTTER_ACTOR (stage);
+ }
+
+ static ClutterStageView *
+@@ -1884,7 +1955,9 @@ clutter_stage_finalize (GObject *object)
+
+ g_array_free (priv->paint_volume_stack, TRUE);
+
+- _clutter_id_pool_free (priv->pick_id_pool);
++ _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);
+@@ -2384,7 +2457,10 @@ clutter_stage_init (ClutterStage *self)
+ priv->paint_volume_stack =
+ g_array_new (FALSE, FALSE, sizeof (ClutterPaintVolume));
+
+- priv->pick_id_pool = _clutter_id_pool_new (256);
++ 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;
+ }
+
+ /**
+@@ -4208,6 +4284,12 @@ _clutter_stage_queue_actor_redraw (ClutterStage *stage,
+ CLUTTER_NOTE (CLIPPING, "stage_queue_actor_redraw (actor=%s, clip=%p): ",
+ _clutter_actor_get_debug_name (actor), clip);
+
++ /* Queuing a redraw or clip change invalidates the pick cache, unless we're
++ * in the middle of building it. So we reset the cached flag but don't
++ * completely clear the pick stack...
++ */
++ priv->cached_pick_mode = CLUTTER_PICK_NONE;
++
+ if (!priv->redraw_pending)
+ {
+ ClutterMasterClock *master_clock;
+@@ -4468,39 +4550,6 @@ _clutter_stage_get_active_framebuffer (ClutterStage *stage)
+ return stage->priv->active_framebuffer;
+ }
+
+-gint32
+-_clutter_stage_acquire_pick_id (ClutterStage *stage,
+- ClutterActor *actor)
+-{
+- ClutterStagePrivate *priv = stage->priv;
+-
+- g_assert (priv->pick_id_pool != NULL);
+-
+- return _clutter_id_pool_add (priv->pick_id_pool, actor);
+-}
+-
+-void
+-_clutter_stage_release_pick_id (ClutterStage *stage,
+- gint32 pick_id)
+-{
+- ClutterStagePrivate *priv = stage->priv;
+-
+- g_assert (priv->pick_id_pool != NULL);
+-
+- _clutter_id_pool_remove (priv->pick_id_pool, pick_id);
+-}
+-
+-ClutterActor *
+-_clutter_stage_get_actor_by_pick_id (ClutterStage *stage,
+- gint32 pick_id)
+-{
+- ClutterStagePrivate *priv = stage->priv;
+-
+- g_assert (priv->pick_id_pool != NULL);
+-
+- return _clutter_id_pool_lookup (priv->pick_id_pool, pick_id);
+-}
+-
+ void
+ _clutter_stage_add_pointer_drag_actor (ClutterStage *stage,
+ ClutterInputDevice *device,
+diff --git a/clutter/clutter/cogl/clutter-stage-cogl.c b/clutter/clutter/cogl/clutter-stage-cogl.c
+index 4f8d6a650..f957d9840 100644
+--- a/clutter/clutter/cogl/clutter-stage-cogl.c
++++ b/clutter/clutter/cogl/clutter-stage-cogl.c
+@@ -983,55 +983,6 @@ clutter_stage_cogl_redraw (ClutterStageWindow *stage_window)
+ stage_cogl->frame_count++;
+ }
+
+-static void
+-clutter_stage_cogl_get_dirty_pixel (ClutterStageWindow *stage_window,
+- ClutterStageView *view,
+- int *x,
+- int *y)
+-{
+- CoglFramebuffer *framebuffer = clutter_stage_view_get_framebuffer (view);
+- gboolean has_buffer_age =
+- cogl_is_onscreen (framebuffer) &&
+- is_buffer_age_enabled ();
+- float fb_scale;
+- gboolean scale_is_fractional;
+-
+- fb_scale = clutter_stage_view_get_scale (view);
+- if (fb_scale != floorf (fb_scale))
+- scale_is_fractional = TRUE;
+- else
+- scale_is_fractional = FALSE;
+-
+- /*
+- * Buffer damage is tracked in the framebuffer coordinate space
+- * using the damage history. When fractional scaling is used, a
+- * coordinate on the stage might not correspond to the exact position of any
+- * physical pixel, which causes issues when painting using the pick mode.
+- *
+- * For now, always use the (0, 0) pixel for picking when using fractional
+- * framebuffer scaling.
+- */
+- if (!has_buffer_age || scale_is_fractional)
+- {
+- *x = 0;
+- *y = 0;
+- }
+- else
+- {
+- ClutterStageViewCogl *view_cogl = CLUTTER_STAGE_VIEW_COGL (view);
+- ClutterStageViewCoglPrivate *view_priv =
+- clutter_stage_view_cogl_get_instance_private (view_cogl);
+- cairo_rectangle_int_t view_layout;
+- cairo_rectangle_int_t *fb_damage;
+-
+- clutter_stage_view_get_layout (view, &view_layout);
+-
+- fb_damage = &view_priv->damage_history[DAMAGE_HISTORY (view_priv->damage_index - 1)];
+- *x = fb_damage->x / fb_scale;
+- *y = fb_damage->y / fb_scale;
+- }
+-}
+-
+ static void
+ clutter_stage_window_iface_init (ClutterStageWindowInterface *iface)
+ {
+@@ -1049,7 +1000,6 @@ clutter_stage_window_iface_init (ClutterStageWindowInterface *iface)
+ iface->ignoring_redraw_clips = clutter_stage_cogl_ignoring_redraw_clips;
+ iface->get_redraw_clip_bounds = clutter_stage_cogl_get_redraw_clip_bounds;
+ iface->redraw = clutter_stage_cogl_redraw;
+- iface->get_dirty_pixel = clutter_stage_cogl_get_dirty_pixel;
+ }
+
+ static void
+diff --git a/clutter/clutter/deprecated/clutter-texture.c b/clutter/clutter/deprecated/clutter-texture.c
+index bea239f45..2c677b8a4 100644
+--- a/clutter/clutter/deprecated/clutter-texture.c
++++ b/clutter/clutter/deprecated/clutter-texture.c
+@@ -572,83 +572,6 @@ gen_texcoords_and_draw_cogl_rectangle (ClutterActor *self,
+ 0, 0, t_w, t_h);
+ }
+
+-static CoglPipeline *
+-create_pick_pipeline (ClutterActor *self)
+-{
+- ClutterTexture *texture = CLUTTER_TEXTURE (self);
+- ClutterTexturePrivate *priv = texture->priv;
+- CoglPipeline *pick_pipeline = cogl_pipeline_copy (texture_template_pipeline);
+- GError *error = NULL;
+-
+- if (!cogl_pipeline_set_layer_combine (pick_pipeline, 0,
+- "RGBA = "
+- " MODULATE (CONSTANT, TEXTURE[A])",
+- &error))
+- {
+- if (!priv->seen_create_pick_pipeline_warning)
+- g_warning ("Error setting up texture combine for shaped "
+- "texture picking: %s", error->message);
+- priv->seen_create_pick_pipeline_warning = TRUE;
+- g_error_free (error);
+- cogl_object_unref (pick_pipeline);
+- return NULL;
+- }
+-
+- cogl_pipeline_set_blend (pick_pipeline,
+- "RGBA = ADD (SRC_COLOR[RGBA], 0)",
+- NULL);
+-
+- cogl_pipeline_set_alpha_test_function (pick_pipeline,
+- COGL_PIPELINE_ALPHA_FUNC_EQUAL,
+- 1.0);
+-
+- return pick_pipeline;
+-}
+-
+-static void
+-clutter_texture_pick (ClutterActor *self,
+- const ClutterColor *color)
+-{
+- ClutterTexture *texture = CLUTTER_TEXTURE (self);
+- ClutterTexturePrivate *priv = texture->priv;
+- CoglFramebuffer *framebuffer = cogl_get_draw_framebuffer ();
+-
+- if (!clutter_actor_should_pick_paint (self))
+- return;
+-
+- if (G_LIKELY (priv->pick_with_alpha_supported) && priv->pick_with_alpha)
+- {
+- CoglColor pick_color;
+-
+- if (priv->pick_pipeline == NULL)
+- priv->pick_pipeline = create_pick_pipeline (self);
+-
+- if (priv->pick_pipeline == NULL)
+- {
+- priv->pick_with_alpha_supported = FALSE;
+- CLUTTER_ACTOR_CLASS (clutter_texture_parent_class)->pick (self,
+- color);
+- return;
+- }
+-
+- if (priv->fbo_handle != NULL)
+- update_fbo (self);
+-
+- cogl_color_init_from_4ub (&pick_color,
+- color->red,
+- color->green,
+- color->blue,
+- 0xff);
+- cogl_pipeline_set_layer_combine_constant (priv->pick_pipeline,
+- 0, &pick_color);
+- cogl_pipeline_set_layer_texture (priv->pick_pipeline, 0,
+- clutter_texture_get_cogl_texture (texture));
+- gen_texcoords_and_draw_cogl_rectangle (self, priv->pick_pipeline, framebuffer);
+- }
+- else
+- CLUTTER_ACTOR_CLASS (clutter_texture_parent_class)->pick (self, color);
+-}
+-
+ static void
+ clutter_texture_paint (ClutterActor *self)
+ {
+@@ -767,12 +690,6 @@ clutter_texture_dispose (GObject *object)
+ priv->pipeline = NULL;
+ }
+
+- if (priv->pick_pipeline != NULL)
+- {
+- cogl_object_unref (priv->pick_pipeline);
+- priv->pick_pipeline = NULL;
+- }
+-
+ G_OBJECT_CLASS (clutter_texture_parent_class)->dispose (object);
+ }
+
+@@ -944,7 +861,6 @@ clutter_texture_class_init (ClutterTextureClass *klass)
+ GParamSpec *pspec;
+
+ actor_class->paint = clutter_texture_paint;
+- actor_class->pick = clutter_texture_pick;
+ actor_class->get_paint_volume = clutter_texture_get_paint_volume;
+ actor_class->realize = clutter_texture_realize;
+ actor_class->unrealize = clutter_texture_unrealize;
+@@ -1263,11 +1179,9 @@ clutter_texture_init (ClutterTexture *self)
+ priv->repeat_y = FALSE;
+ priv->sync_actor_size = TRUE;
+ priv->fbo_handle = NULL;
+- priv->pick_pipeline = NULL;
+ priv->keep_aspect_ratio = FALSE;
+ priv->pick_with_alpha = FALSE;
+ priv->pick_with_alpha_supported = TRUE;
+- priv->seen_create_pick_pipeline_warning = FALSE;
+
+ if (G_UNLIKELY (texture_template_pipeline == NULL))
+ {
+@@ -3052,13 +2966,8 @@ clutter_texture_set_pick_with_alpha (ClutterTexture *texture,
+ if (priv->pick_with_alpha == pick_with_alpha)
+ return;
+
+- if (!pick_with_alpha && priv->pick_pipeline != NULL)
+- {
+- cogl_object_unref (priv->pick_pipeline);
+- priv->pick_pipeline = NULL;
+- }
++ g_assert (!pick_with_alpha); /* No longer supported */
+
+- /* NB: the pick pipeline is created lazily when we first pick */
+ priv->pick_with_alpha = pick_with_alpha;
+
+ /* NB: actors are expected to call clutter_actor_queue_redraw when
+diff --git a/clutter/tests/conform/actor-pick.c b/clutter/tests/conform/actor-pick.c
+index 969b4920a..2bf5954c7 100644
+--- a/clutter/tests/conform/actor-pick.c
++++ b/clutter/tests/conform/actor-pick.c
+@@ -5,7 +5,6 @@
+ #define STAGE_HEIGHT 480
+ #define ACTORS_X 12
+ #define ACTORS_Y 16
+-#define SHIFT_STEP STAGE_WIDTH / ACTORS_X
+
+ typedef struct _State State;
+
+@@ -20,84 +19,11 @@ 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 ())
+-
+-GType shift_effect_get_type (void);
+-
+-G_DEFINE_TYPE (ShiftEffect,
+- shift_effect,
+- CLUTTER_TYPE_SHADER_EFFECT);
+-
+-static void
+-shader_paint (ClutterEffect *effect,
+- ClutterEffectPaintFlags flags)
+-{
+- ClutterShaderEffect *shader = CLUTTER_SHADER_EFFECT (effect);
+- float tex_width;
+- ClutterActor *actor =
+- clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (effect));
+-
+- if (g_test_verbose ())
+- g_debug ("shader_paint");
+-
+- clutter_shader_effect_set_shader_source (shader,
+- "uniform sampler2D tex;\n"
+- "uniform float step;\n"
+- "void main (void)\n"
+- "{\n"
+- " cogl_color_out = texture2D(tex, vec2 (cogl_tex_coord_in[0].s + step,\n"
+- " cogl_tex_coord_in[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,
+- ClutterEffectPaintFlags 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 const char *test_passes[] = {
+ "No covering actor",
+ "Invisible covering actor",
+ "Clipped covering actor",
+ "Blur effect",
+- "Shift effect",
+ };
+
+ static gboolean
+@@ -165,30 +91,10 @@ on_timeout (gpointer data)
+ if (g_test_verbose ())
+ g_print ("With blur effect:\n");
+ }
+- else if (test_num == 4)
+- {
+- if (!clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL))
+- continue;
+-
+- 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++)
+ {
+- if (test_num == 4)
+- x = 1;
+- else
+- x = 0;
++ x = 0;
+
+ for (; x < ACTORS_X; x++)
+ {
+@@ -198,9 +104,6 @@ on_timeout (gpointer data)
+
+ 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,
+diff --git a/clutter/tests/conform/meson.build b/clutter/tests/conform/meson.build
+index 916f5c342..e747c9b79 100644
+--- a/clutter/tests/conform/meson.build
++++ b/clutter/tests/conform/meson.build
+@@ -43,7 +43,6 @@ clutter_conform_tests_deprecated_tests = [
+ 'behaviours',
+ 'group',
+ 'rectangle',
+- 'texture',
+ ]
+
+ clutter_conform_tests = []
+diff --git a/clutter/tests/conform/texture.c b/clutter/tests/conform/texture.c
+deleted file mode 100644
+index 392fd5c47..000000000
+--- a/clutter/tests/conform/texture.c
++++ /dev/null
+@@ -1,84 +0,0 @@
+-#define CLUTTER_DISABLE_DEPRECATION_WARNINGS
+-#include <clutter/clutter.h>
+-#include <string.h>
+-
+-static CoglHandle
+-make_texture (void)
+-{
+- guint32 *data = g_malloc (100 * 100 * 4);
+- int x;
+- int y;
+-
+- for (y = 0; y < 100; y ++)
+- for (x = 0; x < 100; x++)
+- {
+- if (x < 50 && y < 50)
+- data[y * 100 + x] = 0xff00ff00;
+- else
+- data[y * 100 + x] = 0xff00ffff;
+- }
+- return cogl_texture_new_from_data (100,
+- 100,
+- COGL_TEXTURE_NONE,
+- COGL_PIXEL_FORMAT_ARGB_8888,
+- COGL_PIXEL_FORMAT_ARGB_8888,
+- 400,
+- (guchar *)data);
+-}
+-
+-static void
+-texture_pick_with_alpha (void)
+-{
+- ClutterTexture *tex = CLUTTER_TEXTURE (clutter_texture_new ());
+- ClutterStage *stage = CLUTTER_STAGE (clutter_test_get_stage ());
+- ClutterActor *actor;
+-
+- clutter_texture_set_cogl_texture (tex, make_texture ());
+-
+- clutter_actor_add_child (CLUTTER_ACTOR (stage), CLUTTER_ACTOR (tex));
+-
+- clutter_actor_show (CLUTTER_ACTOR (stage));
+-
+- if (g_test_verbose ())
+- {
+- g_print ("\nstage = %p\n", stage);
+- g_print ("texture = %p\n\n", tex);
+- }
+-
+- clutter_texture_set_pick_with_alpha (tex, TRUE);
+- if (g_test_verbose ())
+- g_print ("Testing with pick-with-alpha enabled:\n");
+-
+- /* This should fall through and hit the stage: */
+- actor = clutter_stage_get_actor_at_pos (stage, CLUTTER_PICK_ALL, 10, 10);
+- if (g_test_verbose ())
+- g_print ("actor @ (10, 10) = %p\n", actor);
+- g_assert (actor == CLUTTER_ACTOR (stage));
+-
+- /* The rest should hit the texture */
+- actor = clutter_stage_get_actor_at_pos (stage, CLUTTER_PICK_ALL, 90, 10);
+- if (g_test_verbose ())
+- g_print ("actor @ (90, 10) = %p\n", actor);
+- g_assert (actor == CLUTTER_ACTOR (tex));
+- actor = clutter_stage_get_actor_at_pos (stage, CLUTTER_PICK_ALL, 90, 90);
+- if (g_test_verbose ())
+- g_print ("actor @ (90, 90) = %p\n", actor);
+- g_assert (actor == CLUTTER_ACTOR (tex));
+- actor = clutter_stage_get_actor_at_pos (stage, CLUTTER_PICK_ALL, 10, 90);
+- if (g_test_verbose ())
+- g_print ("actor @ (10, 90) = %p\n", actor);
+- g_assert (actor == CLUTTER_ACTOR (tex));
+-
+- clutter_texture_set_pick_with_alpha (tex, FALSE);
+- if (g_test_verbose ())
+- g_print ("Testing with pick-with-alpha disabled:\n");
+-
+- actor = clutter_stage_get_actor_at_pos (stage, CLUTTER_PICK_ALL, 10, 10);
+- if (g_test_verbose ())
+- g_print ("actor @ (10, 10) = %p\n", actor);
+- g_assert (actor == CLUTTER_ACTOR (tex));
+-}
+-
+-CLUTTER_TEST_SUITE (
+- CLUTTER_TEST_UNIT ("/texture/pick-with-alpha", texture_pick_with_alpha)
+-)
+diff --git a/src/compositor/meta-surface-actor.c b/src/compositor/meta-surface-actor.c
+index ca4ca19a9..814199145 100644
+--- a/src/compositor/meta-surface-actor.c
++++ b/src/compositor/meta-surface-actor.c
+@@ -70,38 +70,23 @@ meta_surface_actor_pick (ClutterActor *actor,
+ else
+ {
+ int n_rects;
+- float *rectangles;
+ int i;
+- CoglPipeline *pipeline;
+- CoglContext *ctx;
+- CoglFramebuffer *fb;
+- CoglColor cogl_color;
+
+ n_rects = cairo_region_num_rectangles (priv->input_region);
+- rectangles = g_alloca (sizeof (float) * 4 * n_rects);
+
+ for (i = 0; i < n_rects; i++)
+ {
+ cairo_rectangle_int_t rect;
+- int pos = i * 4;
++ ClutterActorBox box;
+
+ cairo_region_get_rectangle (priv->input_region, i, &rect);
+
+- rectangles[pos + 0] = rect.x;
+- rectangles[pos + 1] = rect.y;
+- rectangles[pos + 2] = rect.x + rect.width;
+- rectangles[pos + 3] = rect.y + rect.height;
++ box.x1 = rect.x;
++ box.y1 = rect.y;
++ box.x2 = rect.x + rect.width;
++ box.y2 = rect.y + rect.height;
++ clutter_actor_pick_box (actor, &box);
+ }
+-
+- ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
+- fb = cogl_get_draw_framebuffer ();
+-
+- cogl_color_init_from_4ub (&cogl_color, color->red, color->green, color->blue, color->alpha);
+-
+- pipeline = cogl_pipeline_new (ctx);
+- cogl_pipeline_set_color (pipeline, &cogl_color);
+- cogl_framebuffer_draw_rectangles (fb, pipeline, rectangles, n_rects);
+- cogl_object_unref (pipeline);
+ }
+
+ clutter_actor_iter_init (&iter, actor);
+--
+2.23.0
+
Modified: PKGBUILD
===================================================================
--- PKGBUILD 2019-08-29 22:42:27 UTC (rev 361399)
+++ PKGBUILD 2019-08-29 22:46:42 UTC (rev 361400)
@@ -19,10 +19,12 @@
_commit=b7f158811934d8e4d9dd0be28ad8e1746ceac46c # gnome-3-32
source=("git+https://gitlab.gnome.org/GNOME/mutter.git#commit=$_commit"
0001-Remove-GLX-threaded-swap-wait.patch
- 0001-Geometric-OpenGL-less-picking.patch)
+ 0001-Add-point-polygon-testing-API.patch
+ 0002-Geometric-OpenGL-less-picking.patch)
sha256sums=('SKIP'
'92c0dd3a1df455722c7bfb205eab7c72ee21055d64f397bea5a8332431f3fee7'
- '997cbf2e5cc0252914ea59f1c5388d176b8583e6785ba12fdc729d13971b4e3e')
+ '12c69bdc0836b47d5d6eba0f5b0118035aa08330388a05fcee367025adb31f21'
+ 'a6643593071ab171866dd6fdb30e65826a753a134832a8e1ec05b2b515d599e5')
pkgver() {
cd $pkgname
@@ -48,7 +50,8 @@
git cherry-pick -n a20a0d7a4563366d2cd29c32a1b95a59121e7bf5 # !283
git apply -3 ../0001-Remove-GLX-threaded-swap-wait.patch # !602
git cherry-pick -n a2507cd51a248e2ee50eb64479f47e5da2564535 # !189
- git apply -3 ../0001-Geometric-OpenGL-less-picking.patch # !189
+ git apply -3 ../0001-Add-point-polygon-testing-API.patch # !189
+ git apply -3 ../0002-Geometric-OpenGL-less-picking.patch # !189
# fix background corruption on nvidia
git cherry-pick -n a5265365dd268e15a461a58000a10b122d0bccba # !600
More information about the arch-commits
mailing list