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