Blame SOURCES/geometric-picking.patch

0e33ea
From ac946bf95ce3e4dc900f72dcb4189dd49bdb3155 Mon Sep 17 00:00:00 2001
0e33ea
From: Daniel van Vugt <daniel.van.vugt@canonical.com>
0e33ea
Date: Thu, 18 Jul 2019 16:56:41 +0800
0e33ea
Subject: [PATCH 1/4] clutter/point: Add ClutterPoint quarilateral testing API
0e33ea
0e33ea
Add a function to check whether a point is inside a quadrilateral
0e33ea
by checking the cross product of vectors with the quadrilateral
0e33ea
points, and the point being checked.
0e33ea
0e33ea
If the passed quadrilateral is zero-sized, no point is ever reported
0e33ea
to be inside it.
0e33ea
0e33ea
This will be used by the next commit when comparing the transformed
0e33ea
actor vertices.
0e33ea
0e33ea
[feaneron: add a commit message and remove unecessary code]
0e33ea
0e33ea
https://gitlab.gnome.org/GNOME/mutter/merge_requests/189
0e33ea
---
0e33ea
 clutter/clutter/clutter-base-types.c | 62 ++++++++++++++++++++++++++++
0e33ea
 clutter/clutter/clutter-types.h      |  3 ++
0e33ea
 2 files changed, 65 insertions(+)
0e33ea
0e33ea
diff --git a/clutter/clutter/clutter-base-types.c b/clutter/clutter/clutter-base-types.c
0e33ea
index aeb25c90ef..c84f9aa64b 100644
0e33ea
--- a/clutter/clutter/clutter-base-types.c
0e33ea
+++ b/clutter/clutter/clutter-base-types.c
0e33ea
@@ -570,6 +570,68 @@ G_DEFINE_BOXED_TYPE_WITH_CODE (ClutterPoint, clutter_point,
0e33ea
                                clutter_point_free,
0e33ea
                                CLUTTER_REGISTER_INTERVAL_PROGRESS (clutter_point_progress))
0e33ea
 
0e33ea
+static int
0e33ea
+clutter_point_compare_line (const ClutterPoint *p,
0e33ea
+                            const ClutterPoint *a,
0e33ea
+                            const ClutterPoint *b)
0e33ea
+{
0e33ea
+  float x1 = b->x - a->x;
0e33ea
+  float y1 = b->y - a->y;
0e33ea
+  float x2 = p->x - a->x;
0e33ea
+  float y2 = p->y - a->y;
0e33ea
+  float cross_z = x1 * y2 - y1 * x2;
0e33ea
+
0e33ea
+  if (cross_z > 0.f)
0e33ea
+    return 1;
0e33ea
+  else if (cross_z < 0.f)
0e33ea
+    return -1;
0e33ea
+  else
0e33ea
+    return 0;
0e33ea
+}
0e33ea
+
0e33ea
+/**
0e33ea
+ * clutter_point_inside_quadrilateral:
0e33ea
+ * @point: a #ClutterPoint to test
0e33ea
+ * @vertices: array of vertices of the quadrilateral, in clockwise order,
0e33ea
+ *            from top-left to bottom-left
0e33ea
+ *
0e33ea
+ * Determines whether a point is inside the convex quadrilateral provided,
0e33ea
+ * and not on any of its edges or vertices.
0e33ea
+ *
0e33ea
+ * Returns: %TRUE if @point is inside the quadrilateral
0e33ea
+ */
0e33ea
+gboolean
0e33ea
+clutter_point_inside_quadrilateral (const ClutterPoint *point,
0e33ea
+                                    const ClutterPoint *vertices)
0e33ea
+{
0e33ea
+  unsigned int i;
0e33ea
+  int first_side;
0e33ea
+
0e33ea
+  first_side = 0;
0e33ea
+
0e33ea
+  for (i = 0; i < 4; i++)
0e33ea
+    {
0e33ea
+      int side;
0e33ea
+
0e33ea
+      side = clutter_point_compare_line (point,
0e33ea
+                                         &vertices[i],
0e33ea
+                                         &vertices[(i + 1) % 4]);
0e33ea
+
0e33ea
+      if (side)
0e33ea
+        {
0e33ea
+          if (first_side == 0)
0e33ea
+            first_side = side;
0e33ea
+          else if (side != first_side)
0e33ea
+            return FALSE;
0e33ea
+        }
0e33ea
+    }
0e33ea
+
0e33ea
+  if (first_side == 0)
0e33ea
+    return FALSE;
0e33ea
+
0e33ea
+  return TRUE;
0e33ea
+}
0e33ea
+
0e33ea
 
0e33ea
 
0e33ea
 /*
0e33ea
diff --git a/clutter/clutter/clutter-types.h b/clutter/clutter/clutter-types.h
0e33ea
index 0f0fb1c2ac..0508028273 100644
0e33ea
--- a/clutter/clutter/clutter-types.h
0e33ea
+++ b/clutter/clutter/clutter-types.h
0e33ea
@@ -200,6 +200,9 @@ float                   clutter_point_distance  (const ClutterPoint *a,
0e33ea
                                                  const ClutterPoint *b,
0e33ea
                                                  float              *x_distance,
0e33ea
                                                  float              *y_distance);
0e33ea
+CLUTTER_EXPORT
0e33ea
+gboolean clutter_point_inside_quadrilateral     (const ClutterPoint *point,
0e33ea
+                                                 const ClutterPoint *vertices);
0e33ea
 
0e33ea
 /**
0e33ea
  * ClutterSize:
0e33ea
-- 
0e33ea
2.29.2
0e33ea
0e33ea
0e33ea
From 8abac81711cfef8317bb675349d6b5b0a79eb05d Mon Sep 17 00:00:00 2001
0e33ea
From: Daniel van Vugt <daniel.van.vugt@canonical.com>
0e33ea
Date: Thu, 2 Aug 2018 19:03:30 +0800
0e33ea
Subject: [PATCH 2/4] clutter: Introduce geometric picking
0e33ea
0e33ea
Currently, Clutter does picking by drawing with Cogl and reading
0e33ea
the pixel that's beneath the given point. Since Cogl has a journal
0e33ea
that records drawing operations, and has optimizations to read a
0e33ea
single pixel from a list of rectangle, it would be expected that
0e33ea
we would hit this fast path and not flush the journal while picking.
0e33ea
0e33ea
However, that's not the case: dithering, clipping with scissors, etc,
0e33ea
can all flush the journal, issuing commands to the GPU and making
0e33ea
picking slow. On NVidia-based systems, this glReadPixels() call is
0e33ea
extremely costly.
0e33ea
0e33ea
Introduce geometric picking, and avoid using the Cogl journal entirely.
0e33ea
Do this by introducing a stack of actors in ClutterStage. This stack
0e33ea
is cached, but for now, don't use the cache as much as possible.
0e33ea
0e33ea
The picking routines are still tied to painting.
0e33ea
0e33ea
When projecting the actor vertexes, do it manually and take the modelview
0e33ea
matrix of the framebuffer into account as well.
0e33ea
0e33ea
CPU usage on an Intel i7-7700, tested with two different GPUs/drivers:
0e33ea
0e33ea
  |         |     Intel | Nvidia |
0e33ea
  | ------: | --------: | -----: |
0e33ea
  | Moving the mouse:            |
0e33ea
  | Before  |       10% |    10% |
0e33ea
  | After   |        6% |     6% |
0e33ea
  | Moving a window:             |
0e33ea
  | Before  |       23% |    81% |
0e33ea
  | After   |       19% |    40% |
0e33ea
0e33ea
Closes: https://gitlab.gnome.org/GNOME/mutter/issues/154,
0e33ea
        https://gitlab.gnome.org/GNOME/mutter/issues/691
0e33ea
0e33ea
Helps significantly with: https://gitlab.gnome.org/GNOME/mutter/issues/283,
0e33ea
                          https://gitlab.gnome.org/GNOME/mutter/issues/590,
0e33ea
                          https://gitlab.gnome.org/GNOME/mutter/issues/700
0e33ea
0e33ea
v2: Fix code style issues
0e33ea
    Simplify quadrilateral checks
0e33ea
    Remove the 0.5f hack
0e33ea
    Differentiate axis-aligned rectangles
0e33ea
0e33ea
https://gitlab.gnome.org/GNOME/mutter/merge_requests/189
0e33ea
---
0e33ea
 clutter/clutter/clutter-actor-private.h      |   2 -
0e33ea
 clutter/clutter/clutter-actor.c              | 232 ++++++----
0e33ea
 clutter/clutter/clutter-actor.h              |   4 +
0e33ea
 clutter/clutter/clutter-debug.h              |   1 -
0e33ea
 clutter/clutter/clutter-main.c               | 120 -----
0e33ea
 clutter/clutter/clutter-private.h            |   5 -
0e33ea
 clutter/clutter/clutter-stage-private.h      |  16 +-
0e33ea
 clutter/clutter/clutter-stage-window.c       |  18 -
0e33ea
 clutter/clutter/clutter-stage-window.h       |   8 -
0e33ea
 clutter/clutter/clutter-stage.c              | 459 +++++++++++--------
0e33ea
 clutter/clutter/cogl/clutter-stage-cogl.c    |  52 ---
0e33ea
 clutter/clutter/deprecated/clutter-texture.c |  93 +---
0e33ea
 clutter/tests/conform/actor-pick.c           |  99 +---
0e33ea
 clutter/tests/conform/meson.build            |   1 -
0e33ea
 clutter/tests/conform/texture.c              |  84 ----
0e33ea
 src/compositor/meta-surface-actor.c          |  27 +-
0e33ea
 16 files changed, 439 insertions(+), 782 deletions(-)
0e33ea
 delete mode 100644 clutter/tests/conform/texture.c
0e33ea
0e33ea
diff --git a/clutter/clutter/clutter-actor-private.h b/clutter/clutter/clutter-actor-private.h
0e33ea
index c44f6342fd..9bf1a30493 100644
0e33ea
--- a/clutter/clutter/clutter-actor-private.h
0e33ea
+++ b/clutter/clutter/clutter-actor-private.h
0e33ea
@@ -297,8 +297,6 @@ const gchar *                   _clutter_actor_get_debug_name
0e33ea
 void                            _clutter_actor_push_clone_paint                         (void);
0e33ea
 void                            _clutter_actor_pop_clone_paint                          (void);
0e33ea
 
0e33ea
-guint32                         _clutter_actor_get_pick_id                              (ClutterActor *self);
0e33ea
-
0e33ea
 void                            _clutter_actor_shader_pre_paint                         (ClutterActor *actor,
0e33ea
                                                                                          gboolean      repeat);
0e33ea
 void                            _clutter_actor_shader_post_paint                        (ClutterActor *actor);
0e33ea
diff --git a/clutter/clutter/clutter-actor.c b/clutter/clutter/clutter-actor.c
0e33ea
index 43093fe79d..01ffa51caa 100644
0e33ea
--- a/clutter/clutter/clutter-actor.c
0e33ea
+++ b/clutter/clutter/clutter-actor.c
0e33ea
@@ -730,8 +730,6 @@ struct _ClutterActorPrivate
0e33ea
 
0e33ea
   gchar *name; /* a non-unique name, used for debugging */
0e33ea
 
0e33ea
-  gint32 pick_id; /* per-stage unique id, used for picking */
0e33ea
-
0e33ea
   /* a back-pointer to the Pango context that we can use
0e33ea
    * to create pre-configured PangoLayout
0e33ea
    */
0e33ea
@@ -1281,6 +1279,105 @@ clutter_actor_verify_map_state (ClutterActor *self)
0e33ea
 
0e33ea
 #endif /* CLUTTER_ENABLE_DEBUG */
0e33ea
 
