From c9fbdeb516f8ab3f992c1e6906034cf76b042497 Mon Sep 17 00:00:00 2001 From: Jerry Date: Mon, 9 Sep 2019 18:13:24 +0800 Subject: [PATCH] add package: mutter-performance --- mutter-performance/189.diff | 1692 +++++++++++++++++++++++++++++++++++ mutter-performance/429.diff | 218 +++++ mutter-performance/724.diff | 149 +++ mutter-performance/PKGBUILD | 308 +++++++ 4 files changed, 2367 insertions(+) create mode 100644 mutter-performance/189.diff create mode 100644 mutter-performance/429.diff create mode 100644 mutter-performance/724.diff create mode 100644 mutter-performance/PKGBUILD diff --git a/mutter-performance/189.diff b/mutter-performance/189.diff new file mode 100644 index 0000000..a777613 --- /dev/null +++ b/mutter-performance/189.diff @@ -0,0 +1,1692 @@ +diff --git a/clutter/clutter/clutter-actor-private.h b/clutter/clutter/clutter-actor-private.h +index c44f6342f..9bf1a3049 100644 +--- a/clutter/clutter/clutter-actor-private.h ++++ b/clutter/clutter/clutter-actor-private.h +@@ -297,8 +297,6 @@ const gchar * _clutter_actor_get_debug_name + void _clutter_actor_push_clone_paint (void); + void _clutter_actor_pop_clone_paint (void); + +-guint32 _clutter_actor_get_pick_id (ClutterActor *self); +- + void _clutter_actor_shader_pre_paint (ClutterActor *actor, + gboolean repeat); + void _clutter_actor_shader_post_paint (ClutterActor *actor); +diff --git a/clutter/clutter/clutter-actor.c b/clutter/clutter/clutter-actor.c +index 803f76aae..f77d73a95 100644 +--- a/clutter/clutter/clutter-actor.c ++++ b/clutter/clutter/clutter-actor.c +@@ -729,8 +729,6 @@ struct _ClutterActorPrivate + + gchar *name; /* a non-unique name, used for debugging */ + +- gint32 pick_id; /* per-stage unique id, used for picking */ +- + /* a back-pointer to the Pango context that we can use + * to create pre-configured PangoLayout + */ +@@ -1280,6 +1278,129 @@ clutter_actor_verify_map_state (ClutterActor *self) + + #endif /* CLUTTER_ENABLE_DEBUG */ + ++static gboolean ++_clutter_actor_transform_local_box_to_stage (ClutterActor *self, ++ const ClutterActorBox *box, ++ ClutterPoint vertices[4]) ++{ ++ ClutterActor *stage = _clutter_actor_get_stage_internal (self); ++ CoglMatrix stage_transform, inv_stage_transform; ++ CoglMatrix modelview, transform_to_stage; ++ int v; ++ ++ g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE); ++ g_return_val_if_fail (box != NULL, FALSE); ++ ++ g_return_val_if_fail (stage != NULL, FALSE); ++ g_return_val_if_fail (box->x1 <= box->x2, FALSE); ++ g_return_val_if_fail (box->y1 <= box->y2, FALSE); ++ ++ /* Below is generally equivalent to: ++ * ++ * _clutter_actor_get_relative_transformation_matrix (self, ++ * stage, ++ * &transform_to_stage); ++ * ++ * but we do it the hard way here instead so as to accurately include any ++ * cogl transformations that an actor's paint function might have added in. ++ * Those additional transformations are only known to cogl matrices and not ++ * known to the clutter getter like above. So this way we more accurately ++ * represent what's really getting painted. ++ */ ++ clutter_actor_get_transform (stage, &stage_transform); ++ if (!cogl_matrix_get_inverse (&stage_transform, &inv_stage_transform)) ++ return FALSE; ++ cogl_get_modelview_matrix (&modelview); ++ cogl_matrix_multiply (&transform_to_stage, &inv_stage_transform, &modelview); ++ ++ vertices[0].x = box->x1; ++ vertices[0].y = box->y1; ++ ++ vertices[1].x = box->x2; ++ vertices[1].y = box->y1; ++ ++ vertices[2].x = box->x2; ++ vertices[2].y = box->y2; ++ ++ vertices[3].x = box->x1; ++ vertices[3].y = box->y2; ++ ++ for (v = 0; v < 4; v++) ++ { ++ gfloat z = 0.f; ++ gfloat w = 1.f; ++ ++ cogl_matrix_transform_point (&transform_to_stage, ++ &vertices[v].x, ++ &vertices[v].y, ++ &z, ++ &w); ++ } ++ ++ return TRUE; ++} ++ ++/** ++ * clutter_actor_pick_box: ++ * @self: The #ClutterActor being "pick" painted. ++ * @box: A rectangle in the actor's own local coordinates. ++ * ++ * Logs (does a virtual paint of) a rectangle for picking. Note that @box is ++ * in the actor's own local coordinates, so is usually {0,0,width,height} ++ * to include the whole actor. That is unless the actor has a shaped input ++ * region in which case you may wish to log the (multiple) smaller rectangles ++ * that make up the input region. ++ */ ++void ++clutter_actor_pick_box (ClutterActor *self, ++ const ClutterActorBox *box) ++{ ++ ClutterActor *stage; ++ ClutterPoint vertices[4]; ++ ++ g_return_if_fail (CLUTTER_IS_ACTOR (self)); ++ g_return_if_fail (box != NULL); ++ ++ /* An empty box to a "pick" paint means to paint nothing at all. */ ++ if (box->x1 >= box->x2 || box->y1 >= box->y2) ++ return; ++ ++ stage = _clutter_actor_get_stage_internal (self); ++ ++ if (_clutter_actor_transform_local_box_to_stage (self, box, vertices)) ++ _clutter_stage_log_pick (CLUTTER_STAGE (stage), vertices, self); ++} ++ ++static gboolean ++_clutter_actor_push_pick_clip (ClutterActor *self, ++ const ClutterActorBox *clip) ++{ ++ ClutterActor *stage; ++ ClutterPoint vertices[4]; ++ ++ g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE); ++ g_return_val_if_fail (clip != NULL, FALSE); ++ ++ stage = _clutter_actor_get_stage_internal (self); ++ ++ if (!_clutter_actor_transform_local_box_to_stage (self, clip, vertices)) ++ return FALSE; ++ ++ _clutter_stage_push_pick_clip (CLUTTER_STAGE (stage), vertices); ++ return TRUE; ++} ++ ++static void ++_clutter_actor_pop_pick_clip (ClutterActor *self) ++{ ++ ClutterActor *stage; ++ ++ g_return_if_fail (CLUTTER_IS_ACTOR (self)); ++ ++ stage = _clutter_actor_get_stage_internal (self); ++ _clutter_stage_pop_pick_clip (CLUTTER_STAGE (stage)); ++} ++ + static void + clutter_actor_set_mapped (ClutterActor *self, + gboolean mapped) +@@ -1508,8 +1629,7 @@ clutter_actor_update_map_state (ClutterActor *self, + static void + clutter_actor_real_map (ClutterActor *self) + { +- ClutterActorPrivate *priv = self->priv; +- ClutterActor *stage, *iter; ++ ClutterActor *iter; + + g_assert (!CLUTTER_ACTOR_IS_MAPPED (self)); + +@@ -1520,13 +1640,6 @@ clutter_actor_real_map (ClutterActor *self) + + self->priv->needs_paint_volume_update = TRUE; + +- stage = _clutter_actor_get_stage_internal (self); +- priv->pick_id = _clutter_stage_acquire_pick_id (CLUTTER_STAGE (stage), self); +- +- CLUTTER_NOTE (ACTOR, "Pick id '%d' for actor '%s'", +- priv->pick_id, +- _clutter_actor_get_debug_name (self)); +- + clutter_actor_ensure_resource_scale (self); + + /* notify on parent mapped before potentially mapping +@@ -1631,11 +1744,6 @@ clutter_actor_real_unmap (ClutterActor *self) + + stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (self)); + +- if (stage != NULL) +- _clutter_stage_release_pick_id (stage, priv->pick_id); +- +- priv->pick_id = -1; +- + if (stage != NULL && + clutter_stage_get_key_focus (stage) == self) + { +@@ -2254,46 +2362,14 @@ static void + clutter_actor_real_pick (ClutterActor *self, + const ClutterColor *color) + { +- CoglFramebuffer *framebuffer = cogl_get_draw_framebuffer (); +- +- /* the default implementation is just to paint a rectangle +- * with the same size of the actor using the passed color +- */ + if (clutter_actor_should_pick_paint (self)) + { +- static CoglPipeline *default_pick_pipeline = NULL; +- ClutterActorBox box = { 0, }; +- CoglPipeline *pick_pipeline; +- float width, height; +- +- if (G_UNLIKELY (default_pick_pipeline == NULL)) +- { +- CoglContext *ctx = +- clutter_backend_get_cogl_context (clutter_get_default_backend ()); +- +- default_pick_pipeline = cogl_pipeline_new (ctx); +- } +- +- g_assert (default_pick_pipeline != NULL); +- pick_pipeline = cogl_pipeline_copy (default_pick_pipeline); +- +- clutter_actor_get_allocation_box (self, &box); +- +- width = box.x2 - box.x1; +- height = box.y2 - box.y1; +- +- cogl_pipeline_set_color4ub (pick_pipeline, +- color->red, +- color->green, +- color->blue, +- color->alpha); ++ ClutterActorBox box = {0, ++ 0, ++ clutter_actor_get_width (self), ++ clutter_actor_get_height (self)}; + +- cogl_framebuffer_draw_rectangle (framebuffer, +- pick_pipeline, +- 0, 0, +- width, height); +- +- cogl_object_unref (pick_pipeline); ++ clutter_actor_pick_box (self, &box); + } + + /* XXX - this thoroughly sucks, but we need to maintain compatibility +@@ -3584,15 +3660,6 @@ _clutter_actor_update_last_paint_volume (ClutterActor *self) + priv->last_paint_volume_valid = TRUE; + } + +-guint32 +-_clutter_actor_get_pick_id (ClutterActor *self) +-{ +- if (self->priv->pick_id < 0) +- return 0; +- +- return self->priv->pick_id; +-} +- + /* This is the same as clutter_actor_add_effect except that it doesn't + queue a redraw and it doesn't notify on the effect property */ + static void +@@ -3824,6 +3891,7 @@ clutter_actor_paint (ClutterActor *self) + { + ClutterActorPrivate *priv; + ClutterPickMode pick_mode; ++ ClutterActorBox clip; + gboolean clip_set = FALSE; + ClutterStage *stage; + +@@ -3917,24 +3985,38 @@ clutter_actor_paint (ClutterActor *self) + + if (priv->has_clip) + { +- CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage); +- cogl_framebuffer_push_rectangle_clip (fb, +- priv->clip.origin.x, +- priv->clip.origin.y, +- priv->clip.origin.x + priv->clip.size.width, +- priv->clip.origin.y + priv->clip.size.height); ++ clip.x1 = priv->clip.origin.x; ++ clip.y1 = priv->clip.origin.y; ++ clip.x2 = priv->clip.origin.x + priv->clip.size.width; ++ clip.y2 = priv->clip.origin.y + priv->clip.size.height; + clip_set = TRUE; + } + else if (priv->clip_to_allocation) + { +- CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage); +- gfloat width, height; ++ clip.x1 = 0.f; ++ clip.y1 = 0.f; ++ clip.x2 = priv->allocation.x2 - priv->allocation.x1; ++ clip.y2 = priv->allocation.y2 - priv->allocation.y1; ++ clip_set = TRUE; ++ } + +- width = priv->allocation.x2 - priv->allocation.x1; +- height = priv->allocation.y2 - priv->allocation.y1; ++ if (clip_set) ++ { ++ if (pick_mode == CLUTTER_PICK_NONE) ++ { ++ CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage); + +- cogl_framebuffer_push_rectangle_clip (fb, 0, 0, width, height); +- clip_set = TRUE; ++ cogl_framebuffer_push_rectangle_clip (fb, ++ clip.x1, ++ clip.y1, ++ clip.x2, ++ clip.y2); ++ } ++ else ++ { ++ if (!_clutter_actor_push_pick_clip (self, &clip)) ++ clip_set = FALSE; ++ } + } + + if (pick_mode == CLUTTER_PICK_NONE) +@@ -4015,9 +4097,16 @@ clutter_actor_paint (ClutterActor *self) + done: + if (clip_set) + { +- CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage); ++ if (pick_mode == CLUTTER_PICK_NONE) ++ { ++ CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage); + +- cogl_framebuffer_pop_clip (fb); ++ cogl_framebuffer_pop_clip (fb); ++ } ++ else ++ { ++ _clutter_actor_pop_pick_clip (self); ++ } + } + + cogl_pop_matrix (); +@@ -4088,11 +4177,12 @@ clutter_actor_continue_paint (ClutterActor *self) + { + ClutterColor col = { 0, }; + +- _clutter_id_to_color (_clutter_actor_get_pick_id (self), &col); +- +- /* Actor will then paint silhouette of itself in supplied +- * color. See clutter_stage_get_actor_at_pos() for where +- * picking is enabled. ++ /* The actor will log a silhouette of itself to the stage pick log. ++ * Note that the picking color is no longer used as the "log" instead ++ * keeps a weak pointer to the actor itself. But we keep the color ++ * parameter for now so as to maintain ABI compatibility. The color ++ * parameter can be removed when someone feels like breaking the ABI ++ * along with gnome-shell. + * + * XXX:2.0 - Call the pick() virtual directly + */ +@@ -8598,8 +8688,6 @@ clutter_actor_init (ClutterActor *self) + + self->priv = priv = clutter_actor_get_instance_private (self); + +- priv->pick_id = -1; +- + priv->opacity = 0xff; + priv->show_on_set_parent = TRUE; + priv->resource_scale = -1.0f; +diff --git a/clutter/clutter/clutter-actor.h b/clutter/clutter/clutter-actor.h +index 7d2168af1..b1abff86d 100644 +--- a/clutter/clutter/clutter-actor.h ++++ b/clutter/clutter/clutter-actor.h +@@ -902,6 +902,10 @@ void clutter_actor_bind_model_with_properties + const char *first_model_property, + ...); + ++CLUTTER_EXPORT ++void clutter_actor_pick_box (ClutterActor *self, ++ const ClutterActorBox *box); ++ + G_END_DECLS + + #endif /* __CLUTTER_ACTOR_H__ */ +diff --git a/clutter/clutter/clutter-base-types.c b/clutter/clutter/clutter-base-types.c +index aeb25c90e..0d98c4814 100644 +--- a/clutter/clutter/clutter-base-types.c ++++ b/clutter/clutter/clutter-base-types.c +@@ -570,6 +570,119 @@ G_DEFINE_BOXED_TYPE_WITH_CODE (ClutterPoint, clutter_point, + clutter_point_free, + CLUTTER_REGISTER_INTERVAL_PROGRESS (clutter_point_progress)) + ++static int ++clutter_point_compare_line (const ClutterPoint *p, ++ const ClutterPoint *a, ++ const ClutterPoint *b) ++{ ++ /* ++ * Given two vectors ab and ap: ++ * ab = (x1, y1, 0) ++ * ap = (x2, y2, 0) ++ * their cross product is a vector: ++ * ab x ap = (0, 0, x1 * y2 - y1 * x2) ++ */ ++ float x1 = b->x - a->x; ++ float y1 = b->y - a->y; ++ float x2 = p->x - a->x; ++ float y2 = p->y - a->y; ++ float cross_z = x1 * y2 - y1 * x2; ++ ++ /* ++ * The cross product is also proportional to the sine of the angle between ++ * the vectors, so its sign tells us the sign of the angle between the two ++ * vectors. That is whether p is left of or right of line ab. ++ */ ++ if (cross_z > 0.f) ++ return +1; ++ else if (cross_z < 0.f) ++ return -1; ++ else ++ return 0; ++} ++ ++static int ++clutter_point_compare_polygon (const ClutterPoint *point, ++ const ClutterPoint *vertices, ++ unsigned int nvertices) ++{ ++ unsigned int i; ++ int first_side = 0; ++ gboolean on_an_edge = FALSE; ++ ++ g_return_val_if_fail (nvertices >= 3, +1); ++ ++ for (i = 0; i < nvertices; i++) ++ { ++ int side = clutter_point_compare_line (point, ++ &vertices[i], ++ &vertices[(i + 1) % nvertices]); ++ ++ if (side) ++ { ++ if (!first_side) ++ first_side = side; ++ else if (side != first_side) ++ return +1; /* outside */ ++ } ++ else ++ { ++ on_an_edge = TRUE; ++ } ++ } ++ ++ if (first_side) ++ { ++ return on_an_edge ? 0 : -1; /* on an edge or completely inside */ ++ } ++ else ++ { ++ /* The point was neither inside nor outside any edges so we have an empty ++ * polygon. Therefore if the vertices match the point we'll at least call ++ * that touching. Otherwise the point is outside. ++ */ ++ return point->x == vertices[0].x && point->y == vertices[0].y ? 0 : +1; ++ } ++} ++ ++/** ++ * clutter_point_inside_polygon: ++ * @point: a #ClutterPoint to test ++ * @vertices: array of vertices of the polygon ++ * @nvertices: number of vertices in array @vertices ++ * ++ * Determines whether a point is inside the convex polygon provided, and not ++ * on any of its edges or vertices. ++ * ++ * Return value: true if @point is inside the polygon ++ */ ++gboolean ++clutter_point_inside_polygon (const ClutterPoint *point, ++ const ClutterPoint *vertices, ++ unsigned int nvertices) ++{ ++ return clutter_point_compare_polygon (point, vertices, nvertices) < 0; ++} ++ ++/** ++ * clutter_point_touches_polygon: ++ * @point: a #ClutterPoint to test ++ * @vertices: array of vertices of the polygon ++ * @nvertices: number of vertices in array @vertices ++ * ++ * Determines whether a point is on the convex polygon provided, including ++ * on any of its edges or vertices. ++ * ++ * Return value: true if @point is on the polygon ++ */ ++gboolean ++clutter_point_touches_polygon (const ClutterPoint *point, ++ const ClutterPoint *vertices, ++ unsigned int nvertices) ++{ ++ return clutter_point_compare_polygon (point, vertices, nvertices) <= 0; ++} ++ + + + /* +diff --git a/clutter/clutter/clutter-debug.h b/clutter/clutter/clutter-debug.h +index 2462385f6..3744c648f 100644 +--- a/clutter/clutter/clutter-debug.h ++++ b/clutter/clutter/clutter-debug.h +@@ -29,8 +29,7 @@ typedef enum + + typedef enum + { +- CLUTTER_DEBUG_NOP_PICKING = 1 << 0, +- CLUTTER_DEBUG_DUMP_PICK_BUFFERS = 1 << 1 ++ CLUTTER_DEBUG_NOP_PICKING = 1 << 0 + } ClutterPickDebugFlag; + + typedef enum +diff --git a/clutter/clutter/clutter-main.c b/clutter/clutter/clutter-main.c +index 2bb97bb66..87b457d3e 100644 +--- a/clutter/clutter/clutter-main.c ++++ b/clutter/clutter/clutter-main.c +@@ -128,7 +128,6 @@ static const GDebugKey clutter_debug_keys[] = { + + static const GDebugKey clutter_pick_debug_keys[] = { + { "nop-picking", CLUTTER_DEBUG_NOP_PICKING }, +- { "dump-pick-buffers", CLUTTER_DEBUG_DUMP_PICK_BUFFERS }, + }; + + static const GDebugKey clutter_paint_debug_keys[] = { +@@ -400,125 +399,6 @@ clutter_disable_accessibility (void) + clutter_enable_accessibility = FALSE; + } + +-void +-_clutter_id_to_color (guint id_, +- ClutterColor *col) +-{ +- ClutterMainContext *ctx; +- gint red, green, blue; +- +- ctx = _clutter_context_get_default (); +- +- if (ctx->fb_g_mask == 0) +- { +- /* Figure out framebuffer masks used for pick */ +- cogl_get_bitmasks (&ctx->fb_r_mask, +- &ctx->fb_g_mask, +- &ctx->fb_b_mask, NULL); +- +- ctx->fb_r_mask_used = ctx->fb_r_mask; +- ctx->fb_g_mask_used = ctx->fb_g_mask; +- ctx->fb_b_mask_used = ctx->fb_b_mask; +- +- /* XXX - describe what "fuzzy picking" is */ +- if (clutter_use_fuzzy_picking) +- { +- ctx->fb_r_mask_used--; +- ctx->fb_g_mask_used--; +- ctx->fb_b_mask_used--; +- } +- } +- +- /* compute the numbers we'll store in the components */ +- red = (id_ >> (ctx->fb_g_mask_used+ctx->fb_b_mask_used)) +- & (0xff >> (8-ctx->fb_r_mask_used)); +- green = (id_ >> ctx->fb_b_mask_used) +- & (0xff >> (8-ctx->fb_g_mask_used)); +- blue = (id_) +- & (0xff >> (8-ctx->fb_b_mask_used)); +- +- /* shift left bits a bit and add one, this circumvents +- * at least some potential rounding errors in GL/GLES +- * driver / hw implementation. +- */ +- if (ctx->fb_r_mask_used != ctx->fb_r_mask) +- red = red * 2; +- if (ctx->fb_g_mask_used != ctx->fb_g_mask) +- green = green * 2; +- if (ctx->fb_b_mask_used != ctx->fb_b_mask) +- blue = blue * 2; +- +- /* shift up to be full 8bit values */ +- red = (red << (8 - ctx->fb_r_mask)) | (0x7f >> (ctx->fb_r_mask_used)); +- green = (green << (8 - ctx->fb_g_mask)) | (0x7f >> (ctx->fb_g_mask_used)); +- blue = (blue << (8 - ctx->fb_b_mask)) | (0x7f >> (ctx->fb_b_mask_used)); +- +- col->red = red; +- col->green = green; +- col->blue = blue; +- col->alpha = 0xff; +- +- /* XXX: We rotate the nibbles of the colors here so that there is a +- * visible variation between colors of sequential actor identifiers; +- * otherwise pick buffers dumped to an image will pretty much just look +- * black. +- */ +- if (G_UNLIKELY (clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS)) +- { +- col->red = (col->red << 4) | (col->red >> 4); +- col->green = (col->green << 4) | (col->green >> 4); +- col->blue = (col->blue << 4) | (col->blue >> 4); +- } +-} +- +-guint +-_clutter_pixel_to_id (guchar pixel[4]) +-{ +- ClutterMainContext *ctx; +- gint red, green, blue; +- guint retval; +- +- ctx = _clutter_context_get_default (); +- +- /* reduce the pixel components to the number of bits actually used of the +- * 8bits. +- */ +- if (G_UNLIKELY (clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS)) +- { +- guchar tmp; +- +- /* XXX: In _clutter_id_to_color we rotated the nibbles of the colors so +- * that there is a visible variation between colors of sequential actor +- * identifiers (otherwise pick buffers dumped to an image will pretty +- * much just look black.) Here we reverse that rotation. +- */ +- tmp = ((pixel[0] << 4) | (pixel[0] >> 4)); +- red = tmp >> (8 - ctx->fb_r_mask); +- tmp = ((pixel[1] << 4) | (pixel[1] >> 4)); +- green = tmp >> (8 - ctx->fb_g_mask); +- tmp = ((pixel[2] << 4) | (pixel[2] >> 4)); +- blue = tmp >> (8 - ctx->fb_b_mask); +- } +- else +- { +- red = pixel[0] >> (8 - ctx->fb_r_mask); +- green = pixel[1] >> (8 - ctx->fb_g_mask); +- blue = pixel[2] >> (8 - ctx->fb_b_mask); +- } +- +- /* divide potentially by two if 'fuzzy' */ +- red = red >> (ctx->fb_r_mask - ctx->fb_r_mask_used); +- green = green >> (ctx->fb_g_mask - ctx->fb_g_mask_used); +- blue = blue >> (ctx->fb_b_mask - ctx->fb_b_mask_used); +- +- /* combine the correct per component values into the final id */ +- retval = blue +- + (green << ctx->fb_b_mask_used) +- + (red << (ctx->fb_b_mask_used + ctx->fb_g_mask_used)); +- +- return retval; +-} +- + static CoglPangoFontMap * + clutter_context_get_pango_fontmap (void) + { +diff --git a/clutter/clutter/clutter-private.h b/clutter/clutter/clutter-private.h +index cdda7e4c4..79664c650 100644 +--- a/clutter/clutter/clutter-private.h ++++ b/clutter/clutter/clutter-private.h +@@ -202,11 +202,7 @@ gboolean _clutter_feature_init (GError **error); + gboolean _clutter_diagnostic_enabled (void); + void _clutter_diagnostic_message (const char *fmt, ...) G_GNUC_PRINTF (1, 2); + +-/* Picking code */ +-guint _clutter_pixel_to_id (guchar pixel[4]); +-void _clutter_id_to_color (guint id, +- ClutterColor *col); +- ++CLUTTER_EXPORT + void _clutter_set_sync_to_vblank (gboolean sync_to_vblank); + + /* use this function as the accumulator if you have a signal with +diff --git a/clutter/clutter/clutter-stage-private.h b/clutter/clutter/clutter-stage-private.h +index 74d4f7bfd..3782e9925 100644 +--- a/clutter/clutter/clutter-stage-private.h ++++ b/clutter/clutter/clutter-stage-private.h +@@ -74,6 +74,15 @@ gint64 _clutter_stage_get_update_time (ClutterStage *stage); + void _clutter_stage_clear_update_time (ClutterStage *stage); + gboolean _clutter_stage_has_full_redraw_queued (ClutterStage *stage); + ++void _clutter_stage_log_pick (ClutterStage *stage, ++ const ClutterPoint vertices[4], ++ ClutterActor *actor); ++ ++void _clutter_stage_push_pick_clip (ClutterStage *stage, ++ const ClutterPoint vertices[4]); ++ ++void _clutter_stage_pop_pick_clip (ClutterStage *stage); ++ + ClutterActor *_clutter_stage_do_pick (ClutterStage *stage, + gint x, + gint y, +@@ -92,13 +101,6 @@ void _clutter_stage_queue_redraw_entry_invalidate (Clut + + CoglFramebuffer *_clutter_stage_get_active_framebuffer (ClutterStage *stage); + +-gint32 _clutter_stage_acquire_pick_id (ClutterStage *stage, +- ClutterActor *actor); +-void _clutter_stage_release_pick_id (ClutterStage *stage, +- gint32 pick_id); +-ClutterActor * _clutter_stage_get_actor_by_pick_id (ClutterStage *stage, +- gint32 pick_id); +- + void _clutter_stage_add_pointer_drag_actor (ClutterStage *stage, + ClutterInputDevice *device, + ClutterActor *actor); +diff --git a/clutter/clutter/clutter-stage-window.c b/clutter/clutter/clutter-stage-window.c +index 76421d6a0..5da7c50f5 100644 +--- a/clutter/clutter/clutter-stage-window.c ++++ b/clutter/clutter/clutter-stage-window.c +@@ -275,24 +275,6 @@ _clutter_stage_window_redraw (ClutterStageWindow *window) + iface->redraw (window); + } + +- +-void +-_clutter_stage_window_get_dirty_pixel (ClutterStageWindow *window, +- ClutterStageView *view, +- int *x, int *y) +-{ +- ClutterStageWindowInterface *iface; +- +- *x = 0; +- *y = 0; +- +- g_return_if_fail (CLUTTER_IS_STAGE_WINDOW (window)); +- +- iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window); +- if (iface->get_dirty_pixel) +- iface->get_dirty_pixel (window, view, x, y); +-} +- + gboolean + _clutter_stage_window_can_clip_redraws (ClutterStageWindow *window) + { +diff --git a/clutter/clutter/clutter-stage-window.h b/clutter/clutter/clutter-stage-window.h +index 389ed0596..f0aa3d3e9 100644 +--- a/clutter/clutter/clutter-stage-window.h ++++ b/clutter/clutter/clutter-stage-window.h +@@ -64,10 +64,6 @@ struct _ClutterStageWindowInterface + + void (* redraw) (ClutterStageWindow *stage_window); + +- void (* get_dirty_pixel) (ClutterStageWindow *stage_window, +- ClutterStageView *view, +- int *x, int *y); +- + gboolean (* can_clip_redraws) (ClutterStageWindow *stage_window); + + GList *(* get_views) (ClutterStageWindow *stage_window); +@@ -111,10 +107,6 @@ void _clutter_stage_window_set_accept_focus (ClutterStageWin + + void _clutter_stage_window_redraw (ClutterStageWindow *window); + +-void _clutter_stage_window_get_dirty_pixel (ClutterStageWindow *window, +- ClutterStageView *view, +- int *x, int *y); +- + gboolean _clutter_stage_window_can_clip_redraws (ClutterStageWindow *window); + + GList * _clutter_stage_window_get_views (ClutterStageWindow *window); +diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c +index 9e9e8d006..453e0ed38 100644 +--- a/clutter/clutter/clutter-stage.c ++++ b/clutter/clutter/clutter-stage.c +@@ -103,6 +103,23 @@ struct _ClutterStageQueueRedrawEntry + ClutterPaintVolume clip; + }; + ++struct _PickRecord ++{ ++ ClutterPoint vertex[4]; ++ ClutterActor *actor; ++ int clip_stack_top; ++}; ++ ++typedef struct _PickRecord PickRecord; ++ ++struct _PickClipRecord ++{ ++ int prev; ++ ClutterPoint vertex[4]; ++}; ++ ++typedef struct _PickClipRecord PickClipRecord; ++ + struct _ClutterStagePrivate + { + /* the stage implementation */ +@@ -136,7 +153,11 @@ struct _ClutterStagePrivate + GTimer *fps_timer; + gint32 timer_n_frames; + +- ClutterIDPool *pick_id_pool; ++ GArray *pick_stack; ++ GArray *pick_clip_stack; ++ int pick_clip_stack_top; ++ gboolean pick_stack_frozen; ++ ClutterPickMode cached_pick_mode; + + #ifdef CLUTTER_ENABLE_DEBUG + gulong redraw_count; +@@ -315,6 +336,177 @@ clutter_stage_get_preferred_height (ClutterActor *self, + *natural_height_p = geom.height; + } + ++/* In order to keep weak pointers valid between frames we need them to not ++ * move in memory, so the stack is marked as "frozen". ++ */ ++static void ++_clutter_stage_freeze_pick_stack (ClutterStage *stage) ++{ ++ ClutterStagePrivate *priv = stage->priv; ++ int i; ++ ++ if (priv->pick_stack_frozen) ++ return; ++ ++ for (i = 0; i < priv->pick_stack->len; i++) ++ { ++ PickRecord *rec = &g_array_index (priv->pick_stack, PickRecord, i); ++ ++ if (rec->actor) ++ g_object_add_weak_pointer (G_OBJECT (rec->actor), ++ (gpointer) &rec->actor); ++ } ++ ++ priv->pick_stack_frozen = TRUE; ++} ++ ++static void ++_clutter_stage_thaw_pick_stack (ClutterStage *stage) ++{ ++ ClutterStagePrivate *priv = stage->priv; ++ int i; ++ ++ if (!priv->pick_stack_frozen) ++ return; ++ ++ for (i = 0; i < priv->pick_stack->len; i++) ++ { ++ PickRecord *rec = &g_array_index (priv->pick_stack, PickRecord, i); ++ ++ if (rec->actor) ++ g_object_remove_weak_pointer (G_OBJECT (rec->actor), ++ (gpointer) &rec->actor); ++ } ++ ++ priv->pick_stack_frozen = FALSE; ++} ++ ++static void ++_clutter_stage_clear_pick_stack (ClutterStage *stage) ++{ ++ ClutterStagePrivate *priv = stage->priv; ++ ++ _clutter_stage_thaw_pick_stack (stage); ++ g_array_set_size (priv->pick_stack, 0); ++ g_array_set_size (priv->pick_clip_stack, 0); ++ priv->pick_clip_stack_top = -1; ++ priv->cached_pick_mode = CLUTTER_PICK_NONE; ++} ++ ++void ++_clutter_stage_log_pick (ClutterStage *stage, ++ const ClutterPoint vertices[4], ++ ClutterActor *actor) ++{ ++ ClutterStagePrivate *priv; ++ PickRecord rec; ++ ++ g_return_if_fail (CLUTTER_IS_STAGE (stage)); ++ g_return_if_fail (actor != NULL); ++ ++ priv = stage->priv; ++ ++ g_assert (!priv->pick_stack_frozen); ++ ++ memcpy (rec.vertex, vertices, 4 * sizeof (ClutterPoint)); ++ rec.actor = actor; ++ rec.clip_stack_top = priv->pick_clip_stack_top; ++ ++ g_array_append_val (priv->pick_stack, rec); ++} ++ ++void ++_clutter_stage_push_pick_clip (ClutterStage *stage, ++ const ClutterPoint vertices[4]) ++{ ++ ClutterStagePrivate *priv; ++ PickClipRecord clip; ++ ++ g_return_if_fail (CLUTTER_IS_STAGE (stage)); ++ ++ priv = stage->priv; ++ ++ g_assert (!priv->pick_stack_frozen); ++ ++ clip.prev = priv->pick_clip_stack_top; ++ memcpy (clip.vertex, vertices, 4 * sizeof (ClutterPoint)); ++ ++ g_array_append_val (priv->pick_clip_stack, clip); ++ priv->pick_clip_stack_top = priv->pick_clip_stack->len - 1; ++} ++ ++void ++_clutter_stage_pop_pick_clip (ClutterStage *stage) ++{ ++ ClutterStagePrivate *priv; ++ const PickClipRecord *top; ++ ++ g_return_if_fail (CLUTTER_IS_STAGE (stage)); ++ ++ priv = stage->priv; ++ ++ g_assert (!priv->pick_stack_frozen); ++ g_assert (priv->pick_clip_stack_top >= 0); ++ ++ /* Individual elements of pick_clip_stack are not freed. This is so they ++ * can be shared as part of a tree of different stacks used by different ++ * actors in the pick_stack. The whole pick_clip_stack does however get ++ * freed later in _clutter_stage_clear_pick_stack. ++ */ ++ ++ top = &g_array_index (priv->pick_clip_stack, ++ PickClipRecord, ++ priv->pick_clip_stack_top); ++ ++ priv->pick_clip_stack_top = top->prev; ++} ++ ++static gboolean ++pick_record_contains_pixel (ClutterStage *stage, ++ const PickRecord *rec, ++ int x, ++ int y) ++{ ++ ClutterStagePrivate *priv; ++ int clip_index; ++ ++ /* We test the centre point of the pixel so that we can use ++ * clutter_point_inside_polygon and avoid any ambiguity about the boundaries ++ * of pixel-aligned actors. If we were to use point (x,y) instead (which is ++ * the top-left corner of the pixel) then we'd need to use ++ * clutter_point_touches_polygon. ++ */ ++ const ClutterPoint point = { x + 0.5f, y + 0.5f }; ++ ++ if (!clutter_point_inside_polygon (&point, rec->vertex, 4)) ++ return FALSE; ++ ++ priv = stage->priv; ++ ++ /* pick_clip_stack is actually a graph containing one or more trees. And the ++ * clip_stack_top for a given PickRecord is the path from a leaf of such a ++ * tree to its root. It's still a stack though, from the point of view of the ++ * code that built it and the code that traverses it here. ++ * ++ * Allowing multiple stacks to overlap and merge into trees saves us time and ++ * memory. ++ */ ++ clip_index = rec->clip_stack_top; ++ while (clip_index >= 0) ++ { ++ const PickClipRecord *clip = &g_array_index (priv->pick_clip_stack, ++ PickClipRecord, ++ clip_index); ++ ++ if (!clutter_point_inside_polygon (&point, clip->vertex, 4)) ++ return FALSE; ++ ++ clip_index = clip->prev; ++ } ++ ++ return TRUE; ++} ++ + static inline void + queue_full_redraw (ClutterStage *stage) + { +@@ -622,6 +814,12 @@ clutter_stage_do_paint_view (ClutterStage *stage, + float viewport[4]; + cairo_rectangle_int_t geom; + ++ /* Any mode of painting/picking invalidates the pick cache, unless we're ++ * in the middle of building it. So we reset the cached flag but don't ++ * completely clear the pick stack... ++ */ ++ priv->cached_pick_mode = CLUTTER_PICK_NONE; ++ + _clutter_stage_window_get_geometry (priv->impl, &geom); + + viewport[0] = priv->viewport[0]; +@@ -1353,40 +1551,6 @@ clutter_stage_get_redraw_clip_bounds (ClutterStage *stage, + } + } + +-static void +-read_pixels_to_file (CoglFramebuffer *fb, +- char *filename_stem, +- int x, +- int y, +- int width, +- int height) +-{ +- guint8 *data; +- cairo_surface_t *surface; +- static int read_count = 0; +- char *filename = g_strdup_printf ("%s-%05d.png", +- filename_stem, +- read_count); +- +- data = g_malloc (4 * width * height); +- cogl_framebuffer_read_pixels (fb, +- x, y, width, height, +- CLUTTER_CAIRO_FORMAT_ARGB32, +- data); +- +- surface = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_RGB24, +- width, height, +- width * 4); +- +- cairo_surface_write_to_png (surface, filename); +- cairo_surface_destroy (surface); +- +- g_free (data); +- g_free (filename); +- +- read_count++; +-} +- + static ClutterActor * + _clutter_stage_do_pick_on_view (ClutterStage *stage, + gint x, +@@ -1394,140 +1558,47 @@ _clutter_stage_do_pick_on_view (ClutterStage *stage, + ClutterPickMode mode, + ClutterStageView *view) + { +- ClutterActor *actor = CLUTTER_ACTOR (stage); ++ ClutterMainContext *context = _clutter_context_get_default (); ++ int i; + ClutterStagePrivate *priv = stage->priv; + CoglFramebuffer *fb = clutter_stage_view_get_framebuffer (view); +- cairo_rectangle_int_t view_layout; +- ClutterMainContext *context; +- guchar pixel[4] = { 0xff, 0xff, 0xff, 0xff }; +- CoglColor stage_pick_id; +- gboolean dither_enabled_save; +- ClutterActor *retval; +- gint dirty_x; +- gint dirty_y; +- gint read_x; +- gint read_y; +- float fb_width, fb_height; +- float fb_scale; +- float viewport_offset_x; +- float viewport_offset_y; +- +- priv = stage->priv; +- +- context = _clutter_context_get_default (); +- fb_scale = clutter_stage_view_get_scale (view); +- clutter_stage_view_get_layout (view, &view_layout); +- +- fb_width = view_layout.width * fb_scale; +- fb_height = view_layout.height * fb_scale; +- cogl_push_framebuffer (fb); + +- /* needed for when a context switch happens */ +- _clutter_stage_maybe_setup_viewport (stage, view); +- +- /* FIXME: For some reason leaving the cogl clip stack empty causes the +- * picking to not work at all, so setting it the whole framebuffer content +- * for now. */ +- cogl_framebuffer_push_scissor_clip (fb, 0, 0, +- view_layout.width * fb_scale, +- view_layout.height * fb_scale); +- +- _clutter_stage_window_get_dirty_pixel (priv->impl, view, &dirty_x, &dirty_y); +- +- if (G_LIKELY (!(clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS))) +- { +- CLUTTER_NOTE (PICK, "Pushing pick scissor clip x: %d, y: %d, 1x1", +- (int) (dirty_x * fb_scale), +- (int) (dirty_y * fb_scale)); +- cogl_framebuffer_push_scissor_clip (fb, dirty_x * fb_scale, dirty_y * fb_scale, 1, 1); +- } ++ /* We are not reentrant right now. If this proves to be a problem then we ++ * would need to keep a stack of pick_stack's, or something... :( ++ */ ++ g_assert (context->pick_mode == CLUTTER_PICK_NONE); + +- viewport_offset_x = x * fb_scale - dirty_x * fb_scale; +- viewport_offset_y = y * fb_scale - dirty_y * fb_scale; +- CLUTTER_NOTE (PICK, "Setting viewport to %f, %f, %f, %f", +- priv->viewport[0] * fb_scale - viewport_offset_x, +- priv->viewport[1] * fb_scale - viewport_offset_y, +- priv->viewport[2] * fb_scale, +- priv->viewport[3] * fb_scale); +- cogl_framebuffer_set_viewport (fb, +- priv->viewport[0] * fb_scale - viewport_offset_x, +- priv->viewport[1] * fb_scale - viewport_offset_y, +- priv->viewport[2] * fb_scale, +- priv->viewport[3] * fb_scale); +- +- read_x = dirty_x * fb_scale; +- read_y = dirty_y * fb_scale; +- +- CLUTTER_NOTE (PICK, "Performing pick at %i,%i on view %dx%d+%d+%d s: %f", +- x, y, +- view_layout.width, view_layout.height, +- view_layout.x, view_layout.y, fb_scale); +- +- cogl_color_init_from_4ub (&stage_pick_id, 255, 255, 255, 255); +- cogl_framebuffer_clear (fb, COGL_BUFFER_BIT_COLOR | COGL_BUFFER_BIT_DEPTH, &stage_pick_id); +- +- /* Disable dithering (if any) when doing the painting in pick mode */ +- dither_enabled_save = cogl_framebuffer_get_dither_enabled (fb); +- cogl_framebuffer_set_dither_enabled (fb, FALSE); +- +- /* Render the entire scence in pick mode - just single colored silhouette's +- * are drawn offscreen (as we never swap buffers) +- */ +- context->pick_mode = mode; +- +- clutter_stage_do_paint_view (stage, view, NULL); +- context->pick_mode = CLUTTER_PICK_NONE; +- +- /* Read the color of the screen co-ords pixel. RGBA_8888_PRE is used +- even though we don't care about the alpha component because under +- GLES this is the only format that is guaranteed to work so Cogl +- will end up having to do a conversion if any other format is +- used. The format is requested as pre-multiplied because Cogl +- assumes that all pixels in the framebuffer are premultiplied so +- it avoids a conversion. */ +- cogl_framebuffer_read_pixels (fb, +- read_x, read_y, 1, 1, +- COGL_PIXEL_FORMAT_RGBA_8888_PRE, +- pixel); +- +- if (G_UNLIKELY (clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS)) ++ if (mode != priv->cached_pick_mode) + { +- char *file_name = +- g_strdup_printf ("pick-buffer-%s-view-x-%d", +- _clutter_actor_get_debug_name (actor), +- view_layout.x); +- +- read_pixels_to_file (fb, file_name, 0, 0, fb_width, fb_height); ++ _clutter_stage_clear_pick_stack (stage); + +- g_free (file_name); ++ /* We don't render to the fb, but have to set one to stop the cogl matrix ++ * operations from crashing in paint functions. Because the matrices are ++ * stored relative to the current fb. ++ */ ++ cogl_push_framebuffer (fb); ++ context->pick_mode = mode; ++ clutter_stage_do_paint_view (stage, view, NULL); ++ context->pick_mode = CLUTTER_PICK_NONE; ++ priv->cached_pick_mode = mode; ++ cogl_pop_framebuffer (); ++ ++ _clutter_stage_freeze_pick_stack (stage); + } + +- /* Restore whether GL_DITHER was enabled */ +- cogl_framebuffer_set_dither_enabled (fb, dither_enabled_save); +- +- if (G_LIKELY (!(clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS))) +- cogl_framebuffer_pop_clip (fb); +- +- cogl_framebuffer_pop_clip (fb); +- +- _clutter_stage_dirty_viewport (stage); +- +- if (pixel[0] == 0xff && pixel[1] == 0xff && pixel[2] == 0xff) +- retval = actor; +- else ++ /* Search all "painted" pickable actors from front to back. A linear search ++ * is required, and also performs fine since there is typically only ++ * on the order of dozens of actors in the list (on screen) at a time. ++ */ ++ for (i = priv->pick_stack->len - 1; i >= 0; i--) + { +- guint32 id_ = _clutter_pixel_to_id (pixel); ++ const PickRecord *rec = &g_array_index (priv->pick_stack, PickRecord, i); + +- retval = _clutter_stage_get_actor_by_pick_id (stage, id_); +- CLUTTER_NOTE (PICK, "Picking actor %s with id %u (pixel: 0x%x%x%x%x", +- G_OBJECT_TYPE_NAME (retval), +- id_, +- pixel[0], pixel[1], pixel[2], pixel[3]); ++ if (rec->actor && pick_record_contains_pixel (stage, rec, x, y)) ++ return rec->actor; + } + +- cogl_pop_framebuffer (); +- +- return retval; ++ return CLUTTER_ACTOR (stage); + } + + static ClutterStageView * +@@ -1819,7 +1890,9 @@ clutter_stage_finalize (GObject *object) + + g_array_free (priv->paint_volume_stack, TRUE); + +- _clutter_id_pool_free (priv->pick_id_pool); ++ _clutter_stage_clear_pick_stack (stage); ++ g_array_free (priv->pick_clip_stack, TRUE); ++ g_array_free (priv->pick_stack, TRUE); + + if (priv->fps_timer != NULL) + g_timer_destroy (priv->fps_timer); +@@ -2225,7 +2298,10 @@ clutter_stage_init (ClutterStage *self) + priv->paint_volume_stack = + g_array_new (FALSE, FALSE, sizeof (ClutterPaintVolume)); + +- priv->pick_id_pool = _clutter_id_pool_new (256); ++ priv->pick_stack = g_array_new (FALSE, FALSE, sizeof (PickRecord)); ++ priv->pick_clip_stack = g_array_new (FALSE, FALSE, sizeof (PickClipRecord)); ++ priv->pick_clip_stack_top = -1; ++ priv->cached_pick_mode = CLUTTER_PICK_NONE; + } + + /** +@@ -3895,6 +3971,12 @@ _clutter_stage_queue_actor_redraw (ClutterStage *stage, + CLUTTER_NOTE (CLIPPING, "stage_queue_actor_redraw (actor=%s, clip=%p): ", + _clutter_actor_get_debug_name (actor), clip); + ++ /* Queuing a redraw or clip change invalidates the pick cache, unless we're ++ * in the middle of building it. So we reset the cached flag but don't ++ * completely clear the pick stack... ++ */ ++ priv->cached_pick_mode = CLUTTER_PICK_NONE; ++ + if (!priv->redraw_pending) + { + ClutterMasterClock *master_clock; +@@ -4155,39 +4237,6 @@ _clutter_stage_get_active_framebuffer (ClutterStage *stage) + return stage->priv->active_framebuffer; + } + +-gint32 +-_clutter_stage_acquire_pick_id (ClutterStage *stage, +- ClutterActor *actor) +-{ +- ClutterStagePrivate *priv = stage->priv; +- +- g_assert (priv->pick_id_pool != NULL); +- +- return _clutter_id_pool_add (priv->pick_id_pool, actor); +-} +- +-void +-_clutter_stage_release_pick_id (ClutterStage *stage, +- gint32 pick_id) +-{ +- ClutterStagePrivate *priv = stage->priv; +- +- g_assert (priv->pick_id_pool != NULL); +- +- _clutter_id_pool_remove (priv->pick_id_pool, pick_id); +-} +- +-ClutterActor * +-_clutter_stage_get_actor_by_pick_id (ClutterStage *stage, +- gint32 pick_id) +-{ +- ClutterStagePrivate *priv = stage->priv; +- +- g_assert (priv->pick_id_pool != NULL); +- +- return _clutter_id_pool_lookup (priv->pick_id_pool, pick_id); +-} +- + void + _clutter_stage_add_pointer_drag_actor (ClutterStage *stage, + ClutterInputDevice *device, +diff --git a/clutter/clutter/clutter-types.h b/clutter/clutter/clutter-types.h +index 0f0fb1c2a..e3683184b 100644 +--- a/clutter/clutter/clutter-types.h ++++ b/clutter/clutter/clutter-types.h +@@ -200,6 +200,14 @@ float clutter_point_distance (const ClutterPoint *a, + const ClutterPoint *b, + float *x_distance, + float *y_distance); ++CLUTTER_EXPORT ++gboolean clutter_point_inside_polygon (const ClutterPoint *point, ++ const ClutterPoint *vertices, ++ unsigned int nvertices); ++CLUTTER_EXPORT ++gboolean clutter_point_touches_polygon (const ClutterPoint *point, ++ const ClutterPoint *vertices, ++ unsigned int nvertices); + + /** + * ClutterSize: +diff --git a/clutter/clutter/cogl/clutter-stage-cogl.c b/clutter/clutter/cogl/clutter-stage-cogl.c +index e0c39185b..137c66598 100644 +--- a/clutter/clutter/cogl/clutter-stage-cogl.c ++++ b/clutter/clutter/cogl/clutter-stage-cogl.c +@@ -956,55 +956,6 @@ clutter_stage_cogl_redraw (ClutterStageWindow *stage_window) + stage_cogl->frame_count++; + } + +-static void +-clutter_stage_cogl_get_dirty_pixel (ClutterStageWindow *stage_window, +- ClutterStageView *view, +- int *x, +- int *y) +-{ +- CoglFramebuffer *framebuffer = clutter_stage_view_get_framebuffer (view); +- gboolean has_buffer_age = +- cogl_is_onscreen (framebuffer) && +- is_buffer_age_enabled (); +- float fb_scale; +- gboolean scale_is_fractional; +- +- fb_scale = clutter_stage_view_get_scale (view); +- if (fb_scale != floorf (fb_scale)) +- scale_is_fractional = TRUE; +- else +- scale_is_fractional = FALSE; +- +- /* +- * Buffer damage is tracked in the framebuffer coordinate space +- * using the damage history. When fractional scaling is used, a +- * coordinate on the stage might not correspond to the exact position of any +- * physical pixel, which causes issues when painting using the pick mode. +- * +- * For now, always use the (0, 0) pixel for picking when using fractional +- * framebuffer scaling. +- */ +- if (!has_buffer_age || scale_is_fractional) +- { +- *x = 0; +- *y = 0; +- } +- else +- { +- ClutterStageViewCogl *view_cogl = CLUTTER_STAGE_VIEW_COGL (view); +- ClutterStageViewCoglPrivate *view_priv = +- clutter_stage_view_cogl_get_instance_private (view_cogl); +- cairo_rectangle_int_t view_layout; +- cairo_rectangle_int_t *fb_damage; +- +- clutter_stage_view_get_layout (view, &view_layout); +- +- fb_damage = &view_priv->damage_history[DAMAGE_HISTORY (view_priv->damage_index - 1)]; +- *x = fb_damage->x / fb_scale; +- *y = fb_damage->y / fb_scale; +- } +-} +- + static void + clutter_stage_window_iface_init (ClutterStageWindowInterface *iface) + { +@@ -1022,7 +973,6 @@ clutter_stage_window_iface_init (ClutterStageWindowInterface *iface) + iface->ignoring_redraw_clips = clutter_stage_cogl_ignoring_redraw_clips; + iface->get_redraw_clip_bounds = clutter_stage_cogl_get_redraw_clip_bounds; + iface->redraw = clutter_stage_cogl_redraw; +- iface->get_dirty_pixel = clutter_stage_cogl_get_dirty_pixel; + } + + static void +diff --git a/clutter/clutter/deprecated/clutter-texture.c b/clutter/clutter/deprecated/clutter-texture.c +index bea239f45..2c677b8a4 100644 +--- a/clutter/clutter/deprecated/clutter-texture.c ++++ b/clutter/clutter/deprecated/clutter-texture.c +@@ -572,83 +572,6 @@ gen_texcoords_and_draw_cogl_rectangle (ClutterActor *self, + 0, 0, t_w, t_h); + } + +-static CoglPipeline * +-create_pick_pipeline (ClutterActor *self) +-{ +- ClutterTexture *texture = CLUTTER_TEXTURE (self); +- ClutterTexturePrivate *priv = texture->priv; +- CoglPipeline *pick_pipeline = cogl_pipeline_copy (texture_template_pipeline); +- GError *error = NULL; +- +- if (!cogl_pipeline_set_layer_combine (pick_pipeline, 0, +- "RGBA = " +- " MODULATE (CONSTANT, TEXTURE[A])", +- &error)) +- { +- if (!priv->seen_create_pick_pipeline_warning) +- g_warning ("Error setting up texture combine for shaped " +- "texture picking: %s", error->message); +- priv->seen_create_pick_pipeline_warning = TRUE; +- g_error_free (error); +- cogl_object_unref (pick_pipeline); +- return NULL; +- } +- +- cogl_pipeline_set_blend (pick_pipeline, +- "RGBA = ADD (SRC_COLOR[RGBA], 0)", +- NULL); +- +- cogl_pipeline_set_alpha_test_function (pick_pipeline, +- COGL_PIPELINE_ALPHA_FUNC_EQUAL, +- 1.0); +- +- return pick_pipeline; +-} +- +-static void +-clutter_texture_pick (ClutterActor *self, +- const ClutterColor *color) +-{ +- ClutterTexture *texture = CLUTTER_TEXTURE (self); +- ClutterTexturePrivate *priv = texture->priv; +- CoglFramebuffer *framebuffer = cogl_get_draw_framebuffer (); +- +- if (!clutter_actor_should_pick_paint (self)) +- return; +- +- if (G_LIKELY (priv->pick_with_alpha_supported) && priv->pick_with_alpha) +- { +- CoglColor pick_color; +- +- if (priv->pick_pipeline == NULL) +- priv->pick_pipeline = create_pick_pipeline (self); +- +- if (priv->pick_pipeline == NULL) +- { +- priv->pick_with_alpha_supported = FALSE; +- CLUTTER_ACTOR_CLASS (clutter_texture_parent_class)->pick (self, +- color); +- return; +- } +- +- if (priv->fbo_handle != NULL) +- update_fbo (self); +- +- cogl_color_init_from_4ub (&pick_color, +- color->red, +- color->green, +- color->blue, +- 0xff); +- cogl_pipeline_set_layer_combine_constant (priv->pick_pipeline, +- 0, &pick_color); +- cogl_pipeline_set_layer_texture (priv->pick_pipeline, 0, +- clutter_texture_get_cogl_texture (texture)); +- gen_texcoords_and_draw_cogl_rectangle (self, priv->pick_pipeline, framebuffer); +- } +- else +- CLUTTER_ACTOR_CLASS (clutter_texture_parent_class)->pick (self, color); +-} +- + static void + clutter_texture_paint (ClutterActor *self) + { +@@ -767,12 +690,6 @@ clutter_texture_dispose (GObject *object) + priv->pipeline = NULL; + } + +- if (priv->pick_pipeline != NULL) +- { +- cogl_object_unref (priv->pick_pipeline); +- priv->pick_pipeline = NULL; +- } +- + G_OBJECT_CLASS (clutter_texture_parent_class)->dispose (object); + } + +@@ -944,7 +861,6 @@ clutter_texture_class_init (ClutterTextureClass *klass) + GParamSpec *pspec; + + actor_class->paint = clutter_texture_paint; +- actor_class->pick = clutter_texture_pick; + actor_class->get_paint_volume = clutter_texture_get_paint_volume; + actor_class->realize = clutter_texture_realize; + actor_class->unrealize = clutter_texture_unrealize; +@@ -1263,11 +1179,9 @@ clutter_texture_init (ClutterTexture *self) + priv->repeat_y = FALSE; + priv->sync_actor_size = TRUE; + priv->fbo_handle = NULL; +- priv->pick_pipeline = NULL; + priv->keep_aspect_ratio = FALSE; + priv->pick_with_alpha = FALSE; + priv->pick_with_alpha_supported = TRUE; +- priv->seen_create_pick_pipeline_warning = FALSE; + + if (G_UNLIKELY (texture_template_pipeline == NULL)) + { +@@ -3052,13 +2966,8 @@ clutter_texture_set_pick_with_alpha (ClutterTexture *texture, + if (priv->pick_with_alpha == pick_with_alpha) + return; + +- if (!pick_with_alpha && priv->pick_pipeline != NULL) +- { +- cogl_object_unref (priv->pick_pipeline); +- priv->pick_pipeline = NULL; +- } ++ g_assert (!pick_with_alpha); /* No longer supported */ + +- /* NB: the pick pipeline is created lazily when we first pick */ + priv->pick_with_alpha = pick_with_alpha; + + /* NB: actors are expected to call clutter_actor_queue_redraw when +diff --git a/clutter/tests/conform/actor-pick.c b/clutter/tests/conform/actor-pick.c +index 969b4920a..2bf5954c7 100644 +--- a/clutter/tests/conform/actor-pick.c ++++ b/clutter/tests/conform/actor-pick.c +@@ -5,7 +5,6 @@ + #define STAGE_HEIGHT 480 + #define ACTORS_X 12 + #define ACTORS_Y 16 +-#define SHIFT_STEP STAGE_WIDTH / ACTORS_X + + typedef struct _State State; + +@@ -20,84 +19,11 @@ struct _State + gboolean pass; + }; + +-struct _ShiftEffect +-{ +- ClutterShaderEffect parent_instance; +-}; +- +-struct _ShiftEffectClass +-{ +- ClutterShaderEffectClass parent_class; +-}; +- +-typedef struct _ShiftEffect ShiftEffect; +-typedef struct _ShiftEffectClass ShiftEffectClass; +- +-#define TYPE_SHIFT_EFFECT (shift_effect_get_type ()) +- +-GType shift_effect_get_type (void); +- +-G_DEFINE_TYPE (ShiftEffect, +- shift_effect, +- CLUTTER_TYPE_SHADER_EFFECT); +- +-static void +-shader_paint (ClutterEffect *effect, +- ClutterEffectPaintFlags flags) +-{ +- ClutterShaderEffect *shader = CLUTTER_SHADER_EFFECT (effect); +- float tex_width; +- ClutterActor *actor = +- clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (effect)); +- +- if (g_test_verbose ()) +- g_debug ("shader_paint"); +- +- clutter_shader_effect_set_shader_source (shader, +- "uniform sampler2D tex;\n" +- "uniform float step;\n" +- "void main (void)\n" +- "{\n" +- " cogl_color_out = texture2D(tex, vec2 (cogl_tex_coord_in[0].s + step,\n" +- " cogl_tex_coord_in[0].t));\n" +- "}\n"); +- +- tex_width = clutter_actor_get_width (actor); +- +- clutter_shader_effect_set_uniform (shader, "tex", G_TYPE_INT, 1, 0); +- clutter_shader_effect_set_uniform (shader, "step", G_TYPE_FLOAT, 1, +- SHIFT_STEP / tex_width); +- +- CLUTTER_EFFECT_CLASS (shift_effect_parent_class)->paint (effect, flags); +-} +- +-static void +-shader_pick (ClutterEffect *effect, +- ClutterEffectPaintFlags flags) +-{ +- shader_paint (effect, flags); +-} +- +-static void +-shift_effect_class_init (ShiftEffectClass *klass) +-{ +- ClutterEffectClass *shader_class = CLUTTER_EFFECT_CLASS (klass); +- +- shader_class->paint = shader_paint; +- shader_class->pick = shader_pick; +-} +- +-static void +-shift_effect_init (ShiftEffect *self) +-{ +-} +- + static const char *test_passes[] = { + "No covering actor", + "Invisible covering actor", + "Clipped covering actor", + "Blur effect", +- "Shift effect", + }; + + static gboolean +@@ -165,30 +91,10 @@ on_timeout (gpointer data) + if (g_test_verbose ()) + g_print ("With blur effect:\n"); + } +- else if (test_num == 4) +- { +- if (!clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL)) +- continue; +- +- clutter_actor_hide (over_actor); +- clutter_actor_remove_effect_by_name (CLUTTER_ACTOR (state->stage), +- "blur"); +- +- clutter_actor_add_effect_with_name (CLUTTER_ACTOR (state->stage), +- "shift", +- g_object_new (TYPE_SHIFT_EFFECT, +- NULL)); +- +- if (g_test_verbose ()) +- g_print ("With shift effect:\n"); +- } + + for (y = 0; y < ACTORS_Y; y++) + { +- if (test_num == 4) +- x = 1; +- else +- x = 0; ++ x = 0; + + for (; x < ACTORS_X; x++) + { +@@ -198,9 +104,6 @@ on_timeout (gpointer data) + + pick_x = x * state->actor_width + state->actor_width / 2; + +- if (test_num == 4) +- pick_x -= SHIFT_STEP; +- + actor = + clutter_stage_get_actor_at_pos (CLUTTER_STAGE (state->stage), + CLUTTER_PICK_ALL, +diff --git a/src/compositor/meta-surface-actor.c b/src/compositor/meta-surface-actor.c +index ca4ca19a9..814199145 100644 +--- a/src/compositor/meta-surface-actor.c ++++ b/src/compositor/meta-surface-actor.c +@@ -70,38 +70,23 @@ meta_surface_actor_pick (ClutterActor *actor, + else + { + int n_rects; +- float *rectangles; + int i; +- CoglPipeline *pipeline; +- CoglContext *ctx; +- CoglFramebuffer *fb; +- CoglColor cogl_color; + + n_rects = cairo_region_num_rectangles (priv->input_region); +- rectangles = g_alloca (sizeof (float) * 4 * n_rects); + + for (i = 0; i < n_rects; i++) + { + cairo_rectangle_int_t rect; +- int pos = i * 4; ++ ClutterActorBox box; + + cairo_region_get_rectangle (priv->input_region, i, &rect); + +- rectangles[pos + 0] = rect.x; +- rectangles[pos + 1] = rect.y; +- rectangles[pos + 2] = rect.x + rect.width; +- rectangles[pos + 3] = rect.y + rect.height; ++ box.x1 = rect.x; ++ box.y1 = rect.y; ++ box.x2 = rect.x + rect.width; ++ box.y2 = rect.y + rect.height; ++ clutter_actor_pick_box (actor, &box); + } +- +- ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); +- fb = cogl_get_draw_framebuffer (); +- +- cogl_color_init_from_4ub (&cogl_color, color->red, color->green, color->blue, color->alpha); +- +- pipeline = cogl_pipeline_new (ctx); +- cogl_pipeline_set_color (pipeline, &cogl_color); +- cogl_framebuffer_draw_rectangles (fb, pipeline, rectangles, n_rects); +- cogl_object_unref (pipeline); + } + + clutter_actor_iter_init (&iter, actor); diff --git a/mutter-performance/429.diff b/mutter-performance/429.diff new file mode 100644 index 0000000..3c30b13 --- /dev/null +++ b/mutter-performance/429.diff @@ -0,0 +1,218 @@ +diff --git a/clutter/clutter/clutter-actor.c b/clutter/clutter/clutter-actor.c +index 33c439533..7c8d671bb 100644 +--- a/clutter/clutter/clutter-actor.c ++++ b/clutter/clutter/clutter-actor.c +@@ -1798,6 +1798,7 @@ static void + clutter_actor_real_show (ClutterActor *self) + { + ClutterActorPrivate *priv = self->priv; ++ ClutterStage *stage; + + if (CLUTTER_ACTOR_IS_VISIBLE (self)) + return; +@@ -1832,6 +1833,11 @@ clutter_actor_real_show (ClutterActor *self) + clutter_actor_queue_shallow_relayout (self); + clutter_actor_queue_redraw (self); + } ++ ++ stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (self)); ++ ++ if (stage != NULL) ++ _clutter_stage_queue_repick (stage); + } + + static inline void +@@ -1975,6 +1981,7 @@ static void + clutter_actor_real_hide (ClutterActor *self) + { + ClutterActorPrivate *priv = self->priv; ++ ClutterStage *stage; + + if (!CLUTTER_ACTOR_IS_VISIBLE (self)) + return; +@@ -1993,6 +2000,11 @@ clutter_actor_real_hide (ClutterActor *self) + if (priv->parent != NULL && + (!(priv->parent->flags & CLUTTER_ACTOR_NO_LAYOUT))) + clutter_actor_queue_relayout (priv->parent); ++ ++ stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (self)); ++ ++ if (stage != NULL) ++ _clutter_stage_queue_repick (stage); + } + + /** +@@ -2640,6 +2652,8 @@ clutter_actor_set_allocation_internal (ClutterActor *self, + x2_changed || + y2_changed) + { ++ ClutterStage *stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (self)); ++ + CLUTTER_NOTE (LAYOUT, "Allocation for '%s' changed", + _clutter_actor_get_debug_name (self)); + +@@ -2654,6 +2668,8 @@ clutter_actor_set_allocation_internal (ClutterActor *self, + g_object_notify_by_pspec (obj, obj_props[PROP_CONTENT_BOX]); + } + ++ _clutter_stage_queue_repick (stage); ++ + retval = TRUE; + } + else +@@ -4486,7 +4502,12 @@ clutter_actor_remove_child_internal (ClutterActor *self, + * for the removed child + */ + if (was_mapped) +- clutter_actor_queue_relayout (self); ++ { ++ ClutterStage *stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (self)); ++ ++ _clutter_stage_queue_repick (stage); ++ clutter_actor_queue_relayout (self); ++ } + + /* we need to emit the signal before dropping the reference */ + if (emit_actor_removed) +@@ -13153,7 +13174,12 @@ clutter_actor_add_child_internal (ClutterActor *self, + * the actor is supposed to be visible when it's added + */ + if (CLUTTER_ACTOR_IS_MAPPED (child)) +- clutter_actor_queue_redraw (child); ++ { ++ ClutterStage *stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (self)); ++ ++ _clutter_stage_queue_repick (stage); ++ clutter_actor_queue_redraw (child); ++ } + + /* maintain the invariant that if an actor needs layout, + * its parents do as well +diff --git a/clutter/clutter/clutter-stage-private.h b/clutter/clutter/clutter-stage-private.h +index 1dd3a82f8..2812b5fac 100644 +--- a/clutter/clutter/clutter-stage-private.h ++++ b/clutter/clutter/clutter-stage-private.h +@@ -63,6 +63,7 @@ void _clutter_stage_maybe_relayout (ClutterActor + gboolean _clutter_stage_needs_update (ClutterStage *stage); + gboolean _clutter_stage_do_update (ClutterStage *stage); + ++void _clutter_stage_queue_repick (ClutterStage *stage); + void _clutter_stage_queue_event (ClutterStage *stage, + ClutterEvent *event, + gboolean copy_event); +diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c +index 52fecfddd..48dbdbb7f 100644 +--- a/clutter/clutter/clutter-stage.c ++++ b/clutter/clutter/clutter-stage.c +@@ -174,6 +174,7 @@ struct _ClutterStagePrivate + int update_freeze_count; + + guint redraw_pending : 1; ++ guint needs_repick : 1; + guint is_cursor_visible : 1; + guint use_fog : 1; + guint throttle_motion_events : 1; +@@ -182,7 +183,6 @@ struct _ClutterStagePrivate + guint accept_focus : 1; + guint motion_events_enabled : 1; + guint has_custom_perspective : 1; +- guint stage_was_relayout : 1; + }; + + enum +@@ -1319,8 +1319,6 @@ _clutter_stage_maybe_relayout (ClutterActor *actor) + + g_list_free_full (pending_queue_relayouts, g_object_unref); + +- if (count) +- priv->stage_was_relayout = TRUE; + } + + static void +@@ -1431,11 +1429,8 @@ gboolean + _clutter_stage_do_update (ClutterStage *stage) + { + ClutterStagePrivate *priv = stage->priv; +- gboolean stage_was_relayout = priv->stage_was_relayout; + GSList *pointers = NULL; + +- priv->stage_was_relayout = FALSE; +- + /* if the stage is being destroyed, or if the destruction already + * happened and we don't have an StageWindow any more, then we + * should bail out +@@ -1447,18 +1442,33 @@ _clutter_stage_do_update (ClutterStage *stage) + return FALSE; + + /* NB: We need to ensure we have an up to date layout *before* we +- * check or clear the pending redraws flag since a relayout may +- * queue a redraw. ++ * check or clear the pending redraws flag since a relayout or the ++ * repick afterwards may queue a redraw. + */ + _clutter_stage_maybe_relayout (CLUTTER_ACTOR (stage)); + +- if (!priv->redraw_pending) +- return FALSE; ++ /* Finish the queued redraws now so the redraw clip is initialized ++ * when we do the repick. */ ++ clutter_stage_maybe_finish_queue_redraws (stage); + +- if (stage_was_relayout) +- pointers = _clutter_stage_check_updated_pointers (stage); ++ if (priv->needs_repick) ++ { ++ pointers = _clutter_stage_check_updated_pointers (stage); ++ while (pointers) ++ { ++ _clutter_input_device_update (pointers->data, NULL, TRUE); ++ pointers = g_slist_delete_link (pointers, pointers); ++ } + +- clutter_stage_maybe_finish_queue_redraws (stage); ++ /* Make sure any newly queued redraws are also handled in this ++ * paint cycle. */ ++ clutter_stage_maybe_finish_queue_redraws (stage); ++ ++ priv->needs_repick = FALSE; ++ } ++ ++ if (!priv->redraw_pending) ++ return FALSE; + + clutter_stage_do_redraw (stage); + +@@ -1475,15 +1485,18 @@ _clutter_stage_do_update (ClutterStage *stage) + } + #endif /* CLUTTER_ENABLE_DEBUG */ + +- while (pointers) +- { +- _clutter_input_device_update (pointers->data, NULL, TRUE); +- pointers = g_slist_delete_link (pointers, pointers); +- } +- + return TRUE; + } + ++void ++_clutter_stage_queue_repick (ClutterStage *self) ++{ ++ ClutterStagePrivate *priv = self->priv; ++ ++ if (!priv->needs_repick) ++ priv->needs_repick = TRUE; ++} ++ + static void + clutter_stage_real_queue_relayout (ClutterActor *self) + { +@@ -2349,6 +2362,8 @@ clutter_stage_init (ClutterStage *self) + + clutter_stage_queue_actor_relayout (self, CLUTTER_ACTOR (self)); + ++ priv->needs_repick = FALSE; ++ + clutter_actor_set_reactive (CLUTTER_ACTOR (self), TRUE); + clutter_stage_set_title (self, g_get_prgname ()); + clutter_stage_set_key_focus (self, NULL); diff --git a/mutter-performance/724.diff b/mutter-performance/724.diff new file mode 100644 index 0000000..dbe0ed5 --- /dev/null +++ b/mutter-performance/724.diff @@ -0,0 +1,149 @@ +diff --git a/clutter/clutter/clutter-stage-private.h b/clutter/clutter/clutter-stage-private.h +index 2812b5fac..1a583ba2b 100644 +--- a/clutter/clutter/clutter-stage-private.h ++++ b/clutter/clutter/clutter-stage-private.h +@@ -83,6 +83,7 @@ void _clutter_stage_push_pick_clip (ClutterStage *stage, + const ClutterPoint vertices[4]); + + void _clutter_stage_pop_pick_clip (ClutterStage *stage); ++int64_t _clutter_stage_get_next_presentation_time (ClutterStage *stage); + + ClutterActor *_clutter_stage_do_pick (ClutterStage *stage, + gint x, +diff --git a/clutter/clutter/clutter-stage-window.c b/clutter/clutter/clutter-stage-window.c +index 5da7c50f5..312ce0c3e 100644 +--- a/clutter/clutter/clutter-stage-window.c ++++ b/clutter/clutter/clutter-stage-window.c +@@ -178,6 +178,22 @@ _clutter_stage_window_clear_update_time (ClutterStageWindow *window) + iface->clear_update_time (window); + } + ++int64_t ++_clutter_stage_window_get_next_presentation_time (ClutterStageWindow *window) ++{ ++ ClutterStageWindowInterface *iface; ++ ++ g_return_val_if_fail (CLUTTER_IS_STAGE_WINDOW (window), 0); ++ ++ iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window); ++ ++ /* If not implemented then just revert to the old behaviour... */ ++ if (iface->get_next_presentation_time == NULL) ++ return _clutter_stage_window_get_update_time (window); ++ ++ return iface->get_next_presentation_time (window); ++} ++ + void + _clutter_stage_window_add_redraw_clip (ClutterStageWindow *window, + cairo_rectangle_int_t *stage_clip) +diff --git a/clutter/clutter/clutter-stage-window.h b/clutter/clutter/clutter-stage-window.h +index f0aa3d3e9..6fd5200d9 100644 +--- a/clutter/clutter/clutter-stage-window.h ++++ b/clutter/clutter/clutter-stage-window.h +@@ -69,6 +69,8 @@ struct _ClutterStageWindowInterface + GList *(* get_views) (ClutterStageWindow *stage_window); + int64_t (* get_frame_counter) (ClutterStageWindow *stage_window); + void (* finish_frame) (ClutterStageWindow *stage_window); ++ ++ int64_t (* get_next_presentation_time) (ClutterStageWindow *stage_window); + }; + + ClutterActor * _clutter_stage_window_get_wrapper (ClutterStageWindow *window); +@@ -115,6 +117,8 @@ void _clutter_stage_window_finish_frame (ClutterStageWin + + int64_t _clutter_stage_window_get_frame_counter (ClutterStageWindow *window); + ++int64_t _clutter_stage_window_get_next_presentation_time (ClutterStageWindow *window); ++ + G_END_DECLS + + #endif /* __CLUTTER_STAGE_WINDOW_H__ */ +diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c +index 982c1da6c..2e4c4d674 100644 +--- a/clutter/clutter/clutter-stage.c ++++ b/clutter/clutter/clutter-stage.c +@@ -3908,6 +3908,21 @@ _clutter_stage_clear_update_time (ClutterStage *stage) + _clutter_stage_window_clear_update_time (stage_window); + } + ++int64_t ++_clutter_stage_get_next_presentation_time (ClutterStage *stage) ++{ ++ ClutterStageWindow *stage_window; ++ ++ if (CLUTTER_ACTOR_IN_DESTRUCTION (stage)) ++ return 0; ++ ++ stage_window = _clutter_stage_get_window (stage); ++ if (stage_window == NULL) ++ return 0; ++ ++ return _clutter_stage_window_get_next_presentation_time (stage_window); ++} ++ + /** + * clutter_stage_set_no_clear_hint: + * @stage: a #ClutterStage +diff --git a/clutter/clutter/cogl/clutter-stage-cogl.c b/clutter/clutter/cogl/clutter-stage-cogl.c +index fe7444d7e..89ace1983 100644 +--- a/clutter/clutter/cogl/clutter-stage-cogl.c ++++ b/clutter/clutter/cogl/clutter-stage-cogl.c +@@ -217,7 +217,12 @@ clutter_stage_cogl_schedule_update (ClutterStageWindow *stage_window, + stage_cogl->update_time = next_presentation_time - max_render_time_allowed; + + if (stage_cogl->update_time == stage_cogl->last_update_time) +- stage_cogl->update_time = stage_cogl->last_update_time + refresh_interval; ++ { ++ stage_cogl->update_time += refresh_interval; ++ next_presentation_time += refresh_interval; ++ } ++ ++ stage_cogl->next_presentation_time = next_presentation_time; + } + + static gint64 +@@ -235,6 +240,15 @@ clutter_stage_cogl_clear_update_time (ClutterStageWindow *stage_window) + + stage_cogl->last_update_time = stage_cogl->update_time; + stage_cogl->update_time = -1; ++ stage_cogl->next_presentation_time = -1; ++} ++ ++static int64_t ++clutter_stage_cogl_get_next_presentation_time (ClutterStageWindow *stage_window) ++{ ++ ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window); ++ ++ return stage_cogl->next_presentation_time; + } + + static ClutterActor * +@@ -975,6 +989,7 @@ clutter_stage_window_iface_init (ClutterStageWindowInterface *iface) + iface->schedule_update = clutter_stage_cogl_schedule_update; + iface->get_update_time = clutter_stage_cogl_get_update_time; + iface->clear_update_time = clutter_stage_cogl_clear_update_time; ++ iface->get_next_presentation_time = clutter_stage_cogl_get_next_presentation_time; + iface->add_redraw_clip = clutter_stage_cogl_add_redraw_clip; + iface->has_redraw_clips = clutter_stage_cogl_has_redraw_clips; + iface->ignoring_redraw_clips = clutter_stage_cogl_ignoring_redraw_clips; +@@ -1024,6 +1039,7 @@ _clutter_stage_cogl_init (ClutterStageCogl *stage) + stage->refresh_rate = 0.0; + + stage->update_time = -1; ++ stage->next_presentation_time = -1; + } + + static void +diff --git a/clutter/clutter/cogl/clutter-stage-cogl.h b/clutter/clutter/cogl/clutter-stage-cogl.h +index 53d0267de..1d83d5525 100644 +--- a/clutter/clutter/cogl/clutter-stage-cogl.h ++++ b/clutter/clutter/cogl/clutter-stage-cogl.h +@@ -53,6 +53,7 @@ struct _ClutterStageCogl + gint64 last_presentation_time; + gint64 update_time; + int64_t last_update_time; ++ int64_t next_presentation_time; + + /* We only enable clipped redraws after 2 frames, since we've seen + * a lot of drivers can struggle to get going and may output some diff --git a/mutter-performance/PKGBUILD b/mutter-performance/PKGBUILD new file mode 100644 index 0000000..b02b94b --- /dev/null +++ b/mutter-performance/PKGBUILD @@ -0,0 +1,308 @@ +# Maintainer: Saren Arterius +# Maintainer: Térence Clastres + +# Maintainer: Jan Alexander Steffens (heftig) +# Maintainer: Ionut Biru +# Contributor: Michael Kanis + +pkgname=mutter-performance +pkgver=3.32.2+43+gb7f158811 +pkgrel=19 +pkgdesc="A window manager for GNOME | Attempts to improve performances with non-upstreamed merge-requests and frequent stable branch resync" +url="https://gitlab.gnome.org/GNOME/mutter" +arch=(x86_64) +license=(GPL) +depends=(dconf gobject-introspection-runtime gsettings-desktop-schemas libcanberra + startup-notification zenity libsm gnome-desktop upower libxkbcommon-x11 + gnome-settings-daemon libgudev libinput pipewire xorg-server-xwayland gnome-shell) +makedepends=(gobject-introspection git egl-wayland meson xorg-server) +checkdepends=(xorg-server-xvfb) +options=(debug !strip) +provides=(mutter mutter-781835-workaround) +conflicts=(mutter) +replaces=(mutter-781835-workaround) +groups=(gnome) +_commit=b7f158811934d8e4d9dd0be28ad8e1746ceac46c # tags/3.32.2^43 +source=("$pkgname::git+https://gitlab.gnome.org/GNOME/mutter.git#commit=$_commit" + 429.diff + 724.diff + 189.diff) +sha256sums=('SKIP' + 'c22bc32ab8a29e3da986f386d14a8376f1985c051328ca6786571b4b67ec9e48' + '3bfd673cbae598f6482124525d342b4323a1395a0f72d6532bbcf34f66773213' + '95e29ca135a024ad2fab6d0b4d8d6674699a95e6df91f4ca9e0437a475459d87') + +pkgver() { + cd $pkgname + git describe --tags | sed 's/-/+/g' +} + +hash_of() { + git log --oneline --all | grep "$1" | tail -n 1 | awk '{print $1}' +} + +git_cp_by_msg() { + # Comment: Saren found a way to fetch hash based on commit name. It's controversial but might be interesting to create a function to call for each MR to not have to update the hash at each rebase. + h_first=$(hash_of "$2") + if [[ -n "$3" ]]; then + h_last=$(hash_of "$3") + echo "Found $h_first^$h_last for $1" + git cherry-pick -n -Xtheirs $h_first^..$h_last + else + echo "Found $h_first for $1" + git cherry-pick -n -Xtheirs $h_first + fi +} + +prepare() { + cd $pkgname + + ### Adding and fetching remotes providing the selected merge-requests + + git cherry-pick --abort || true + git remote add vanvugt https://gitlab.gnome.org/vanvugt/mutter.git || true + git fetch vanvugt + + + ### Merge Requests + + # Merge Request Prototype + # Title: + # URL: + # Type: + # Status: + # Comment: + # git cherry-pick -n first_commit^..last_commit + # + # Possible Status: + # 1. Needs rebase: Conflicts with master branch. + # 2. Needs review: Mutter maintainers needs to review the new/updated MR and provide feedback. + # 3. Needs changes: MR needs to be adjusted based on maintainers feedback. + # 4. Merged: MR approved and it changes commited to master. + # + # Generally, a MR status oscillate between 2 and 3 and then becomes 4. + # + # Possible Type: + # 1. Improvement: Makes an already existing feature behave better, more efficiently/reliably. + # 2. Feature: Adds a new functionality. + # 3. Fix: Regression/bug fix only available in master (not backported). + # 4. Cleanup: Code styling improvement, function deprecation, rearrangement... + + + + # Title: Various Clutter cleanups + # URL: https://gitlab.gnome.org/GNOME/mutter/merge_requests/666 + # Type: 4 + # Status: 4 + # Comment: Needed so !189 doesn't conflict + git cherry-pick -n 5bd85ef7^..b0b1ff36 + + # Title: Minor clutter stage cleanup + # URL: https://gitlab.gnome.org/GNOME/mutter/merge_requests/616 + # Type: 4 + # Status: 4 + # Comment: Needed so !661 doesn't conflict + git cherry-pick -n 4064d9a7^..3073acc3 + + # Title: Geometric (OpenGL-less) picking + # URL: https://gitlab.gnome.org/GNOME/mutter/merge_requests/189 + # Type: 1 + # Status: 2 + # Comment: + patch -Np1 < ../189.diff + git add . + + # Title: clutter/stage-cogl: Don't skip over the next frame + # URL : https://gitlab.gnome.org/GNOME/mutter/merge_requests/520 + # Type: 1 + # Status: 4 + # Comment: + git cherry-pick -n 45244852 + + # Title: Consolidate all frame throttling into clutter-stage-cogl + # URL: https://gitlab.gnome.org/GNOME/mutter/merge_requests/363 + # Type: 1 + # Status: 4 + # Comment: + git cherry-pick -n 912a9ecf^..1dbf25af + + # Title: clutter/stage-cogl: Reschedule update on present + # URL: https://gitlab.gnome.org/GNOME/mutter/merge_requests/281 + # Type: 1 + # Status: 4 + # Comment: + git cherry-pick -n 4faeb127 + + # Title: clutter/stage: Only queue compressible events + # URL: https://gitlab.gnome.org/GNOME/mutter/merge_requests/711 + # Type: 1 + # Status: 2 + # Comment: Replaces !168 + git_cp_by_msg '!711' 'clutter/device-manager-evdev: Update device modifiers before queuing' 'clutter/stage: Only queue compressible events' + + # Title: Resource scale computation optimizations + # URL: https://gitlab.gnome.org/GNOME/mutter/merge_requests/493 + # Type: 1 + # Status: 3 + # Comment: Disabled because breaks the overview on Wayland. https://gitlab.gnome.org/GNOME/mutter/merge_requests/493#note_549833 + if [ -f "$HOME/.i_dont_use_wayland_on_gnome" ]; then + echo "OK, you dont use wayland on gnome" + git cherry-pick -n 3aa449af^..1017ce44 + fi + + # Title: clutter: Defer actor allocations till shown + # URL: https://gitlab.gnome.org/GNOME/mutter/merge_requests/677 + # Type: 1 + # Status: 4 + # Comment: + git cherry-pick -n 0eab73dc + + # Title: Honour `CLUTTER_ACTOR_NO_LAYOUT` more efficiently + # URL: https://gitlab.gnome.org/GNOME/mutter/merge_requests/575 + # Type: 1 + # Status: 2 + git_cp_by_msg '!575' 'clutter/stage: Add an API for shallow relayouts' 'clutter/actor: Use the new shallow relayout API' + + # Title: cogl: Remove GLX "threaded swap wait" used on Nvidia + # URL: https://gitlab.gnome.org/GNOME/mutter/merge_requests/602 + # Type: 1 + # Status: 2 + # Comment: Makes the shell unresponsive to most of the interactions with mouse and keyboard + # git_cp_by_msg '!602' 'cogl: Remove GLX "threaded swap wait" used on Nvidia' + # git cherry-pick -n -Xtheirs df38ad3c + + # Title: compositor: Don't emit size-changed when only position changes + # URL: https://gitlab.gnome.org/GNOME/mutter/merge_requests/568 + # Type: 1 + # Status: 4 + # Comment: + git cherry-pick -n 01e20a6b + + # Title: cogl: Enable EGL_IMG_context_priority + # URL: https://gitlab.gnome.org/GNOME/mutter/merge_requests/454 + # Type: 1 + # Status: 4 + # Comment: + git cherry-pick -n 3f29b478^..7df86fb2 + + # Title: Add experimental key for RT scheduling + # URL: https://gitlab.gnome.org/GNOME/mutter/merge_requests/460 + # Type: 1 + # Status: 4 + # Comment: Only works in Wayland + git cherry-pick -n dae2c1d4 + + # Title: backends: Do not reload keymap on new keyboard notifications + # URL: https://gitlab.gnome.org/GNOME/mutter/merge_requests/579 + # Type: 1 + # Status: 4 + # Comment: Disabled by default because it has issues when using multiple layouts + if [ -f "$HOME/.i_dont_use_multiple_keyboard_layouts" ]; then + echo "OK, you dont use multiple keyboard layouts" + git cherry-pick -n b01edc22 + fi + + # Title: Fix background texture corruption when resuming from suspend on Nvidia + # URL: https://gitlab.gnome.org/GNOME/mutter/merge_requests/600 + # Type: 2 + # Status: 4 + # Comment: + git cherry-pick -n a5265365 + + # Title: WIP: renderer-native: Accept frames without ever blocking + # URL: https://gitlab.gnome.org/GNOME/mutter/merge_requests/73 + # Type: 1 + # Status: 1 + # Comment: Might be replaced: https://gitlab.gnome.org/GNOME/mutter/merge_requests/73#note_544784 + # git cherry-pick -n 35ec0eaf^..202530c9 + + # Title: clutter: Force an allocation on clone source if necessary + # URL: https://gitlab.gnome.org/GNOME/mutter/merge_requests/693 + # Type: 3 + # Status: 4 + # Comment: Sometimes produces artefacts in the application grid: https://gitlab.gnome.org/GNOME/mutter/merge_requests/693#note_566491 + #git cherry-pick -n 08a3cbfc + + # Title: core: Only trigger MetaWorkspace::window-* on toplevel window types + # URL: https://gitlab.gnome.org/GNOME/mutter/merge_requests/701 + # Type: 1 + # Status: 2 + # Comment: + git cherry-pick -n d7f799bf + + # Title: Make MetaCullable implementations more thorough wrt painted areas + # URL: https://gitlab.gnome.org/GNOME/mutter/merge_requests/698 + # Type: 1 + # Status: 4 + # Comment: + git cherry-pick -n 2812338b^..f501fdcc + + #Title: window-actor: Fix rectangle coordinates in culling + #URL: https://gitlab.gnome.org/GNOME/mutter/merge_requests/707 + #Type: 3 + #Status: 4 + #Comment: Fix for !698 + git cherry-pick -n aae9f3a3 + + # Title: clutter-stage-cogl: Use regions + # URL: https://gitlab.gnome.org/GNOME/mutter/merge_requests/692 + # Type: 1 + # Status: 1 + # Comment: + #git cherry-pick -n f2694e72^..d0edf91c + + # Title: clutter/stage: Update input devices right after doing a relayout + # URL: https://gitlab.gnome.org/GNOME/mutter/merge_requests/429 + # Type: 1 + # Status: 2 + # Comment: Can't be cleanly applied on 3.32 without lot of cherry-pick unrelated commits... + patch -Np1 < ../429.diff + git add clutter/clutter/*.{c,h} + + # Title: Implement clipboard manager + # URL: https://gitlab.gnome.org/GNOME/mutter/merge_requests/320 + # Type: 2 + # Status: 4 + # Comment: crashes in wayland session when trying to drag a folder/file in nautilus or a tab in firefox + # git cherry-pick -n 156980ef^..02c99524 + + # Title: renderer-native: Reference count front buffers. + # URL: https://gitlab.gnome.org/GNOME/mutter/merge_requests/119 + # Type: 2 + # Status: 4 + # Comment: Needed for !719 to behave well + git cherry-pick -n fecc57dd + + # Title: Remove pending_swaps counter + # URL: https://gitlab.gnome.org/GNOME/mutter/merge_requests/719 + # Type: 1 + # Status: 2 + # Comment: + git_cp_by_msg '!719' 'clutter/stage-cogl: Remove pending_swaps counter' + + # Title: clutter: Use va_marshallers for actor signals + # URL: https://gitlab.gnome.org/GNOME/mutter/merge_requests/700 + # Type: 1 + # Status: 4 + # Comment: + git cherry-pick -n c4a9117e^..9d65eab5 -Xours + + # Title: Sync timelines to hardware vsync + # URL: https://gitlab.gnome.org/GNOME/mutter/merge_requests/724 + # Type: 1 + # Status: 2 + # Comment: + patch -Np1 < ../724.diff +} + +build() { + arch-meson $pkgname build \ + -D egl_device=true \ + -D wayland_eglstream=true \ + -D installed_tests=false + ninja -C build +} + +package() { + DESTDIR="$pkgdir" meson install -C build +}