0e33ea
+static gboolean
0e33ea
+_clutter_actor_transform_local_box_to_stage (ClutterActor          *self,
0e33ea
+                                             ClutterStage          *stage,
0e33ea
+                                             const ClutterActorBox *box,
0e33ea
+                                             ClutterPoint           vertices[4])
0e33ea
+{
0e33ea
+  CoglFramebuffer *fb = cogl_get_draw_framebuffer ();
0e33ea
+  CoglMatrix stage_transform, inv_stage_transform;
0e33ea
+  CoglMatrix modelview, transform_to_stage;
0e33ea
+  int v;
0e33ea
+
0e33ea
+  clutter_actor_get_transform (CLUTTER_ACTOR (stage), &stage_transform);
0e33ea
+  if (!cogl_matrix_get_inverse (&stage_transform, &inv_stage_transform))
0e33ea
+    return FALSE;
0e33ea
+  cogl_framebuffer_get_modelview_matrix (fb, &modelview);
0e33ea
+  cogl_matrix_multiply (&transform_to_stage, &inv_stage_transform, &modelview);
0e33ea
+
0e33ea
+  vertices[0].x = box->x1;
0e33ea
+  vertices[0].y = box->y1;
0e33ea
+
0e33ea
+  vertices[1].x = box->x2;
0e33ea
+  vertices[1].y = box->y1;
0e33ea
+
0e33ea
+  vertices[2].x = box->x2;
0e33ea
+  vertices[2].y = box->y2;
0e33ea
+
0e33ea
+  vertices[3].x = box->x1;
0e33ea
+  vertices[3].y = box->y2;
0e33ea
+
0e33ea
+  for (v = 0; v < 4; v++)
0e33ea
+    {
0e33ea
+      float z = 0.f;
0e33ea
+      float w = 1.f;
0e33ea
+
0e33ea
+      cogl_matrix_transform_point (&transform_to_stage,
0e33ea
+                                   &vertices[v].x,
0e33ea
+                                   &vertices[v].y,
0e33ea
+                                   &z,
0e33ea
+                                   &w);
0e33ea
+    }
0e33ea
+
0e33ea
+  return TRUE;
0e33ea
+}
0e33ea
+
0e33ea
+/**
0e33ea
+ * clutter_actor_pick_box:
0e33ea
+ * @self: The #ClutterActor being "pick" painted.
0e33ea
+ * @box: A rectangle in the actor's own local coordinates.
0e33ea
+ *
0e33ea
+ * Logs (does a virtual paint of) a rectangle for picking. Note that @box is
0e33ea
+ * in the actor's own local coordinates, so is usually {0,0,width,height}
0e33ea
+ * to include the whole actor. That is unless the actor has a shaped input
0e33ea
+ * region in which case you may wish to log the (multiple) smaller rectangles
0e33ea
+ * that make up the input region.
0e33ea
+ */
0e33ea
+void
0e33ea
+clutter_actor_pick_box (ClutterActor          *self,
0e33ea
+                        const ClutterActorBox *box)
0e33ea
+{
0e33ea
+  ClutterStage *stage;
0e33ea
+  ClutterPoint vertices[4];
0e33ea
+
0e33ea
+  g_return_if_fail (CLUTTER_IS_ACTOR (self));
0e33ea
+  g_return_if_fail (box != NULL);
0e33ea
+
0e33ea
+  if (box->x1 >= box->x2 || box->y1 >= box->y2)
0e33ea
+    return;
0e33ea
+
0e33ea
+  stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (self));
0e33ea
+
0e33ea
+  if (_clutter_actor_transform_local_box_to_stage (self, stage, box, vertices))
0e33ea
+    clutter_stage_log_pick (stage, vertices, self);
0e33ea
+}
0e33ea
+
0e33ea
+static gboolean
0e33ea
+_clutter_actor_push_pick_clip (ClutterActor          *self,
0e33ea
+                               const ClutterActorBox *clip)
0e33ea
+{
0e33ea
+  ClutterStage *stage;
0e33ea
+  ClutterPoint vertices[4];
0e33ea
+
0e33ea
+  stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (self));
0e33ea
+
0e33ea
+  if (!_clutter_actor_transform_local_box_to_stage (self, stage, clip, vertices))
0e33ea
+    return FALSE;
0e33ea
+
0e33ea
+  clutter_stage_push_pick_clip (stage, vertices);
0e33ea
+  return TRUE;
0e33ea
+}
0e33ea
+
0e33ea
+static void
0e33ea
+_clutter_actor_pop_pick_clip (ClutterActor *self)
0e33ea
+{
0e33ea
+  ClutterActor *stage;
0e33ea
+
0e33ea
+  stage = _clutter_actor_get_stage_internal (self);
0e33ea
+  clutter_stage_pop_pick_clip (CLUTTER_STAGE (stage));
0e33ea
+}
0e33ea
+
0e33ea
 static void
0e33ea
 clutter_actor_set_mapped (ClutterActor *self,
0e33ea
                           gboolean      mapped)
0e33ea
@@ -1509,8 +1606,7 @@ clutter_actor_update_map_state (ClutterActor  *self,
0e33ea
 static void
0e33ea
 clutter_actor_real_map (ClutterActor *self)
0e33ea
 {
0e33ea
-  ClutterActorPrivate *priv = self->priv;
0e33ea
-  ClutterActor *stage, *iter;
0e33ea
+  ClutterActor *iter;
0e33ea
 
0e33ea
   g_assert (!CLUTTER_ACTOR_IS_MAPPED (self));
0e33ea
 
0e33ea
@@ -1521,13 +1617,6 @@ clutter_actor_real_map (ClutterActor *self)
0e33ea
 
0e33ea
   self->priv->needs_paint_volume_update = TRUE;
0e33ea
 
0e33ea
-  stage = _clutter_actor_get_stage_internal (self);
0e33ea
-  priv->pick_id = _clutter_stage_acquire_pick_id (CLUTTER_STAGE (stage), self);
0e33ea
-
0e33ea
-  CLUTTER_NOTE (ACTOR, "Pick id '%d' for actor '%s'",
0e33ea
-                priv->pick_id,
0e33ea
-                _clutter_actor_get_debug_name (self));
0e33ea
-
0e33ea
   clutter_actor_ensure_resource_scale (self);
0e33ea
 
0e33ea
   /* notify on parent mapped before potentially mapping
0e33ea
@@ -1632,11 +1721,6 @@ clutter_actor_real_unmap (ClutterActor *self)
0e33ea
 
0e33ea
       stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (self));
0e33ea
 
0e33ea
-      if (stage != NULL)
0e33ea
-        _clutter_stage_release_pick_id (stage, priv->pick_id);
0e33ea
-
0e33ea
-      priv->pick_id = -1;
0e33ea
-
0e33ea
       if (stage != NULL &&
0e33ea
           clutter_stage_get_key_focus (stage) == self)
0e33ea
         {
0e33ea
@@ -2255,46 +2339,16 @@ static void
0e33ea
 clutter_actor_real_pick (ClutterActor       *self,
0e33ea
 			 const ClutterColor *color)
0e33ea
 {
0e33ea
-  CoglFramebuffer *framebuffer = cogl_get_draw_framebuffer ();
0e33ea
-
0e33ea
-  /* the default implementation is just to paint a rectangle
0e33ea
-   * with the same size of the actor using the passed color
0e33ea
-   */
0e33ea
   if (clutter_actor_should_pick_paint (self))
0e33ea
     {
0e33ea
-      static CoglPipeline *default_pick_pipeline = NULL;
0e33ea
-      ClutterActorBox box = { 0, };
0e33ea
-      CoglPipeline *pick_pipeline;
0e33ea
-      float width, height;
0e33ea
-
0e33ea
-      if (G_UNLIKELY (default_pick_pipeline == NULL))
0e33ea
-        {
0e33ea
-          CoglContext *ctx =
0e33ea
-            clutter_backend_get_cogl_context (clutter_get_default_backend ());
0e33ea
-
0e33ea
-          default_pick_pipeline = cogl_pipeline_new (ctx);
0e33ea
-        }
0e33ea
-
0e33ea
-      g_assert (default_pick_pipeline != NULL);
0e33ea
-      pick_pipeline = cogl_pipeline_copy (default_pick_pipeline);
0e33ea
+      ClutterActorBox box = {
0e33ea
+        .x1 = 0,
0e33ea
+        .y1 = 0,
0e33ea
+        .x2 = clutter_actor_get_width (self),
0e33ea
+        .y2 = clutter_actor_get_height (self),
0e33ea
+      };
0e33ea
 
0e33ea
-      clutter_actor_get_allocation_box (self, &box);
0e33ea
-
0e33ea
-      width = box.x2 - box.x1;
0e33ea
-      height = box.y2 - box.y1;
0e33ea
-
0e33ea
-      cogl_pipeline_set_color4ub (pick_pipeline,
0e33ea
-                                  color->red,
0e33ea
-                                  color->green,
0e33ea
-                                  color->blue,
0e33ea
-                                  color->alpha);
0e33ea
-
0e33ea
-      cogl_framebuffer_draw_rectangle (framebuffer,
0e33ea
-                                       pick_pipeline,
0e33ea
-                                       0, 0,
0e33ea
-                                       width, height);
0e33ea
-
0e33ea
-      cogl_object_unref (pick_pipeline);
0e33ea
+      clutter_actor_pick_box (self, &box);
0e33ea
     }
0e33ea
 
0e33ea
   /* XXX - this thoroughly sucks, but we need to maintain compatibility
0e33ea
@@ -3585,15 +3639,6 @@ _clutter_actor_update_last_paint_volume (ClutterActor *self)
0e33ea
   priv->last_paint_volume_valid = TRUE;
0e33ea
 }
0e33ea
 
0e33ea
-guint32
0e33ea
-_clutter_actor_get_pick_id (ClutterActor *self)
0e33ea
-{
0e33ea
-  if (self->priv->pick_id < 0)
0e33ea
-    return 0;
0e33ea
-
0e33ea
-  return self->priv->pick_id;
0e33ea
-}
0e33ea
-
0e33ea
 /* This is the same as clutter_actor_add_effect except that it doesn't
0e33ea
    queue a redraw and it doesn't notify on the effect property */
0e33ea
 static void
0e33ea
@@ -3826,6 +3871,7 @@ clutter_actor_paint (ClutterActor *self)
0e33ea
   ClutterActorPrivate *priv;
0e33ea
   ClutterPickMode pick_mode;
0e33ea
   gboolean culling_inhibited;
0e33ea
+  ClutterActorBox clip;
0e33ea
   gboolean clip_set = FALSE;
0e33ea
   ClutterStage *stage;
0e33ea
 
0e33ea
@@ -3919,24 +3965,38 @@ clutter_actor_paint (ClutterActor *self)
0e33ea
 
0e33ea
   if (priv->has_clip)
0e33ea
     {
0e33ea
-      CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage);
0e33ea
-      cogl_framebuffer_push_rectangle_clip (fb,
0e33ea
-                                            priv->clip.origin.x,
0e33ea
-                                            priv->clip.origin.y,
0e33ea
-                                            priv->clip.origin.x + priv->clip.size.width,
0e33ea
-                                            priv->clip.origin.y + priv->clip.size.height);
0e33ea
+      clip.x1 = priv->clip.origin.x;
0e33ea
+      clip.y1 = priv->clip.origin.y;
0e33ea
+      clip.x2 = priv->clip.origin.x + priv->clip.size.width;
0e33ea
+      clip.y2 = priv->clip.origin.y + priv->clip.size.height;
0e33ea
       clip_set = TRUE;
0e33ea
     }
0e33ea
   else if (priv->clip_to_allocation)
0e33ea
     {
0e33ea
-      CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage);
0e33ea
-      gfloat width, height;
0e33ea
+      clip.x1 = 0.f;
0e33ea
+      clip.y1 = 0.f;
0e33ea
+      clip.x2 = priv->allocation.x2 - priv->allocation.x1;
0e33ea
+      clip.y2 = priv->allocation.y2 - priv->allocation.y1;
0e33ea
+      clip_set = TRUE;
0e33ea
+    }
0e33ea
 
0e33ea
-      width  = priv->allocation.x2 - priv->allocation.x1;
0e33ea
-      height = priv->allocation.y2 - priv->allocation.y1;
0e33ea
+  if (clip_set)
0e33ea
+    {
0e33ea
+      if (pick_mode == CLUTTER_PICK_NONE)
0e33ea
+        {
0e33ea
+          CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage);
0e33ea
 
0e33ea
-      cogl_framebuffer_push_rectangle_clip (fb, 0, 0, width, height);
0e33ea
-      clip_set = TRUE;
0e33ea
+          cogl_framebuffer_push_rectangle_clip (fb,
0e33ea
+                                                clip.x1,
0e33ea
+                                                clip.y1,
0e33ea
+                                                clip.x2,
0e33ea
+                                                clip.y2);
0e33ea
+        }
0e33ea
+      else
0e33ea
+        {
0e33ea
+          if (!_clutter_actor_push_pick_clip (self, &clip))
0e33ea
+            clip_set = FALSE;
0e33ea
+        }
0e33ea
     }
0e33ea
 
0e33ea
   if (pick_mode == CLUTTER_PICK_NONE)
0e33ea
@@ -4020,9 +4080,16 @@ clutter_actor_paint (ClutterActor *self)
0e33ea
 done:
0e33ea
   if (clip_set)
0e33ea
     {
0e33ea
-      CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage);
0e33ea
+      if (pick_mode == CLUTTER_PICK_NONE)
0e33ea
+        {
0e33ea
+          CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage);
0e33ea
 
0e33ea
-      cogl_framebuffer_pop_clip (fb);
0e33ea
+          cogl_framebuffer_pop_clip (fb);
0e33ea
+        }
0e33ea
+      else
0e33ea
+        {
0e33ea
+          _clutter_actor_pop_pick_clip (self);
0e33ea
+        }
0e33ea
     }
0e33ea
 
0e33ea
   cogl_pop_matrix ();
0e33ea
@@ -4093,11 +4160,12 @@ clutter_actor_continue_paint (ClutterActor *self)
0e33ea
         {
0e33ea
           ClutterColor col = { 0, };
0e33ea
 
0e33ea
-          _clutter_id_to_color (_clutter_actor_get_pick_id (self), &col);
0e33ea
-
0e33ea
-          /* Actor will then paint silhouette of itself in supplied
0e33ea
-           * color.  See clutter_stage_get_actor_at_pos() for where
0e33ea
-           * picking is enabled.
0e33ea
+          /* The actor will log a silhouette of itself to the stage pick log.
0e33ea
+           * Note that the picking color is no longer used as the "log" instead
0e33ea
+           * keeps a weak pointer to the actor itself. But we keep the color
0e33ea
+           * parameter for now so as to maintain ABI compatibility. The color
0e33ea
+           * parameter can be removed when someone feels like breaking the ABI
0e33ea
+           * along with gnome-shell.
0e33ea
            *
0e33ea
            * XXX:2.0 - Call the pick() virtual directly
0e33ea
            */
0e33ea
@@ -8603,8 +8671,6 @@ clutter_actor_init (ClutterActor *self)
0e33ea
 
0e33ea
   self->priv = priv = clutter_actor_get_instance_private (self);
0e33ea
 
0e33ea
-  priv->pick_id = -1;
0e33ea
-
0e33ea
   priv->opacity = 0xff;
0e33ea
   priv->show_on_set_parent = TRUE;
0e33ea
   priv->resource_scale = -1.0f;
0e33ea
diff --git a/clutter/clutter/clutter-actor.h b/clutter/clutter/clutter-actor.h
0e33ea
index 3e7a59ac0c..16b438ba64 100644
0e33ea
--- a/clutter/clutter/clutter-actor.h
0e33ea
+++ b/clutter/clutter/clutter-actor.h
0e33ea
@@ -910,6 +910,10 @@ void                            clutter_actor_bind_model_with_properties
0e33ea
                                                                                  const char                 *first_model_property,
0e33ea
                                                                                  ...);
0e33ea
 
0e33ea
+CLUTTER_EXPORT
0e33ea
+void clutter_actor_pick_box (ClutterActor          *self,
0e33ea
+                             const ClutterActorBox *box);
0e33ea
+
0e33ea
 G_END_DECLS
0e33ea
 
0e33ea
 #endif /* __CLUTTER_ACTOR_H__ */
0e33ea
diff --git a/clutter/clutter/clutter-debug.h b/clutter/clutter/clutter-debug.h
0e33ea
index 2462385f65..7d170d2d54 100644
0e33ea
--- a/clutter/clutter/clutter-debug.h
0e33ea
+++ b/clutter/clutter/clutter-debug.h
0e33ea
@@ -30,7 +30,6 @@ typedef enum
0e33ea
 typedef enum
0e33ea
 {
0e33ea
   CLUTTER_DEBUG_NOP_PICKING         = 1 << 0,
0e33ea
-  CLUTTER_DEBUG_DUMP_PICK_BUFFERS   = 1 << 1
0e33ea
 } ClutterPickDebugFlag;
0e33ea
 
0e33ea
 typedef enum
0e33ea
diff --git a/clutter/clutter/clutter-main.c b/clutter/clutter/clutter-main.c
0e33ea
index 645c8bceb6..11c221a65b 100644
0e33ea
--- a/clutter/clutter/clutter-main.c
0e33ea
+++ b/clutter/clutter/clutter-main.c
0e33ea
@@ -131,7 +131,6 @@ static const GDebugKey clutter_debug_keys[] = {
0e33ea
 
0e33ea
 static const GDebugKey clutter_pick_debug_keys[] = {
0e33ea
   { "nop-picking", CLUTTER_DEBUG_NOP_PICKING },
0e33ea
-  { "dump-pick-buffers", CLUTTER_DEBUG_DUMP_PICK_BUFFERS },
0e33ea
 };
0e33ea
 
0e33ea
 static const GDebugKey clutter_paint_debug_keys[] = {
0e33ea
@@ -533,125 +532,6 @@ clutter_get_motion_events_enabled (void)
0e33ea
   return _clutter_context_get_motion_events_enabled ();
0e33ea
 }
0e33ea
 
0e33ea
-void
0e33ea
-_clutter_id_to_color (guint         id_,
0e33ea
-                      ClutterColor *col)
0e33ea
-{
0e33ea
-  ClutterMainContext *ctx;
0e33ea
-  gint red, green, blue;
0e33ea
-
0e33ea
-  ctx = _clutter_context_get_default ();
0e33ea
-
0e33ea
-  if (ctx->fb_g_mask == 0)
0e33ea
-    {
0e33ea
-      /* Figure out framebuffer masks used for pick */
0e33ea
-      cogl_get_bitmasks (&ctx->fb_r_mask,
0e33ea
-			 &ctx->fb_g_mask,
0e33ea
-			 &ctx->fb_b_mask, NULL);
0e33ea
-
0e33ea
-      ctx->fb_r_mask_used = ctx->fb_r_mask;
0e33ea
-      ctx->fb_g_mask_used = ctx->fb_g_mask;
0e33ea
-      ctx->fb_b_mask_used = ctx->fb_b_mask;
0e33ea
-
0e33ea
-      /* XXX - describe what "fuzzy picking" is */
0e33ea
-      if (clutter_use_fuzzy_picking)
0e33ea
-	{
0e33ea
-	  ctx->fb_r_mask_used--;
0e33ea
-	  ctx->fb_g_mask_used--;
0e33ea
-	  ctx->fb_b_mask_used--;
0e33ea
-	}
0e33ea
-    }
0e33ea
-
0e33ea
-  /* compute the numbers we'll store in the components */
0e33ea
-  red   = (id_ >> (ctx->fb_g_mask_used+ctx->fb_b_mask_used))
0e33ea
-        & (0xff >> (8-ctx->fb_r_mask_used));
0e33ea
-  green = (id_ >> ctx->fb_b_mask_used)
0e33ea
-        & (0xff >> (8-ctx->fb_g_mask_used));
0e33ea
-  blue  = (id_)
0e33ea
-        & (0xff >> (8-ctx->fb_b_mask_used));
0e33ea
-
0e33ea
-  /* shift left bits a bit and add one, this circumvents
0e33ea
-   * at least some potential rounding errors in GL/GLES
0e33ea
-   * driver / hw implementation.
0e33ea
-   */
0e33ea
-  if (ctx->fb_r_mask_used != ctx->fb_r_mask)
0e33ea
-    red = red * 2;
0e33ea
-  if (ctx->fb_g_mask_used != ctx->fb_g_mask)
0e33ea
-    green = green * 2;
0e33ea
-  if (ctx->fb_b_mask_used != ctx->fb_b_mask)
0e33ea
-    blue  = blue  * 2;
0e33ea
-
0e33ea
-  /* shift up to be full 8bit values */
0e33ea
-  red   = (red   << (8 - ctx->fb_r_mask)) | (0x7f >> (ctx->fb_r_mask_used));
0e33ea
-  green = (green << (8 - ctx->fb_g_mask)) | (0x7f >> (ctx->fb_g_mask_used));
0e33ea
-  blue  = (blue  << (8 - ctx->fb_b_mask)) | (0x7f >> (ctx->fb_b_mask_used));
0e33ea
-
0e33ea
-  col->red   = red;
0e33ea
-  col->green = green;
0e33ea
-  col->blue  = blue;
0e33ea
-  col->alpha = 0xff;
0e33ea
-
0e33ea
-  /* XXX: We rotate the nibbles of the colors here so that there is a
0e33ea
-   * visible variation between colors of sequential actor identifiers;
0e33ea
-   * otherwise pick buffers dumped to an image will pretty much just look
0e33ea
-   * black.
0e33ea
-   */
0e33ea
-  if (G_UNLIKELY (clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS))
0e33ea
-    {
0e33ea
-      col->red   = (col->red << 4)   | (col->red >> 4);
0e33ea
-      col->green = (col->green << 4) | (col->green >> 4);
0e33ea
-      col->blue  = (col->blue << 4)  | (col->blue >> 4);
0e33ea
-    }
0e33ea
-}
0e33ea
-
0e33ea
-guint
0e33ea
-_clutter_pixel_to_id (guchar pixel[4])
0e33ea
-{
0e33ea
-  ClutterMainContext *ctx;
0e33ea
-  gint red, green, blue;
0e33ea
-  guint retval;
0e33ea
-
0e33ea
-  ctx = _clutter_context_get_default ();
0e33ea
-
0e33ea
-  /* reduce the pixel components to the number of bits actually used of the
0e33ea
-   * 8bits.
0e33ea
-   */
0e33ea
-  if (G_UNLIKELY (clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS))
0e33ea
-    {
0e33ea
-      guchar tmp;
0e33ea
-
0e33ea
-      /* XXX: In _clutter_id_to_color we rotated the nibbles of the colors so
0e33ea
-       * that there is a visible variation between colors of sequential actor
0e33ea
-       * identifiers (otherwise pick buffers dumped to an image will pretty
0e33ea
-       * much just look black.) Here we reverse that rotation.
0e33ea
-       */
0e33ea
-      tmp = ((pixel[0] << 4) | (pixel[0] >> 4));
0e33ea
-      red = tmp >> (8 - ctx->fb_r_mask);
0e33ea
-      tmp = ((pixel[1] << 4) | (pixel[1] >> 4));
0e33ea
-      green = tmp >> (8 - ctx->fb_g_mask);
0e33ea
-      tmp = ((pixel[2] << 4) | (pixel[2] >> 4));
0e33ea
-      blue = tmp >> (8 - ctx->fb_b_mask);
0e33ea
-    }
0e33ea
-  else
0e33ea
-    {
0e33ea
-      red   = pixel[0] >> (8 - ctx->fb_r_mask);
0e33ea
-      green = pixel[1] >> (8 - ctx->fb_g_mask);
0e33ea
-      blue  = pixel[2] >> (8 - ctx->fb_b_mask);
0e33ea
-    }
0e33ea
-
0e33ea
-  /* divide potentially by two if 'fuzzy' */
0e33ea
-  red   = red   >> (ctx->fb_r_mask - ctx->fb_r_mask_used);
0e33ea
-  green = green >> (ctx->fb_g_mask - ctx->fb_g_mask_used);
0e33ea
-  blue  = blue  >> (ctx->fb_b_mask - ctx->fb_b_mask_used);
0e33ea
-
0e33ea
-  /* combine the correct per component values into the final id */
0e33ea
-  retval = blue
0e33ea
-         + (green <<  ctx->fb_b_mask_used)
0e33ea
-         + (red << (ctx->fb_b_mask_used + ctx->fb_g_mask_used));
0e33ea
-
0e33ea
-  return retval;
0e33ea
-}
0e33ea
-
0e33ea
 static CoglPangoFontMap *
0e33ea
 clutter_context_get_pango_fontmap (void)
0e33ea
 {
0e33ea
diff --git a/clutter/clutter/clutter-private.h b/clutter/clutter/clutter-private.h
0e33ea
index 5a0fed85c9..f2f870b014 100644
0e33ea
--- a/clutter/clutter/clutter-private.h
0e33ea
+++ b/clutter/clutter/clutter-private.h
0e33ea
@@ -210,11 +210,6 @@ gboolean      _clutter_feature_init (GError **error);
0e33ea
 gboolean        _clutter_diagnostic_enabled     (void);
0e33ea
 void            _clutter_diagnostic_message     (const char *fmt, ...) G_GNUC_PRINTF (1, 2);
0e33ea
 
0e33ea
-/* Picking code */
0e33ea
-guint           _clutter_pixel_to_id            (guchar        pixel[4]);
0e33ea
-void            _clutter_id_to_color            (guint         id,
0e33ea
-                                                 ClutterColor *col);
0e33ea
-
0e33ea
 void            _clutter_set_sync_to_vblank     (gboolean      sync_to_vblank);
0e33ea
 
0e33ea
 /* use this function as the accumulator if you have a signal with
0e33ea
diff --git a/clutter/clutter/clutter-stage-private.h b/clutter/clutter/clutter-stage-private.h
0e33ea
index 42474687ad..51ae47af1d 100644
0e33ea
--- a/clutter/clutter/clutter-stage-private.h
0e33ea
+++ b/clutter/clutter/clutter-stage-private.h
0e33ea
@@ -75,6 +75,15 @@ gint64    _clutter_stage_get_update_time                  (ClutterStage *stage);
0e33ea
 void     _clutter_stage_clear_update_time                 (ClutterStage *stage);
0e33ea
 gboolean _clutter_stage_has_full_redraw_queued            (ClutterStage *stage);
0e33ea
 
0e33ea
+void clutter_stage_log_pick (ClutterStage       *stage,
0e33ea
+                             const ClutterPoint *vertices,
0e33ea
+                             ClutterActor       *actor);
0e33ea
+
0e33ea
+void clutter_stage_push_pick_clip (ClutterStage       *stage,
0e33ea
+                                   const ClutterPoint *vertices);
0e33ea
+
0e33ea
+void clutter_stage_pop_pick_clip (ClutterStage *stage);
0e33ea
+
0e33ea
 ClutterActor *_clutter_stage_do_pick (ClutterStage    *stage,
0e33ea
                                       gint             x,
0e33ea
                                       gint             y,
0e33ea
@@ -93,13 +102,6 @@ void                          _clutter_stage_queue_redraw_entry_invalidate (Clut
0e33ea
 
0e33ea
 CoglFramebuffer *_clutter_stage_get_active_framebuffer (ClutterStage *stage);
0e33ea
 
0e33ea
-gint32          _clutter_stage_acquire_pick_id          (ClutterStage *stage,
0e33ea
-                                                         ClutterActor *actor);
0e33ea
-void            _clutter_stage_release_pick_id          (ClutterStage *stage,
0e33ea
-                                                         gint32        pick_id);
0e33ea
-ClutterActor *  _clutter_stage_get_actor_by_pick_id     (ClutterStage *stage,
0e33ea
-                                                         gint32        pick_id);
0e33ea
-
0e33ea
 void            _clutter_stage_add_pointer_drag_actor    (ClutterStage       *stage,
0e33ea
                                                           ClutterInputDevice *device,
0e33ea
                                                           ClutterActor       *actor);
0e33ea
diff --git a/clutter/clutter/clutter-stage-window.c b/clutter/clutter/clutter-stage-window.c
0e33ea
index e8fa976a7d..4c4ef9d643 100644
0e33ea
--- a/clutter/clutter/clutter-stage-window.c
0e33ea
+++ b/clutter/clutter/clutter-stage-window.c
0e33ea
@@ -293,24 +293,6 @@ _clutter_stage_window_redraw (ClutterStageWindow *window)
0e33ea
     iface->redraw (window);
0e33ea
 }
0e33ea
 
0e33ea
-
0e33ea
-void
0e33ea
-_clutter_stage_window_get_dirty_pixel (ClutterStageWindow *window,
0e33ea
-                                       ClutterStageView   *view,
0e33ea
-                                       int *x, int *y)
0e33ea
-{
0e33ea
-  ClutterStageWindowInterface *iface;
0e33ea
-
0e33ea
-  *x = 0;
0e33ea
-  *y = 0;
0e33ea
-
0e33ea
-  g_return_if_fail (CLUTTER_IS_STAGE_WINDOW (window));
0e33ea
-
0e33ea
-  iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window);
0e33ea
-  if (iface->get_dirty_pixel)
0e33ea
-    iface->get_dirty_pixel (window, view, x, y);
0e33ea
-}
0e33ea
-
0e33ea
 gboolean
0e33ea
 _clutter_stage_window_can_clip_redraws (ClutterStageWindow *window)
0e33ea
 {
0e33ea
diff --git a/clutter/clutter/clutter-stage-window.h b/clutter/clutter/clutter-stage-window.h
0e33ea
index 6c3601745f..aa0c5f71cc 100644
0e33ea
--- a/clutter/clutter/clutter-stage-window.h
0e33ea
+++ b/clutter/clutter/clutter-stage-window.h
0e33ea
@@ -68,10 +68,6 @@ struct _ClutterStageWindowInterface
0e33ea
 
0e33ea
   void              (* redraw)                  (ClutterStageWindow *stage_window);
0e33ea
 
0e33ea
-  void              (* get_dirty_pixel)         (ClutterStageWindow *stage_window,
0e33ea
-                                                 ClutterStageView   *view,
0e33ea
-                                                 int *x, int *y);
0e33ea
-
0e33ea
   gboolean          (* can_clip_redraws)        (ClutterStageWindow *stage_window);
0e33ea
 
0e33ea
   GList            *(* get_views)               (ClutterStageWindow *stage_window);
0e33ea
@@ -119,10 +115,6 @@ void              _clutter_stage_window_set_accept_focus        (ClutterStageWin
0e33ea
 
0e33ea
 void              _clutter_stage_window_redraw                  (ClutterStageWindow *window);
0e33ea
 
0e33ea
-void              _clutter_stage_window_get_dirty_pixel         (ClutterStageWindow *window,
0e33ea
-                                                                 ClutterStageView   *view,
0e33ea
-                                                                 int *x, int *y);
0e33ea
-
0e33ea
 gboolean          _clutter_stage_window_can_clip_redraws        (ClutterStageWindow *window);
0e33ea
 
0e33ea
 GList *           _clutter_stage_window_get_views               (ClutterStageWindow *window);
0e33ea
diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c
0e33ea
index aaa77d9ede..7d88d5752f 100644
0e33ea
--- a/clutter/clutter/clutter-stage.c
0e33ea
+++ b/clutter/clutter/clutter-stage.c
0e33ea
@@ -97,6 +97,11 @@ typedef enum /*< prefix=CLUTTER_STAGE >*/
0e33ea
 
0e33ea
 #define STAGE_NO_CLEAR_ON_PAINT(s)      ((((ClutterStage *) (s))->priv->stage_hints & CLUTTER_STAGE_NO_CLEAR_ON_PAINT) != 0)
0e33ea
 
0e33ea
+#ifndef G_APPROX_VALUE
0e33ea
+#define G_APPROX_VALUE(a, b, epsilon) \
0e33ea
+  (((a) > (b) ? (a) - (b) : (b) - (a)) < (epsilon))
0e33ea
+#endif
0e33ea
+
0e33ea
 struct _ClutterStageQueueRedrawEntry
0e33ea
 {
0e33ea
   ClutterActor *actor;
0e33ea
@@ -104,6 +109,19 @@ struct _ClutterStageQueueRedrawEntry
0e33ea
   ClutterPaintVolume clip;
0e33ea
 };
0e33ea
 
0e33ea
+typedef struct _PickRecord
0e33ea
+{
0e33ea
+  ClutterPoint vertex[4];
0e33ea
+  ClutterActor *actor;
0e33ea
+  int clip_stack_top;
0e33ea
+} PickRecord;
0e33ea
+
0e33ea
+typedef struct _PickClipRecord
0e33ea
+{
0e33ea
+  int prev;
0e33ea
+  ClutterPoint vertex[4];
0e33ea
+} PickClipRecord;
0e33ea
+
0e33ea
 struct _ClutterStagePrivate
0e33ea
 {
0e33ea
   /* the stage implementation */
0e33ea
@@ -137,7 +155,11 @@ struct _ClutterStagePrivate
0e33ea
   GTimer *fps_timer;
0e33ea
   gint32 timer_n_frames;
0e33ea
 
0e33ea
-  ClutterIDPool *pick_id_pool;
0e33ea
+  GArray *pick_stack;
0e33ea
+  GArray *pick_clip_stack;
0e33ea
+  int pick_clip_stack_top;
0e33ea
+  gboolean pick_stack_frozen;
0e33ea
+  ClutterPickMode cached_pick_mode;
0e33ea
 
0e33ea
 #ifdef CLUTTER_ENABLE_DEBUG
0e33ea
   gulong redraw_count;
0e33ea
@@ -326,6 +348,211 @@ clutter_stage_get_preferred_height (ClutterActor *self,
0e33ea
     *natural_height_p = geom.height;
0e33ea
 }
0e33ea
 
0e33ea
+static void
0e33ea
+add_pick_stack_weak_refs (ClutterStage *stage)
0e33ea
+{
0e33ea
+  ClutterStagePrivate *priv = stage->priv;
0e33ea
+  int i;
0e33ea
+
0e33ea
+  if (priv->pick_stack_frozen)
0e33ea
+    return;
0e33ea
+
0e33ea
+  for (i = 0; i < priv->pick_stack->len; i++)
0e33ea
+    {
0e33ea
+      PickRecord *rec = &g_array_index (priv->pick_stack, PickRecord, i);
0e33ea
+
0e33ea
+      if (rec->actor)
0e33ea
+        g_object_add_weak_pointer (G_OBJECT (rec->actor),
0e33ea
+                                   (gpointer) &rec->actor);
0e33ea
+    }
0e33ea
+
0e33ea
+  priv->pick_stack_frozen = TRUE;
0e33ea
+}
0e33ea
+
0e33ea
+static void
0e33ea
+remove_pick_stack_weak_refs (ClutterStage *stage)
0e33ea
+{
0e33ea
+  ClutterStagePrivate *priv = stage->priv;
0e33ea
+  int i;
0e33ea
+
0e33ea
+  if (!priv->pick_stack_frozen)
0e33ea
+    return;
0e33ea
+
0e33ea
+  for (i = 0; i < priv->pick_stack->len; i++)
0e33ea
+    {
0e33ea
+      PickRecord *rec = &g_array_index (priv->pick_stack, PickRecord, i);
0e33ea
+
0e33ea
+      if (rec->actor)
0e33ea
+        g_object_remove_weak_pointer (G_OBJECT (rec->actor),
0e33ea
+                                      (gpointer) &rec->actor);
0e33ea
+    }
0e33ea
+
0e33ea
+  priv->pick_stack_frozen = FALSE;
0e33ea
+}
0e33ea
+
0e33ea
+static void
0e33ea
+_clutter_stage_clear_pick_stack (ClutterStage *stage)
0e33ea
+{
0e33ea
+  ClutterStagePrivate *priv = stage->priv;
0e33ea
+
0e33ea
+  remove_pick_stack_weak_refs (stage);
0e33ea
+  g_array_set_size (priv->pick_stack, 0);
0e33ea
+  g_array_set_size (priv->pick_clip_stack, 0);
0e33ea
+  priv->pick_clip_stack_top = -1;
0e33ea
+  priv->cached_pick_mode = CLUTTER_PICK_NONE;
0e33ea
+}
0e33ea
+
0e33ea
+void
0e33ea
+clutter_stage_log_pick (ClutterStage       *stage,
0e33ea
+                        const ClutterPoint *vertices,
0e33ea
+                        ClutterActor       *actor)
0e33ea
+{
0e33ea
+  ClutterStagePrivate *priv;
0e33ea
+  PickRecord rec;
0e33ea
+
0e33ea
+  g_return_if_fail (CLUTTER_IS_STAGE (stage));
0e33ea
+  g_return_if_fail (actor != NULL);
0e33ea
+
0e33ea
+  priv = stage->priv;
0e33ea
+
0e33ea
+  g_assert (!priv->pick_stack_frozen);
0e33ea
+
0e33ea
+  memcpy (rec.vertex, vertices, 4 * sizeof (ClutterPoint));
0e33ea
+  rec.actor = actor;
0e33ea
+  rec.clip_stack_top = priv->pick_clip_stack_top;
0e33ea
+
0e33ea
+  g_array_append_val (priv->pick_stack, rec);
0e33ea
+}
0e33ea
+
0e33ea
+void
0e33ea
+clutter_stage_push_pick_clip (ClutterStage       *stage,
0e33ea
+                              const ClutterPoint *vertices)
0e33ea
+{
0e33ea
+  ClutterStagePrivate *priv;
0e33ea
+  PickClipRecord clip;
0e33ea
+
0e33ea
+  g_return_if_fail (CLUTTER_IS_STAGE (stage));
0e33ea
+
0e33ea
+  priv = stage->priv;
0e33ea
+
0e33ea
+  g_assert (!priv->pick_stack_frozen);
0e33ea
+
0e33ea
+  clip.prev = priv->pick_clip_stack_top;
0e33ea
+  memcpy (clip.vertex, vertices, 4 * sizeof (ClutterPoint));
0e33ea
+
0e33ea
+  g_array_append_val (priv->pick_clip_stack, clip);
0e33ea
+  priv->pick_clip_stack_top = priv->pick_clip_stack->len - 1;
0e33ea
+}
0e33ea
+
0e33ea
+void
0e33ea
+clutter_stage_pop_pick_clip (ClutterStage *stage)
0e33ea
+{
0e33ea
+  ClutterStagePrivate *priv;
0e33ea
+  const PickClipRecord *top;
0e33ea
+
0e33ea
+  g_return_if_fail (CLUTTER_IS_STAGE (stage));
0e33ea
+
0e33ea
+  priv = stage->priv;
0e33ea
+
0e33ea
+  g_assert (!priv->pick_stack_frozen);
0e33ea
+  g_assert (priv->pick_clip_stack_top >= 0);
0e33ea
+
0e33ea
+  /* Individual elements of pick_clip_stack are not freed. This is so they
0e33ea
+   * can be shared as part of a tree of different stacks used by different
0e33ea
+   * actors in the pick_stack. The whole pick_clip_stack does however get
0e33ea
+   * freed later in _clutter_stage_clear_pick_stack.
0e33ea
+   */
0e33ea
+
0e33ea
+  top = &g_array_index (priv->pick_clip_stack,
0e33ea
+                        PickClipRecord,
0e33ea
+                        priv->pick_clip_stack_top);
0e33ea
+
0e33ea
+  priv->pick_clip_stack_top = top->prev;
0e33ea
+}
0e33ea
+
0e33ea
+static gboolean
0e33ea
+is_quadrilateral_axis_aligned_rectangle (const ClutterPoint *vertices)
0e33ea
+{
0e33ea
+  int i;
0e33ea
+
0e33ea
+  for (i = 0; i < 4; i++)
0e33ea
+    {
0e33ea
+      if (!G_APPROX_VALUE (vertices[i].x,
0e33ea
+                           vertices[(i + 1) % 4].x,
0e33ea
+                           FLT_EPSILON) &&
0e33ea
+          !G_APPROX_VALUE (vertices[i].y,
0e33ea
+                           vertices[(i + 1) % 4].y,
0e33ea
+                           FLT_EPSILON))
0e33ea
+        return FALSE;
0e33ea
+    }
0e33ea
+  return TRUE;
0e33ea
+}
0e33ea
+
0e33ea
+static gboolean
0e33ea
+is_inside_axis_aligned_rectangle (const ClutterPoint *point,
0e33ea
+                                  const ClutterPoint *vertices)
0e33ea
+{
0e33ea
+  float min_x = FLT_MAX;
0e33ea
+  float max_x = FLT_MIN;
0e33ea
+  float min_y = FLT_MAX;
0e33ea
+  float max_y = FLT_MIN;
0e33ea
+  int i;
0e33ea
+
0e33ea
+  for (i = 0; i < 3; i++)
0e33ea
+    {
0e33ea
+      min_x = MIN (min_x, vertices[i].x);
0e33ea
+      min_y = MIN (min_y, vertices[i].y);
0e33ea
+      max_x = MAX (max_x, vertices[i].x);
0e33ea
+      max_y = MAX (max_y, vertices[i].y);
0e33ea
+    }
0e33ea
+
0e33ea
+  return (point->x >= min_x &&
0e33ea
+          point->y >= min_y &&
0e33ea
+          point->x < max_x &&
0e33ea
+          point->y < max_y);
0e33ea
+}
0e33ea
+
0e33ea
+static gboolean
0e33ea
+is_inside_input_region (const ClutterPoint *point,
0e33ea
+                        const ClutterPoint *vertices)
0e33ea
+{
0e33ea
+
0e33ea
+  if (is_quadrilateral_axis_aligned_rectangle (vertices))
0e33ea
+    return is_inside_axis_aligned_rectangle (point, vertices);
0e33ea
+  else
0e33ea
+    return clutter_point_inside_quadrilateral (point, vertices);
0e33ea
+}
0e33ea
+
0e33ea
+static gboolean
0e33ea
+pick_record_contains_pixel (ClutterStage     *stage,
0e33ea
+                            const PickRecord *rec,
0e33ea
+                            int               x,
0e33ea
+                            int               y)
0e33ea
+{
0e33ea
+  const ClutterPoint point = CLUTTER_POINT_INIT (x, y);
0e33ea
+  ClutterStagePrivate *priv;
0e33ea
+  int clip_index;
0e33ea
+
0e33ea
+  if (!is_inside_input_region (&point, rec->vertex))
0e33ea
+      return FALSE;
0e33ea
+
0e33ea
+  priv = stage->priv;
0e33ea
+  clip_index = rec->clip_stack_top;
0e33ea
+  while (clip_index >= 0)
0e33ea
+    {
0e33ea
+      const PickClipRecord *clip = &g_array_index (priv->pick_clip_stack,
0e33ea
+                                                   PickClipRecord,
0e33ea
+                                                   clip_index);
0e33ea
+
0e33ea
+      if (!is_inside_input_region (&point, clip->vertex))
0e33ea
+        return FALSE;
0e33ea
+
0e33ea
+      clip_index = clip->prev;
0e33ea
+    }
0e33ea
+
0e33ea
+  return TRUE;
0e33ea
+}
0e33ea
+
0e33ea
 static inline void
0e33ea
 queue_full_redraw (ClutterStage *stage)
0e33ea
 {
0e33ea
@@ -636,6 +863,12 @@ clutter_stage_do_paint_view (ClutterStage                *stage,
0e33ea
   float viewport[4];
0e33ea
   cairo_rectangle_int_t geom;
0e33ea
 
0e33ea
+  /* Any mode of painting/picking invalidates the pick cache, unless we're
0e33ea
+   * in the middle of building it. So we reset the cached flag but don't
0e33ea
+   * completely clear the pick stack.
0e33ea
+   */
0e33ea
+  priv->cached_pick_mode = CLUTTER_PICK_NONE;
0e33ea
+
0e33ea
   _clutter_stage_window_get_geometry (priv->impl, &geom);
0e33ea
 
0e33ea
   viewport[0] = priv->viewport[0];
0e33ea
@@ -1414,40 +1647,6 @@ clutter_stage_get_redraw_clip_bounds (ClutterStage          *stage,
0e33ea
     }
0e33ea
 }
0e33ea
 
0e33ea
-static void
0e33ea
-read_pixels_to_file (CoglFramebuffer *fb,
0e33ea
-                     char            *filename_stem,
0e33ea
-                     int              x,
0e33ea
-                     int              y,
0e33ea
-                     int              width,
0e33ea
-                     int              height)
0e33ea
-{
0e33ea
-  guint8 *data;
0e33ea
-  cairo_surface_t *surface;
0e33ea
-  static int read_count = 0;
0e33ea
-  char *filename = g_strdup_printf ("%s-%05d.png",
0e33ea
-                                    filename_stem,
0e33ea
-                                    read_count);
0e33ea
-
0e33ea
-  data = g_malloc (4 * width * height);
0e33ea
-  cogl_framebuffer_read_pixels (fb,
0e33ea
-                                x, y, width, height,
0e33ea
-                                CLUTTER_CAIRO_FORMAT_ARGB32,
0e33ea
-                                data);
0e33ea
-
0e33ea
-  surface = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_RGB24,
0e33ea
-                                                 width, height,
0e33ea
-                                                 width * 4);
0e33ea
-
0e33ea
-  cairo_surface_write_to_png (surface, filename);
0e33ea
-  cairo_surface_destroy (surface);
0e33ea
-
0e33ea
-  g_free (data);
0e33ea
-  g_free (filename);
0e33ea
-
0e33ea
-  read_count++;
0e33ea
-}
0e33ea
-
0e33ea
 static ClutterActor *
0e33ea
 _clutter_stage_do_pick_on_view (ClutterStage     *stage,
0e33ea
                                 gint              x,
0e33ea
@@ -1455,140 +1654,42 @@ _clutter_stage_do_pick_on_view (ClutterStage     *stage,
0e33ea
                                 ClutterPickMode   mode,
0e33ea
                                 ClutterStageView *view)
0e33ea
 {
0e33ea
-  ClutterActor *actor = CLUTTER_ACTOR (stage);
0e33ea
+  ClutterMainContext *context = _clutter_context_get_default ();
0e33ea
   ClutterStagePrivate *priv = stage->priv;
0e33ea
   CoglFramebuffer *fb = clutter_stage_view_get_framebuffer (view);
0e33ea
-  cairo_rectangle_int_t view_layout;
0e33ea
-  ClutterMainContext *context;
0e33ea
-  guchar pixel[4] = { 0xff, 0xff, 0xff, 0xff };
0e33ea
-  CoglColor stage_pick_id;
0e33ea
-  gboolean dither_enabled_save;
0e33ea
-  ClutterActor *retval;
0e33ea
-  gint dirty_x;
0e33ea
-  gint dirty_y;
0e33ea
-  gint read_x;
0e33ea
-  gint read_y;
0e33ea
-  float fb_width, fb_height;
0e33ea
-  float fb_scale;
0e33ea
-  float viewport_offset_x;
0e33ea
-  float viewport_offset_y;
0e33ea
-
0e33ea
-  priv = stage->priv;
0e33ea
-
0e33ea
-  context = _clutter_context_get_default ();
0e33ea
-  fb_scale = clutter_stage_view_get_scale (view);
0e33ea
-  clutter_stage_view_get_layout (view, &view_layout);
0e33ea
-
0e33ea
-  fb_width = view_layout.width * fb_scale;
0e33ea
-  fb_height = view_layout.height * fb_scale;
0e33ea
-  cogl_push_framebuffer (fb);
0e33ea
-
0e33ea
-  /* needed for when a context switch happens */
0e33ea
-  _clutter_stage_maybe_setup_viewport (stage, view);
0e33ea
-
0e33ea
-  /* FIXME: For some reason leaving the cogl clip stack empty causes the
0e33ea
-   * picking to not work at all, so setting it the whole framebuffer content
0e33ea
-   * for now. */
0e33ea
-  cogl_framebuffer_push_scissor_clip (fb, 0, 0,
0e33ea
-                                      view_layout.width * fb_scale,
0e33ea
-                                      view_layout.height * fb_scale);
0e33ea
-
0e33ea
-  _clutter_stage_window_get_dirty_pixel (priv->impl, view, &dirty_x, &dirty_y);
0e33ea
+  int i;
0e33ea
 
0e33ea
-  if (G_LIKELY (!(clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS)))
0e33ea
-    {
0e33ea
-      CLUTTER_NOTE (PICK, "Pushing pick scissor clip x: %d, y: %d, 1x1",
0e33ea
-                    (int) (dirty_x * fb_scale),
0e33ea
-                    (int) (dirty_y * fb_scale));
0e33ea
-      cogl_framebuffer_push_scissor_clip (fb, dirty_x * fb_scale, dirty_y * fb_scale, 1, 1);
0e33ea
-    }
0e33ea
+  g_assert (context->pick_mode == CLUTTER_PICK_NONE);
0e33ea
 
0e33ea
-  viewport_offset_x = x * fb_scale - dirty_x * fb_scale;
0e33ea
-  viewport_offset_y = y * fb_scale - dirty_y * fb_scale;
0e33ea
-  CLUTTER_NOTE (PICK, "Setting viewport to %f, %f, %f, %f",
0e33ea
-                priv->viewport[0] * fb_scale - viewport_offset_x,
0e33ea
-                priv->viewport[1] * fb_scale - viewport_offset_y,
0e33ea
-                priv->viewport[2] * fb_scale,
0e33ea
-                priv->viewport[3] * fb_scale);
0e33ea
-  cogl_framebuffer_set_viewport (fb,
0e33ea
-                                 priv->viewport[0] * fb_scale - viewport_offset_x,
0e33ea
-                                 priv->viewport[1] * fb_scale - viewport_offset_y,
0e33ea
-                                 priv->viewport[2] * fb_scale,
0e33ea
-                                 priv->viewport[3] * fb_scale);
0e33ea
-
0e33ea
-  read_x = dirty_x * fb_scale;
0e33ea
-  read_y = dirty_y * fb_scale;
0e33ea
-
0e33ea
-  CLUTTER_NOTE (PICK, "Performing pick at %i,%i on view %dx%d+%d+%d s: %f",
0e33ea
-                x, y,
0e33ea
-                view_layout.width, view_layout.height,
0e33ea
-                view_layout.x, view_layout.y, fb_scale);
0e33ea
-
0e33ea
-  cogl_color_init_from_4ub (&stage_pick_id, 255, 255, 255, 255);
0e33ea
-  cogl_framebuffer_clear (fb, COGL_BUFFER_BIT_COLOR | COGL_BUFFER_BIT_DEPTH, &stage_pick_id);
0e33ea
-
0e33ea
-  /* Disable dithering (if any) when doing the painting in pick mode */
0e33ea
-  dither_enabled_save = cogl_framebuffer_get_dither_enabled (fb);
0e33ea
-  cogl_framebuffer_set_dither_enabled (fb, FALSE);
0e33ea
-
0e33ea
-  /* Render the entire scence in pick mode - just single colored silhouette's
0e33ea
-   * are drawn offscreen (as we never swap buffers)
0e33ea
-  */
0e33ea
-  context->pick_mode = mode;
0e33ea
-
0e33ea
-  clutter_stage_do_paint_view (stage, view, NULL);
0e33ea
-  context->pick_mode = CLUTTER_PICK_NONE;
0e33ea
-
0e33ea
-  /* Read the color of the screen co-ords pixel. RGBA_8888_PRE is used
0e33ea
-     even though we don't care about the alpha component because under
0e33ea
-     GLES this is the only format that is guaranteed to work so Cogl
0e33ea
-     will end up having to do a conversion if any other format is
0e33ea
-     used. The format is requested as pre-multiplied because Cogl
0e33ea
-     assumes that all pixels in the framebuffer are premultiplied so
0e33ea
-     it avoids a conversion. */
0e33ea
-  cogl_framebuffer_read_pixels (fb,
0e33ea
-                                read_x, read_y, 1, 1,
0e33ea
-                                COGL_PIXEL_FORMAT_RGBA_8888_PRE,
0e33ea
-                                pixel);
0e33ea
-
0e33ea
-  if (G_UNLIKELY (clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS))
0e33ea
+  if (mode != priv->cached_pick_mode)
0e33ea
     {
0e33ea
-      char *file_name =
0e33ea
-        g_strdup_printf ("pick-buffer-%s-view-x-%d",
0e33ea
-                         _clutter_actor_get_debug_name (actor),
0e33ea
-                         view_layout.x);
0e33ea
+      _clutter_stage_clear_pick_stack (stage);
0e33ea
 
0e33ea
-      read_pixels_to_file (fb, file_name, 0, 0, fb_width, fb_height);
0e33ea
+      cogl_push_framebuffer (fb);
0e33ea
 
0e33ea
-      g_free (file_name);
0e33ea
-    }
0e33ea
-
0e33ea
-  /* Restore whether GL_DITHER was enabled */
0e33ea
-  cogl_framebuffer_set_dither_enabled (fb, dither_enabled_save);
0e33ea
-
0e33ea
-  if (G_LIKELY (!(clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS)))
0e33ea
-    cogl_framebuffer_pop_clip (fb);
0e33ea
+      context->pick_mode = mode;
0e33ea
+      clutter_stage_do_paint_view (stage, view, NULL);
0e33ea
+      context->pick_mode = CLUTTER_PICK_NONE;
0e33ea
+      priv->cached_pick_mode = mode;
0e33ea
 
0e33ea
-  cogl_framebuffer_pop_clip (fb);
0e33ea
+      cogl_pop_framebuffer ();
0e33ea
 
0e33ea
-  _clutter_stage_dirty_viewport (stage);
0e33ea
+      add_pick_stack_weak_refs (stage);
0e33ea
+    }
0e33ea
 
0e33ea
-  if (pixel[0] == 0xff && pixel[1] == 0xff && pixel[2] == 0xff)
0e33ea
-    retval = actor;
0e33ea
-  else
0e33ea
+  /* Search all "painted" pickable actors from front to back. A linear search
0e33ea
+   * is required, and also performs fine since there is typically only
0e33ea
+   * on the order of dozens of actors in the list (on screen) at a time.
0e33ea
+   */
0e33ea
+  for (i = priv->pick_stack->len - 1; i >= 0; i--)
0e33ea
     {
0e33ea
-      guint32 id_ = _clutter_pixel_to_id (pixel);
0e33ea
+      const PickRecord *rec = &g_array_index (priv->pick_stack, PickRecord, i);
0e33ea
 
0e33ea
-      retval = _clutter_stage_get_actor_by_pick_id (stage, id_);
0e33ea
-      CLUTTER_NOTE (PICK, "Picking actor %s with id %u (pixel: 0x%x%x%x%x",
0e33ea
-                    G_OBJECT_TYPE_NAME (retval),
0e33ea
-                    id_,
0e33ea
-                    pixel[0], pixel[1], pixel[2], pixel[3]);
0e33ea
+      if (rec->actor && pick_record_contains_pixel (stage, rec, x, y))
0e33ea
+        return rec->actor;
0e33ea
     }
0e33ea
 
0e33ea
-  cogl_pop_framebuffer ();
0e33ea
-
0e33ea
-  return retval;
0e33ea
+  return CLUTTER_ACTOR (stage);
0e33ea
 }
0e33ea
 
0e33ea
 static ClutterStageView *
0e33ea
@@ -1901,7 +2002,9 @@ clutter_stage_finalize (GObject *object)
0e33ea
 
0e33ea
   g_array_free (priv->paint_volume_stack, TRUE);
0e33ea
 
0e33ea
-  _clutter_id_pool_free (priv->pick_id_pool);
0e33ea
+  _clutter_stage_clear_pick_stack (stage);
0e33ea
+  g_array_free (priv->pick_clip_stack, TRUE);
0e33ea
+  g_array_free (priv->pick_stack, TRUE);
0e33ea
 
0e33ea
   if (priv->fps_timer != NULL)
0e33ea
     g_timer_destroy (priv->fps_timer);
0e33ea
@@ -2435,7 +2538,10 @@ clutter_stage_init (ClutterStage *self)
0e33ea
   priv->paint_volume_stack =
0e33ea
     g_array_new (FALSE, FALSE, sizeof (ClutterPaintVolume));
0e33ea
 
0e33ea
-  priv->pick_id_pool = _clutter_id_pool_new (256);
0e33ea
+  priv->pick_stack = g_array_new (FALSE, FALSE, sizeof (PickRecord));
0e33ea
+  priv->pick_clip_stack = g_array_new (FALSE, FALSE, sizeof (PickClipRecord));
0e33ea
+  priv->pick_clip_stack_top = -1;
0e33ea
+  priv->cached_pick_mode = CLUTTER_PICK_NONE;
0e33ea
 }
0e33ea
 
0e33ea
 /**
0e33ea
@@ -4253,6 +4359,12 @@ _clutter_stage_queue_actor_redraw (ClutterStage                 *stage,
0e33ea
   CLUTTER_NOTE (CLIPPING, "stage_queue_actor_redraw (actor=%s, clip=%p): ",
0e33ea
                 _clutter_actor_get_debug_name (actor), clip);
0e33ea
 
0e33ea
+  /* Queuing a redraw or clip change invalidates the pick cache, unless we're
0e33ea
+   * in the middle of building it. So we reset the cached flag but don't
0e33ea
+   * completely clear the pick stack...
0e33ea
+   */
0e33ea
+  priv->cached_pick_mode = CLUTTER_PICK_NONE;
0e33ea
+
0e33ea
   if (!priv->redraw_pending)
0e33ea
     {
0e33ea
       ClutterMasterClock *master_clock;
0e33ea
@@ -4513,39 +4625,6 @@ _clutter_stage_get_active_framebuffer (ClutterStage *stage)
0e33ea
   return stage->priv->active_framebuffer;
0e33ea
 }
0e33ea
 
0e33ea
-gint32
0e33ea
-_clutter_stage_acquire_pick_id (ClutterStage *stage,
0e33ea
-                                ClutterActor *actor)
0e33ea
-{
0e33ea
-  ClutterStagePrivate *priv = stage->priv;
0e33ea
-
0e33ea
-  g_assert (priv->pick_id_pool != NULL);
0e33ea
-
0e33ea
-  return _clutter_id_pool_add (priv->pick_id_pool, actor);
0e33ea
-}
0e33ea
-
0e33ea
-void
0e33ea
-_clutter_stage_release_pick_id (ClutterStage *stage,
0e33ea
-                                gint32        pick_id)
0e33ea
-{
0e33ea
-  ClutterStagePrivate *priv = stage->priv;
0e33ea
-
0e33ea
-  g_assert (priv->pick_id_pool != NULL);
0e33ea
-
0e33ea
-  _clutter_id_pool_remove (priv->pick_id_pool, pick_id);
0e33ea
-}
0e33ea
-
0e33ea
-ClutterActor *
0e33ea
-_clutter_stage_get_actor_by_pick_id (ClutterStage *stage,
0e33ea
-                                     gint32        pick_id)
0e33ea
-{
0e33ea
-  ClutterStagePrivate *priv = stage->priv;
0e33ea
-
0e33ea
-  g_assert (priv->pick_id_pool != NULL);
0e33ea
-
0e33ea
-  return _clutter_id_pool_lookup (priv->pick_id_pool, pick_id);
0e33ea
-}
0e33ea
-
0e33ea
 void
0e33ea
 _clutter_stage_add_pointer_drag_actor (ClutterStage       *stage,
0e33ea
                                        ClutterInputDevice *device,
0e33ea
diff --git a/clutter/clutter/cogl/clutter-stage-cogl.c b/clutter/clutter/cogl/clutter-stage-cogl.c
0e33ea
index 3f1f609c4e..effed79759 100644
0e33ea
--- a/clutter/clutter/cogl/clutter-stage-cogl.c
0e33ea
+++ b/clutter/clutter/cogl/clutter-stage-cogl.c
0e33ea
@@ -926,57 +926,6 @@ clutter_stage_cogl_redraw (ClutterStageWindow *stage_window)
0e33ea
   stage_cogl->frame_count++;
0e33ea
 }
0e33ea
 
0e33ea
-static void
0e33ea
-clutter_stage_cogl_get_dirty_pixel (ClutterStageWindow *stage_window,
0e33ea
-                                    ClutterStageView   *view,
0e33ea
-                                    int                *x,
0e33ea
-                                    int                *y)
0e33ea
-{
0e33ea
-  CoglFramebuffer *onscreen = clutter_stage_view_get_onscreen (view);
0e33ea
-  ClutterStageViewCogl *view_cogl = CLUTTER_STAGE_VIEW_COGL (view);
0e33ea
-  ClutterStageViewCoglPrivate *view_priv =
0e33ea
-    clutter_stage_view_cogl_get_instance_private (view_cogl);
0e33ea
-  gboolean has_buffer_age =
0e33ea
-    cogl_is_onscreen (onscreen) &&
0e33ea
-    is_buffer_age_enabled ();
0e33ea
-  float fb_scale;
0e33ea
-  gboolean scale_is_fractional;
0e33ea
-
0e33ea
-  fb_scale = clutter_stage_view_get_scale (view);
0e33ea
-  if (fb_scale != floorf (fb_scale))
0e33ea
-    scale_is_fractional = TRUE;
0e33ea
-  else
0e33ea
-    scale_is_fractional = FALSE;
0e33ea
-
0e33ea
-  /*
0e33ea
-   * Buffer damage is tracked in the framebuffer coordinate space
0e33ea
-   * using the damage history. When fractional scaling is used, a
0e33ea
-   * coordinate on the stage might not correspond to the exact position of any
0e33ea
-   * physical pixel, which causes issues when painting using the pick mode.
0e33ea
-   *
0e33ea
-   * For now, always use the (0, 0) pixel for picking when using fractional
0e33ea
-   * framebuffer scaling.
0e33ea
-   */
0e33ea
-  if (!has_buffer_age ||
0e33ea
-      scale_is_fractional ||
0e33ea
-      !clutter_damage_history_is_age_valid (view_priv->damage_history, 0))
0e33ea
-    {
0e33ea
-      *x = 0;
0e33ea
-      *y = 0;
0e33ea
-    }
0e33ea
-  else
0e33ea
-    {
0e33ea
-      cairo_rectangle_int_t view_layout;
0e33ea
-      const cairo_rectangle_int_t *fb_damage;
0e33ea
-
0e33ea
-      clutter_stage_view_get_layout (view, &view_layout);
0e33ea
-
0e33ea
-      fb_damage = clutter_damage_history_lookup (view_priv->damage_history, 0);
0e33ea
-      *x = fb_damage->x / fb_scale;
0e33ea
-      *y = fb_damage->y / fb_scale;
0e33ea
-    }
0e33ea
-}
0e33ea
-
0e33ea
 static void
0e33ea
 clutter_stage_window_iface_init (ClutterStageWindowInterface *iface)
0e33ea
 {
0e33ea
@@ -994,7 +943,6 @@ clutter_stage_window_iface_init (ClutterStageWindowInterface *iface)
0e33ea
   iface->ignoring_redraw_clips = clutter_stage_cogl_ignoring_redraw_clips;
0e33ea
   iface->get_redraw_clip_bounds = clutter_stage_cogl_get_redraw_clip_bounds;
0e33ea
   iface->redraw = clutter_stage_cogl_redraw;
0e33ea
-  iface->get_dirty_pixel = clutter_stage_cogl_get_dirty_pixel;
0e33ea
 }
0e33ea
 
0e33ea
 static void
0e33ea
diff --git a/clutter/clutter/deprecated/clutter-texture.c b/clutter/clutter/deprecated/clutter-texture.c
0e33ea
index bea239f454..2c677b8a44 100644
0e33ea
--- a/clutter/clutter/deprecated/clutter-texture.c
0e33ea
+++ b/clutter/clutter/deprecated/clutter-texture.c
0e33ea
@@ -572,83 +572,6 @@ gen_texcoords_and_draw_cogl_rectangle (ClutterActor    *self,
0e33ea
                                             0, 0, t_w, t_h);
0e33ea
 }
0e33ea
 
0e33ea
-static CoglPipeline *
0e33ea
-create_pick_pipeline (ClutterActor *self)
0e33ea
-{
0e33ea
-  ClutterTexture *texture = CLUTTER_TEXTURE (self);
0e33ea
-  ClutterTexturePrivate *priv = texture->priv;
0e33ea
-  CoglPipeline *pick_pipeline = cogl_pipeline_copy (texture_template_pipeline);
0e33ea
-  GError *error = NULL;
0e33ea
-
0e33ea
-  if (!cogl_pipeline_set_layer_combine (pick_pipeline, 0,
0e33ea
-                                        "RGBA = "
0e33ea
-                                        "  MODULATE (CONSTANT, TEXTURE[A])",
0e33ea
-                                        &error))
0e33ea
-    {
0e33ea
-      if (!priv->seen_create_pick_pipeline_warning)
0e33ea
-        g_warning ("Error setting up texture combine for shaped "
0e33ea
-                   "texture picking: %s", error->message);
0e33ea
-      priv->seen_create_pick_pipeline_warning = TRUE;
0e33ea
-      g_error_free (error);
0e33ea
-      cogl_object_unref (pick_pipeline);
0e33ea
-      return NULL;
0e33ea
-    }
0e33ea
-
0e33ea
-  cogl_pipeline_set_blend (pick_pipeline,
0e33ea
-                           "RGBA = ADD (SRC_COLOR[RGBA], 0)",
0e33ea
-                           NULL);
0e33ea
-
0e33ea
-  cogl_pipeline_set_alpha_test_function (pick_pipeline,
0e33ea
-                                         COGL_PIPELINE_ALPHA_FUNC_EQUAL,
0e33ea
-                                         1.0);
0e33ea
-
0e33ea
-  return pick_pipeline;
0e33ea
-}
0e33ea
-
0e33ea
-static void
0e33ea
-clutter_texture_pick (ClutterActor       *self,
0e33ea
-                      const ClutterColor *color)
0e33ea
-{
0e33ea
-  ClutterTexture *texture = CLUTTER_TEXTURE (self);
0e33ea
-  ClutterTexturePrivate *priv = texture->priv;
0e33ea
-  CoglFramebuffer *framebuffer = cogl_get_draw_framebuffer ();
0e33ea
-
0e33ea
-  if (!clutter_actor_should_pick_paint (self))
0e33ea
-    return;
0e33ea
-
0e33ea
-  if (G_LIKELY (priv->pick_with_alpha_supported) && priv->pick_with_alpha)
0e33ea
-    {
0e33ea
-      CoglColor pick_color;
0e33ea
-
0e33ea
-      if (priv->pick_pipeline == NULL)
0e33ea
-        priv->pick_pipeline = create_pick_pipeline (self);
0e33ea
-
0e33ea
-      if (priv->pick_pipeline == NULL)
0e33ea
-        {
0e33ea
-          priv->pick_with_alpha_supported = FALSE;
0e33ea
-          CLUTTER_ACTOR_CLASS (clutter_texture_parent_class)->pick (self,
0e33ea
-                                                                    color);
0e33ea
-          return;
0e33ea
-        }
0e33ea
-
0e33ea
-      if (priv->fbo_handle != NULL)
0e33ea
-        update_fbo (self);
0e33ea
-
0e33ea
-      cogl_color_init_from_4ub (&pick_color,
0e33ea
-                                color->red,
0e33ea
-                                color->green,
0e33ea
-                                color->blue,
0e33ea
-                                0xff);
0e33ea
-      cogl_pipeline_set_layer_combine_constant (priv->pick_pipeline,
0e33ea
-                                                0, &pick_color);
0e33ea
-      cogl_pipeline_set_layer_texture (priv->pick_pipeline, 0,
0e33ea
-                                       clutter_texture_get_cogl_texture (texture));
0e33ea
-      gen_texcoords_and_draw_cogl_rectangle (self, priv->pick_pipeline, framebuffer);
0e33ea
-    }
0e33ea
-  else
0e33ea
-    CLUTTER_ACTOR_CLASS (clutter_texture_parent_class)->pick (self, color);
0e33ea
-}
0e33ea
-
0e33ea
 static void
0e33ea
 clutter_texture_paint (ClutterActor *self)
0e33ea
 {
0e33ea
@@ -767,12 +690,6 @@ clutter_texture_dispose (GObject *object)
0e33ea
       priv->pipeline = NULL;
0e33ea
     }
0e33ea
 
0e33ea
-  if (priv->pick_pipeline != NULL)
0e33ea
-    {
0e33ea
-      cogl_object_unref (priv->pick_pipeline);
0e33ea
-      priv->pick_pipeline = NULL;
0e33ea
-    }
0e33ea
-
0e33ea
   G_OBJECT_CLASS (clutter_texture_parent_class)->dispose (object);
0e33ea
 }
0e33ea
 
0e33ea
@@ -944,7 +861,6 @@ clutter_texture_class_init (ClutterTextureClass *klass)
0e33ea
   GParamSpec *pspec;
0e33ea
 
0e33ea
   actor_class->paint            = clutter_texture_paint;
0e33ea
-  actor_class->pick             = clutter_texture_pick;
0e33ea
   actor_class->get_paint_volume = clutter_texture_get_paint_volume;
0e33ea
   actor_class->realize          = clutter_texture_realize;
0e33ea
   actor_class->unrealize        = clutter_texture_unrealize;
0e33ea
@@ -1263,11 +1179,9 @@ clutter_texture_init (ClutterTexture *self)
0e33ea
   priv->repeat_y          = FALSE;
0e33ea
   priv->sync_actor_size   = TRUE;
0e33ea
   priv->fbo_handle        = NULL;
0e33ea
-  priv->pick_pipeline     = NULL;
0e33ea
   priv->keep_aspect_ratio = FALSE;
0e33ea
   priv->pick_with_alpha   = FALSE;
0e33ea
   priv->pick_with_alpha_supported = TRUE;
0e33ea
-  priv->seen_create_pick_pipeline_warning = FALSE;
0e33ea
 
0e33ea
   if (G_UNLIKELY (texture_template_pipeline == NULL))
0e33ea
     {
0e33ea
@@ -3052,13 +2966,8 @@ clutter_texture_set_pick_with_alpha (ClutterTexture *texture,
0e33ea
   if (priv->pick_with_alpha == pick_with_alpha)
0e33ea
     return;
0e33ea
 
0e33ea
-  if (!pick_with_alpha && priv->pick_pipeline != NULL)
0e33ea
-    {
0e33ea
-      cogl_object_unref (priv->pick_pipeline);
0e33ea
-      priv->pick_pipeline = NULL;
0e33ea
-    }
0e33ea
+  g_assert (!pick_with_alpha);  /* No longer supported */
0e33ea
 
0e33ea
-  /* NB: the pick pipeline is created lazily when we first pick */
0e33ea
   priv->pick_with_alpha = pick_with_alpha;
0e33ea
 
0e33ea
   /* NB: actors are expected to call clutter_actor_queue_redraw when
0e33ea
diff --git a/clutter/tests/conform/actor-pick.c b/clutter/tests/conform/actor-pick.c
0e33ea
index 969b4920ac..2bf5954c73 100644
0e33ea
--- a/clutter/tests/conform/actor-pick.c
0e33ea
+++ b/clutter/tests/conform/actor-pick.c
0e33ea
@@ -5,7 +5,6 @@
0e33ea
 #define STAGE_HEIGHT 480
0e33ea
 #define ACTORS_X 12
0e33ea
 #define ACTORS_Y 16
0e33ea
-#define SHIFT_STEP STAGE_WIDTH / ACTORS_X
0e33ea
 
0e33ea
 typedef struct _State State;
0e33ea
 
0e33ea
@@ -20,84 +19,11 @@ struct _State
0e33ea
   gboolean pass;
0e33ea
 };
0e33ea
 
0e33ea
-struct _ShiftEffect
0e33ea
-{
0e33ea
-  ClutterShaderEffect parent_instance;
0e33ea
-};
0e33ea
-
0e33ea
-struct _ShiftEffectClass
0e33ea
-{
0e33ea
-  ClutterShaderEffectClass parent_class;
0e33ea
-};
0e33ea
-
0e33ea
-typedef struct _ShiftEffect       ShiftEffect;
0e33ea
-typedef struct _ShiftEffectClass  ShiftEffectClass;
0e33ea
-
0e33ea
-#define TYPE_SHIFT_EFFECT        (shift_effect_get_type ())
0e33ea
-
0e33ea
-GType shift_effect_get_type (void);
0e33ea
-
0e33ea
-G_DEFINE_TYPE (ShiftEffect,
0e33ea
-               shift_effect,
0e33ea
-               CLUTTER_TYPE_SHADER_EFFECT);
0e33ea
-
0e33ea
-static void
0e33ea
-shader_paint (ClutterEffect           *effect,
0e33ea
-              ClutterEffectPaintFlags  flags)
0e33ea
-{
0e33ea
-  ClutterShaderEffect *shader = CLUTTER_SHADER_EFFECT (effect);
0e33ea
-  float tex_width;
0e33ea
-  ClutterActor *actor =
0e33ea
-    clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (effect));
0e33ea
-
0e33ea
-  if (g_test_verbose ())
0e33ea
-    g_debug ("shader_paint");
0e33ea
-
0e33ea
-  clutter_shader_effect_set_shader_source (shader,
0e33ea
-    "uniform sampler2D tex;\n"
0e33ea
-    "uniform float step;\n"
0e33ea
-    "void main (void)\n"
0e33ea
-    "{\n"
0e33ea
-    "  cogl_color_out = texture2D(tex, vec2 (cogl_tex_coord_in[0].s + step,\n"
0e33ea
-    "                                        cogl_tex_coord_in[0].t));\n"
0e33ea
-    "}\n");
0e33ea
-
0e33ea
-  tex_width = clutter_actor_get_width (actor);
0e33ea
-
0e33ea
-  clutter_shader_effect_set_uniform (shader, "tex", G_TYPE_INT, 1, 0);
0e33ea
-  clutter_shader_effect_set_uniform (shader, "step", G_TYPE_FLOAT, 1,
0e33ea
-                                     SHIFT_STEP / tex_width);
0e33ea
-
0e33ea
-  CLUTTER_EFFECT_CLASS (shift_effect_parent_class)->paint (effect, flags);
0e33ea
-}
0e33ea
-
0e33ea
-static void
0e33ea
-shader_pick (ClutterEffect           *effect,
0e33ea
-             ClutterEffectPaintFlags  flags)
0e33ea
-{
0e33ea
-  shader_paint (effect, flags);
0e33ea
-}
0e33ea
-
0e33ea
-static void
0e33ea
-shift_effect_class_init (ShiftEffectClass *klass)
0e33ea
-{
0e33ea
-  ClutterEffectClass *shader_class = CLUTTER_EFFECT_CLASS (klass);
0e33ea
-
0e33ea
-  shader_class->paint = shader_paint;
0e33ea
-  shader_class->pick = shader_pick;
0e33ea
-}
0e33ea
-
0e33ea
-static void
0e33ea
-shift_effect_init (ShiftEffect *self)
0e33ea
-{
0e33ea
-}
0e33ea
-
0e33ea
 static const char *test_passes[] = {
0e33ea
   "No covering actor",
0e33ea
   "Invisible covering actor",
0e33ea
   "Clipped covering actor",
0e33ea
   "Blur effect",
0e33ea
-  "Shift effect",
0e33ea
 };
0e33ea
 
0e33ea
 static gboolean
0e33ea
@@ -165,30 +91,10 @@ on_timeout (gpointer data)
0e33ea
           if (g_test_verbose ())
0e33ea
             g_print ("With blur effect:\n");
0e33ea
         }
0e33ea
-      else if (test_num == 4)
0e33ea
-        {
0e33ea
-          if (!clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL))
0e33ea
-            continue;
0e33ea
-
0e33ea
-          clutter_actor_hide (over_actor);
0e33ea
-          clutter_actor_remove_effect_by_name (CLUTTER_ACTOR (state->stage),
0e33ea
-                                               "blur");
0e33ea
-
0e33ea
-          clutter_actor_add_effect_with_name (CLUTTER_ACTOR (state->stage),
0e33ea
-                                              "shift",
0e33ea
-                                              g_object_new (TYPE_SHIFT_EFFECT,
0e33ea
-                                                            NULL));
0e33ea
-
0e33ea
-          if (g_test_verbose ())
0e33ea
-            g_print ("With shift effect:\n");
0e33ea
-        }
0e33ea
 
0e33ea
       for (y = 0; y < ACTORS_Y; y++)
0e33ea
         {
0e33ea
-          if (test_num == 4)
0e33ea
-            x = 1;
0e33ea
-          else
0e33ea
-            x = 0;
0e33ea
+          x = 0;
0e33ea
 
0e33ea
           for (; x < ACTORS_X; x++)
0e33ea
             {
0e33ea
@@ -198,9 +104,6 @@ on_timeout (gpointer data)
0e33ea
 
0e33ea
               pick_x = x * state->actor_width + state->actor_width / 2;
0e33ea
 
0e33ea
-              if (test_num == 4)
0e33ea
-                pick_x -= SHIFT_STEP;
0e33ea
-
0e33ea
               actor =
0e33ea
                 clutter_stage_get_actor_at_pos (CLUTTER_STAGE (state->stage),
0e33ea
                                                 CLUTTER_PICK_ALL,
0e33ea
diff --git a/clutter/tests/conform/meson.build b/clutter/tests/conform/meson.build
0e33ea
index a9f2d7e20c..fffc9014c4 100644
0e33ea
--- a/clutter/tests/conform/meson.build
0e33ea
+++ b/clutter/tests/conform/meson.build
0e33ea
@@ -42,7 +42,6 @@ clutter_conform_tests_deprecated_tests = [
0e33ea
   'behaviours',
0e33ea
   'group',
0e33ea
   'rectangle',
0e33ea
-  'texture',
0e33ea
 ]
0e33ea
 
0e33ea
 clutter_conform_tests = []
0e33ea
diff --git a/clutter/tests/conform/texture.c b/clutter/tests/conform/texture.c
0e33ea
deleted file mode 100644
0e33ea
index 392fd5c47e..0000000000
0e33ea
--- a/clutter/tests/conform/texture.c
0e33ea
+++ /dev/null
0e33ea
@@ -1,84 +0,0 @@
0e33ea
-#define CLUTTER_DISABLE_DEPRECATION_WARNINGS
0e33ea
-#include <clutter/clutter.h>
0e33ea
-#include <string.h>
0e33ea
-
0e33ea
-static CoglHandle
0e33ea
-make_texture (void)
0e33ea
-{
0e33ea
-  guint32 *data = g_malloc (100 * 100 * 4);
0e33ea
-  int x;
0e33ea
-  int y;
0e33ea
-
0e33ea
-  for (y = 0; y < 100; y ++)
0e33ea
-    for (x = 0; x < 100; x++)
0e33ea
-      {
0e33ea
-        if (x < 50 && y < 50)
0e33ea
-          data[y * 100 + x] = 0xff00ff00;
0e33ea
-        else
0e33ea
-          data[y * 100 + x] = 0xff00ffff;
0e33ea
-      }
0e33ea
-  return cogl_texture_new_from_data (100,
0e33ea
-                                     100,
0e33ea
-                                     COGL_TEXTURE_NONE,
0e33ea
-                                     COGL_PIXEL_FORMAT_ARGB_8888,
0e33ea
-                                     COGL_PIXEL_FORMAT_ARGB_8888,
0e33ea
-                                     400,
0e33ea
-                                     (guchar *)data);
0e33ea
-}
0e33ea
-
0e33ea
-static void
0e33ea
-texture_pick_with_alpha (void)
0e33ea
-{
0e33ea
-  ClutterTexture *tex = CLUTTER_TEXTURE (clutter_texture_new ());
0e33ea
-  ClutterStage *stage = CLUTTER_STAGE (clutter_test_get_stage ());
0e33ea
-  ClutterActor *actor;
0e33ea
-
0e33ea
-  clutter_texture_set_cogl_texture (tex, make_texture ());
0e33ea
-
0e33ea
-  clutter_actor_add_child (CLUTTER_ACTOR (stage), CLUTTER_ACTOR (tex));
0e33ea
-
0e33ea
-  clutter_actor_show (CLUTTER_ACTOR (stage));
0e33ea
-
0e33ea
-  if (g_test_verbose ())
0e33ea
-    {
0e33ea
-      g_print ("\nstage = %p\n", stage);
0e33ea
-      g_print ("texture = %p\n\n", tex);
0e33ea
-    }
0e33ea
-
0e33ea
-  clutter_texture_set_pick_with_alpha (tex, TRUE);
0e33ea
-  if (g_test_verbose ())
0e33ea
-    g_print ("Testing with pick-with-alpha enabled:\n");
0e33ea
-
0e33ea
-  /* This should fall through and hit the stage: */
0e33ea
-  actor = clutter_stage_get_actor_at_pos (stage, CLUTTER_PICK_ALL, 10, 10);
0e33ea
-  if (g_test_verbose ())
0e33ea
-    g_print ("actor @ (10, 10) = %p\n", actor);
0e33ea
-  g_assert (actor == CLUTTER_ACTOR (stage));
0e33ea
-
0e33ea
-  /* The rest should hit the texture */
0e33ea
-  actor = clutter_stage_get_actor_at_pos (stage, CLUTTER_PICK_ALL, 90, 10);
0e33ea
-  if (g_test_verbose ())
0e33ea
-    g_print ("actor @ (90, 10) = %p\n", actor);
0e33ea
-  g_assert (actor == CLUTTER_ACTOR (tex));
0e33ea
-  actor = clutter_stage_get_actor_at_pos (stage, CLUTTER_PICK_ALL, 90, 90);
0e33ea
-  if (g_test_verbose ())
0e33ea
-    g_print ("actor @ (90, 90) = %p\n", actor);
0e33ea
-  g_assert (actor == CLUTTER_ACTOR (tex));
0e33ea
-  actor = clutter_stage_get_actor_at_pos (stage, CLUTTER_PICK_ALL, 10, 90);
0e33ea
-  if (g_test_verbose ())
0e33ea
-    g_print ("actor @ (10, 90) = %p\n", actor);
0e33ea
-  g_assert (actor == CLUTTER_ACTOR (tex));
0e33ea
-
0e33ea
-  clutter_texture_set_pick_with_alpha (tex, FALSE);
0e33ea
-  if (g_test_verbose ())
0e33ea
-    g_print ("Testing with pick-with-alpha disabled:\n");
0e33ea
-
0e33ea
-  actor = clutter_stage_get_actor_at_pos (stage, CLUTTER_PICK_ALL, 10, 10);
0e33ea
-  if (g_test_verbose ())
0e33ea
-    g_print ("actor @ (10, 10) = %p\n", actor);
0e33ea
-  g_assert (actor == CLUTTER_ACTOR (tex));
0e33ea
-}
0e33ea
-
0e33ea
-CLUTTER_TEST_SUITE (
0e33ea
-  CLUTTER_TEST_UNIT ("/texture/pick-with-alpha", texture_pick_with_alpha)
0e33ea
-)
0e33ea
diff --git a/src/compositor/meta-surface-actor.c b/src/compositor/meta-surface-actor.c
0e33ea
index ca4ca19a99..814199145a 100644
0e33ea
--- a/src/compositor/meta-surface-actor.c
0e33ea
+++ b/src/compositor/meta-surface-actor.c
0e33ea
@@ -70,38 +70,23 @@ meta_surface_actor_pick (ClutterActor       *actor,
0e33ea
   else
0e33ea
     {
0e33ea
       int n_rects;
0e33ea
-      float *rectangles;
0e33ea
       int i;
0e33ea
-      CoglPipeline *pipeline;
0e33ea
-      CoglContext *ctx;
0e33ea
-      CoglFramebuffer *fb;
0e33ea
-      CoglColor cogl_color;
0e33ea
 
0e33ea
       n_rects = cairo_region_num_rectangles (priv->input_region);
0e33ea
-      rectangles = g_alloca (sizeof (float) * 4 * n_rects);
0e33ea
 
0e33ea
       for (i = 0; i < n_rects; i++)
0e33ea
         {
0e33ea
           cairo_rectangle_int_t rect;
0e33ea
-          int pos = i * 4;
0e33ea
+          ClutterActorBox box;
0e33ea
 
0e33ea
           cairo_region_get_rectangle (priv->input_region, i, &rect);
0e33ea
 
0e33ea
-          rectangles[pos + 0] = rect.x;
0e33ea
-          rectangles[pos + 1] = rect.y;
0e33ea
-          rectangles[pos + 2] = rect.x + rect.width;
0e33ea
-          rectangles[pos + 3] = rect.y + rect.height;
0e33ea
+          box.x1 = rect.x;
0e33ea
+          box.y1 = rect.y;
0e33ea
+          box.x2 = rect.x + rect.width;
0e33ea
+          box.y2 = rect.y + rect.height;
0e33ea
+          clutter_actor_pick_box (actor, &box);
0e33ea
         }
0e33ea
-
0e33ea
-      ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
0e33ea
-      fb = cogl_get_draw_framebuffer ();
0e33ea
-
0e33ea
-      cogl_color_init_from_4ub (&cogl_color, color->red, color->green, color->blue, color->alpha);
0e33ea
-
0e33ea
-      pipeline = cogl_pipeline_new (ctx);
0e33ea
-      cogl_pipeline_set_color (pipeline, &cogl_color);
0e33ea
-      cogl_framebuffer_draw_rectangles (fb, pipeline, rectangles, n_rects);
0e33ea
-      cogl_object_unref (pipeline);
0e33ea
     }
0e33ea
 
0e33ea
   clutter_actor_iter_init (&iter, actor);
0e33ea
-- 
0e33ea
2.29.2
0e33ea
0e33ea
0e33ea
From 254e93de8d60393ca94fa430c0acc6f6a7b9516e Mon Sep 17 00:00:00 2001
0e33ea
From: Iain Lane <laney@debian.org>
0e33ea
Date: Mon, 9 Sep 2019 10:17:22 +0100
0e33ea
Subject: [PATCH 3/4] build: Compile with `-ffloat-store` on x86 (32 bit)
0e33ea
0e33ea
GCC's manpage says that this flag does the following:
0e33ea
0e33ea
  Do not store floating-point variables in registers, and inhibit other
0e33ea
  options that might change whether a floating-point value is taken from
0e33ea
  a register or memory.
0e33ea
0e33ea
  This option prevents undesirable excess precision on machines such as
0e33ea
  the 68000 where the floating registers (of the 68881) keep more
0e33ea
  precision than a "double" is supposed to have.  Similarly for the x86
0e33ea
  architecture.  For most programs, the excess precision does only good,
0e33ea
  but a few programs rely on the precise definition of IEEE floating
0e33ea
  point.
0e33ea
0e33ea
We rely on this behaviour in our fork of clutter. When performing
0e33ea
floating point computations on x86, we are getting the wrong results
0e33ea
because of this architecture's use of the CPU's extended (x87, non-IEEE
0e33ea
confirming) precision by default. If we enable `-ffloat-store` here,
0e33ea
then we'll get the same results everywhere by storing into variables
0e33ea
instead of registers. This does not remove the need to be correct when
0e33ea
handling floats, but it does mean we don't need to be more correct than
0e33ea
the IEEE spec requires.
0e33ea
0e33ea
https://gitlab.gnome.org/GNOME/mutter/merge_requests/785
0e33ea
---
0e33ea
 meson.build | 3 +++
0e33ea
 1 file changed, 3 insertions(+)
0e33ea
0e33ea
diff --git a/meson.build b/meson.build
0e33ea
index 8ef592bc58..e1edb78ba7 100644
0e33ea
--- a/meson.build
0e33ea
+++ b/meson.build
0e33ea
@@ -267,6 +267,9 @@ foreach function : required_functions
0e33ea
   endif
0e33ea
 endforeach
0e33ea
 
0e33ea
+if host_machine.cpu_family() == 'x86'
0e33ea
+  add_project_arguments('-ffloat-store', language: 'c')
0e33ea
+endif
0e33ea
 add_project_arguments('-D_GNU_SOURCE', language: 'c')
0e33ea
 
0e33ea
 all_warnings = [
0e33ea
-- 
0e33ea
2.29.2
0e33ea
0e33ea
0e33ea
From 2d42caef14772984344e62ce40957d3b40e1f2b6 Mon Sep 17 00:00:00 2001
0e33ea
From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= <mail@3v1n0.net>
0e33ea
Date: Thu, 19 Sep 2019 11:27:50 +0200
0e33ea
Subject: [PATCH 4/4] stage: Compute view perspective when parameters changed
0e33ea
0e33ea
Clutter stage used to compute the initial projection using a fixed z
0e33ea
translation which wasn't matching the one we computed in
0e33ea
calculate_z_translation().
0e33ea
This caused to have a wrong initial projection on startup which was then
0e33ea
correctly recomputed only at the first paint.
0e33ea
0e33ea
However, since this calculation doesn't depend on view, but only on viewport
0e33ea
size, perspective's fovy and z_near we can safely do this at startup and
0e33ea
only when any of those parameters change.
0e33ea
0e33ea
Then we can move the computation out _clutter_stage_maybe_setup_viewport()
0e33ea
since the cogl framebuffer viewport sizes aren't affecting this.
0e33ea
0e33ea
Fixes https://gitlab.gnome.org/GNOME/gnome-shell/issues/1639
0e33ea
https://gitlab.gnome.org/GNOME/mutter/merge_requests/803
0e33ea
---
0e33ea
 clutter/clutter/clutter-stage.c | 104 +++++++++++++++-----------------
0e33ea
 1 file changed, 47 insertions(+), 57 deletions(-)
0e33ea
0e33ea
diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c
0e33ea
index 7d88d5752f..0cfa87486e 100644
0e33ea
--- a/clutter/clutter/clutter-stage.c
0e33ea
+++ b/clutter/clutter/clutter-stage.c
0e33ea
@@ -234,6 +234,7 @@ static void capture_view_into (ClutterStage          *stage,
0e33ea
                                cairo_rectangle_int_t *rect,
0e33ea
                                uint8_t               *data,
0e33ea
                                int                    stride);
0e33ea
+static void clutter_stage_update_view_perspective (ClutterStage *stage);
0e33ea
 
0e33ea
 static void clutter_container_iface_init (ClutterContainerIface *iface);
0e33ea
 
0e33ea
@@ -2492,29 +2493,6 @@ clutter_stage_init (ClutterStage *self)
0e33ea
   clutter_actor_set_background_color (CLUTTER_ACTOR (self),
0e33ea
                                       &default_stage_color);
0e33ea
 
0e33ea
-  priv->perspective.fovy   = 60.0; /* 60 Degrees */
0e33ea
-  priv->perspective.aspect = (float) geom.width / (float) geom.height;
0e33ea
-  priv->perspective.z_near = 0.1;
0e33ea
-  priv->perspective.z_far  = 100.0;
0e33ea
-
0e33ea
-  cogl_matrix_init_identity (&priv->projection);
0e33ea
-  cogl_matrix_perspective (&priv->projection,
0e33ea
-                           priv->perspective.fovy,
0e33ea
-                           priv->perspective.aspect,
0e33ea
-                           priv->perspective.z_near,
0e33ea
-                           priv->perspective.z_far);
0e33ea
-  cogl_matrix_get_inverse (&priv->projection,
0e33ea
-                           &priv->inverse_projection);
0e33ea
-  cogl_matrix_init_identity (&priv->view);
0e33ea
-  cogl_matrix_view_2d_in_perspective (&priv->view,
0e33ea
-                                      priv->perspective.fovy,
0e33ea
-                                      priv->perspective.aspect,
0e33ea
-                                      priv->perspective.z_near,
0e33ea
-                                      50, /* distance to 2d plane */
0e33ea
-                                      geom.width,
0e33ea
-                                      geom.height);
0e33ea
-
0e33ea
-
0e33ea
   /* FIXME - remove for 2.0 */
0e33ea
   priv->fog.z_near = 1.0;
0e33ea
   priv->fog.z_far  = 2.0;
0e33ea
@@ -2682,6 +2660,7 @@ clutter_stage_set_perspective (ClutterStage       *stage,
0e33ea
   priv->has_custom_perspective = TRUE;
0e33ea
 
0e33ea
   clutter_stage_set_perspective_internal (stage, perspective);
0e33ea
+  clutter_stage_update_view_perspective (stage);
0e33ea
 }
0e33ea
 
0e33ea
 /**
0e33ea
@@ -2808,6 +2787,7 @@ _clutter_stage_set_viewport (ClutterStage *stage,
0e33ea
   priv->viewport[2] = width;
0e33ea
   priv->viewport[3] = height;
0e33ea
 
0e33ea
+  clutter_stage_update_view_perspective (stage);
0e33ea
   _clutter_stage_dirty_viewport (stage);
0e33ea
 
0e33ea
   queue_full_redraw (stage);
0e33ea
@@ -3788,6 +3768,50 @@ calculate_z_translation (float z_near)
0e33ea
        + z_near;
0e33ea
 }
0e33ea
 
0e33ea
+static void
0e33ea
+clutter_stage_update_view_perspective (ClutterStage *stage)
0e33ea
+{
0e33ea
+  ClutterStagePrivate *priv = stage->priv;
0e33ea
+  ClutterPerspective perspective;
0e33ea
+  float z_2d;
0e33ea
+
0e33ea
+  perspective = priv->perspective;
0e33ea
+
0e33ea
+  /* Ideally we want to regenerate the perspective matrix whenever
0e33ea
+   * the size changes but if the user has provided a custom matrix
0e33ea
+   * then we don't want to override it */
0e33ea
+  if (!priv->has_custom_perspective)
0e33ea
+    {
0e33ea
+      perspective.fovy = 60.0; /* 60 Degrees */
0e33ea
+      perspective.z_near = 0.1;
0e33ea
+      perspective.aspect = priv->viewport[2] / priv->viewport[3];
0e33ea
+      z_2d = calculate_z_translation (perspective.z_near);
0e33ea
+
0e33ea
+      /* NB: z_2d is only enough room for 85% of the stage_height between
0e33ea
+       * the stage and the z_near plane. For behind the stage plane we
0e33ea
+       * want a more consistent gap of 10 times the stage_height before
0e33ea
+       * hitting the far plane so we calculate that relative to the final
0e33ea
+       * height of the stage plane at the z_2d_distance we got... */
0e33ea
+      perspective.z_far = z_2d +
0e33ea
+        tanf (_DEG_TO_RAD (perspective.fovy / 2.0f)) * z_2d * 20.0f;
0e33ea
+
0e33ea
+      clutter_stage_set_perspective_internal (stage, &perspective);
0e33ea
+    }
0e33ea
+  else
0e33ea
+    {
0e33ea
+      z_2d = calculate_z_translation (perspective.z_near);
0e33ea
+    }
0e33ea
+
0e33ea
+  cogl_matrix_init_identity (&priv->view);
0e33ea
+  cogl_matrix_view_2d_in_perspective (&priv->view,
0e33ea
+                                      perspective.fovy,
0e33ea
+                                      perspective.aspect,
0e33ea
+                                      perspective.z_near,
0e33ea
+                                      z_2d,
0e33ea
+                                      priv->viewport[2],
0e33ea
+                                      priv->viewport[3]);
0e33ea
+}
0e33ea
+
0e33ea
 void
0e33ea
 _clutter_stage_maybe_setup_viewport (ClutterStage     *stage,
0e33ea
                                      ClutterStageView *view)
0e33ea
@@ -3797,7 +3821,6 @@ _clutter_stage_maybe_setup_viewport (ClutterStage     *stage,
0e33ea
   if (clutter_stage_view_is_dirty_viewport (view))
0e33ea
     {
0e33ea
       cairo_rectangle_int_t view_layout;
0e33ea
-      ClutterPerspective perspective;
0e33ea
       float fb_scale;
0e33ea
       float viewport_offset_x;
0e33ea
       float viewport_offset_y;
0e33ea
@@ -3805,7 +3828,6 @@ _clutter_stage_maybe_setup_viewport (ClutterStage     *stage,
0e33ea
       float viewport_y;
0e33ea
       float viewport_width;
0e33ea
       float viewport_height;
0e33ea
-      float z_2d;
0e33ea
 
0e33ea
       CLUTTER_NOTE (PAINT,
0e33ea
                     "Setting up the viewport { w:%f, h:%f }",
0e33ea
@@ -3825,38 +3847,6 @@ _clutter_stage_maybe_setup_viewport (ClutterStage     *stage,
0e33ea
       clutter_stage_view_set_viewport (view,
0e33ea
                                        viewport_x, viewport_y,
0e33ea
                                        viewport_width, viewport_height);
0e33ea
-
0e33ea
-      perspective = priv->perspective;
0e33ea
-
0e33ea
-      /* Ideally we want to regenerate the perspective matrix whenever
0e33ea
-       * the size changes but if the user has provided a custom matrix
0e33ea
-       * then we don't want to override it */
0e33ea
-      if (!priv->has_custom_perspective)
0e33ea
-        {
0e33ea
-          perspective.aspect = priv->viewport[2] / priv->viewport[3];
0e33ea
-          z_2d = calculate_z_translation (perspective.z_near);
0e33ea
-
0e33ea
-          /* NB: z_2d is only enough room for 85% of the stage_height between
0e33ea
-           * the stage and the z_near plane. For behind the stage plane we
0e33ea
-           * want a more consistent gap of 10 times the stage_height before
0e33ea
-           * hitting the far plane so we calculate that relative to the final
0e33ea
-           * height of the stage plane at the z_2d_distance we got... */
0e33ea
-          perspective.z_far = z_2d +
0e33ea
-            tanf (_DEG_TO_RAD (perspective.fovy / 2.0f)) * z_2d * 20.0f;
0e33ea
-
0e33ea
-          clutter_stage_set_perspective_internal (stage, &perspective);
0e33ea
-        }
0e33ea
-      else
0e33ea
-        z_2d = calculate_z_translation (perspective.z_near);
0e33ea
-
0e33ea
-      cogl_matrix_init_identity (&priv->view);
0e33ea
-      cogl_matrix_view_2d_in_perspective (&priv->view,
0e33ea
-                                          perspective.fovy,
0e33ea
-                                          perspective.aspect,
0e33ea
-                                          perspective.z_near,
0e33ea
-                                          z_2d,
0e33ea
-                                          priv->viewport[2],
0e33ea
-                                          priv->viewport[3]);
0e33ea
     }
0e33ea
 
0e33ea
   if (clutter_stage_view_is_dirty_projection (view))
0e33ea
-- 
0e33ea
2.29.2
0e33ea