diff --git a/src/compositor/meta-shaped-texture.c b/src/compositor/meta-shaped-texture.c index c6239c9d2..d7333a095 100644 --- a/src/compositor/meta-shaped-texture.c +++ b/src/compositor/meta-shaped-texture.c @@ -416,12 +416,32 @@ meta_shaped_texture_set_mask_texture (MetaShapedTexture *stex, clutter_actor_queue_redraw (CLUTTER_ACTOR (stex)); } -void + +/** + * meta_shaped_texture_update_area: + * @stex: #MetaShapedTexture + * @x: the x coordinate of the damaged area + * @y: the y coordinate of the damaged area + * @width: the width of the damaged area + * @height: the height of the damaged area + * @unobscured_region: The unobscured region of the window or %NULL if + * there is no valid one (like when the actor is transformed or + * has a mapped clone) + * + * Repairs the damaged area indicated by @x, @y, @width and @height + * and queues a redraw for the intersection @visibible_region and + * the damage area. If @visibible_region is %NULL a redraw will always + * get queued. + * + * Return value: Whether a redraw have been queued or not + */ +gboolean meta_shaped_texture_update_area (MetaShapedTexture *stex, int x, int y, int width, - int height) + int height, + cairo_region_t *unobscured_region) { MetaShapedTexturePrivate *priv; const cairo_rectangle_int_t clip = { x, y, width, height }; @@ -429,14 +449,41 @@ meta_shaped_texture_update_area (MetaShapedTexture *stex, priv = stex->priv; if (priv->texture == NULL) - return; + return FALSE; cogl_texture_pixmap_x11_update_area (priv->texture, x, y, width, height); meta_texture_tower_update_area (priv->paint_tower, x, y, width, height); + if (unobscured_region) + { + cairo_region_t *intersection; + + if (cairo_region_is_empty (unobscured_region)) + return FALSE; + + intersection = cairo_region_copy (unobscured_region); + cairo_region_intersect_rectangle (intersection, &clip); + + if (!cairo_region_is_empty (intersection)) + { + cairo_rectangle_int_t damage_rect; + cairo_region_get_extents (intersection, &damage_rect); + clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (stex), &damage_rect); + cairo_region_destroy (intersection); + + return TRUE; + } + + cairo_region_destroy (intersection); + + return FALSE; + } + clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (stex), &clip); + + return TRUE; } static void diff --git a/src/compositor/meta-window-actor-private.h b/src/compositor/meta-window-actor-private.h index 90a9e35e0..0e319f5af 100644 --- a/src/compositor/meta-window-actor-private.h +++ b/src/compositor/meta-window-actor-private.h @@ -57,11 +57,14 @@ void meta_window_actor_queue_frame_drawn (MetaWindowActor *self, cairo_region_t *meta_window_actor_get_obscured_region (MetaWindowActor *self); -void meta_window_actor_set_visible_region (MetaWindowActor *self, - cairo_region_t *visible_region); -void meta_window_actor_set_visible_region_beneath (MetaWindowActor *self, - cairo_region_t *beneath_region); -void meta_window_actor_reset_visible_regions (MetaWindowActor *self); +void meta_window_actor_set_clip_region (MetaWindowActor *self, + cairo_region_t *clip_region); +void meta_window_actor_set_clip_region_beneath (MetaWindowActor *self, + cairo_region_t *beneath_region); +void meta_window_actor_reset_clip_regions (MetaWindowActor *self); + +void meta_window_actor_set_unobscured_region (MetaWindowActor *self, + cairo_region_t *unobscured_region); void meta_window_actor_effect_completed (MetaWindowActor *actor, gulong event); diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c index 5b123d6db..c8e4ca03b 100644 --- a/src/compositor/meta-window-actor.c +++ b/src/compositor/meta-window-actor.c @@ -78,6 +78,9 @@ struct _MetaWindowActorPrivate /* The region we should clip to when painting the shadow */ cairo_region_t *shadow_clip; + /* The region that is visible, used to optimize out redraws */ + cairo_region_t *unobscured_region; + /* Extracted size-invariant shape used for shadows */ MetaWindowShape *shadow_shape; @@ -412,6 +415,7 @@ meta_window_actor_dispose (GObject *object) meta_window_actor_detach (self); + g_clear_pointer (&priv->unobscured_region, cairo_region_destroy); g_clear_pointer (&priv->shape_region, cairo_region_destroy); g_clear_pointer (&priv->opaque_region, cairo_region_destroy); g_clear_pointer (&priv->shadow_clip, cairo_region_destroy); @@ -913,7 +917,8 @@ meta_window_actor_damage_all (MetaWindowActor *self) meta_shaped_texture_update_area (META_SHAPED_TEXTURE (priv->actor), 0, 0, cogl_texture_get_width (texture), - cogl_texture_get_height (texture)); + cogl_texture_get_height (texture), + clutter_actor_has_mapped_clones (priv->actor) ? NULL : priv->unobscured_region); priv->needs_damage_all = FALSE; priv->repaint_scheduled = TRUE; @@ -1657,40 +1662,67 @@ see_region (cairo_region_t *region, #endif /** - * meta_window_actor_set_visible_region: + * meta_window_actor_set_unobscured_region: * @self: a #MetaWindowActor - * @visible_region: the region of the screen that isn't completely + * @unobscured_region: the region of the screen that isn't completely + * obscured. + * + * Provides a hint as to what areas of the window need to queue + * redraws when damaged. Regions not in @unobscured_region are completely obscured. + * Unlike meta_window_actor_set_clip_region(), the region here + * doesn't take into account any clipping that is in effect while drawing. + */ +void +meta_window_actor_set_unobscured_region (MetaWindowActor *self, + cairo_region_t *unobscured_region) +{ + MetaWindowActorPrivate *priv = self->priv; + + if (priv->unobscured_region) + cairo_region_destroy (priv->unobscured_region); + + if (unobscured_region) + priv->unobscured_region = cairo_region_copy (unobscured_region); + else + priv->unobscured_region = NULL; +} + +/** + * meta_window_actor_set_clip_region: + * @self: a #MetaWindowActor + * @clip_region: the region of the screen that isn't completely * obscured. * * Provides a hint as to what areas of the window need to be - * drawn. Regions not in @visible_region are completely obscured. + * drawn. Regions not in @clip_region are completely obscured or + * not drawn in this frame. * This will be set before painting then unset afterwards. */ void -meta_window_actor_set_visible_region (MetaWindowActor *self, - cairo_region_t *visible_region) +meta_window_actor_set_clip_region (MetaWindowActor *self, + cairo_region_t *clip_region) { MetaWindowActorPrivate *priv = self->priv; meta_shaped_texture_set_clip_region (META_SHAPED_TEXTURE (priv->actor), - visible_region); + clip_region); } /** - * meta_window_actor_set_visible_region_beneath: + * meta_window_actor_set_clip_region_beneath: * @self: a #MetaWindowActor - * @visible_region: the region of the screen that isn't completely + * @clip_region: the region of the screen that isn't completely * obscured beneath the main window texture. * * Provides a hint as to what areas need to be drawn *beneath* - * the main window texture. This is the relevant visible region + * the main window texture. This is the relevant clip region * when drawing the shadow, properly accounting for areas of the * shadow hid by the window itself. This will be set before painting * then unset afterwards. */ void -meta_window_actor_set_visible_region_beneath (MetaWindowActor *self, - cairo_region_t *beneath_region) +meta_window_actor_set_clip_region_beneath (MetaWindowActor *self, + cairo_region_t *beneath_region) { MetaWindowActorPrivate *priv = self->priv; gboolean appears_focused = meta_window_appears_focused (priv->window); @@ -1709,14 +1741,14 @@ meta_window_actor_set_visible_region_beneath (MetaWindowActor *self, } /** - * meta_window_actor_reset_visible_regions: + * meta_window_actor_reset_clip_regions: * @self: a #MetaWindowActor * - * Unsets the regions set by meta_window_actor_set_visible_region() and - * meta_window_actor_set_visible_region_beneath() + * Unsets the regions set by meta_window_actor_set_clip_region() and + * meta_window_actor_set_clip_region_beneath() */ void -meta_window_actor_reset_visible_regions (MetaWindowActor *self) +meta_window_actor_reset_clip_regions (MetaWindowActor *self) { MetaWindowActorPrivate *priv = self->priv; @@ -1939,7 +1971,8 @@ meta_window_actor_process_damage (MetaWindowActor *self, event->area.x, event->area.y, event->area.width, - event->area.height); + event->area.height, + clutter_actor_has_mapped_clones (priv->actor) ? NULL : priv->unobscured_region); priv->repaint_scheduled = TRUE; } diff --git a/src/compositor/meta-window-group.c b/src/compositor/meta-window-group.c index 5302fd8a1..64494e589 100644 --- a/src/compositor/meta-window-group.c +++ b/src/compositor/meta-window-group.c @@ -89,17 +89,30 @@ painting_untransformed (MetaWindowGroup *window_group, static void meta_window_group_paint (ClutterActor *actor) { - cairo_region_t *visible_region; - ClutterActor *stage; + cairo_region_t *clip_region; + cairo_region_t *unobscured_region; ClutterActorIter iter; ClutterActor *child; - cairo_rectangle_int_t visible_rect; + cairo_rectangle_int_t visible_rect, clip_rect; int paint_x_origin, paint_y_origin; int actor_x_origin, actor_y_origin; int paint_x_offset, paint_y_offset; MetaWindowGroup *window_group = META_WINDOW_GROUP (actor); MetaCompScreen *info = meta_screen_get_compositor_data (window_group->screen); + ClutterActor *stage = clutter_actor_get_stage (actor); + + /* Start off by treating all windows as completely unobscured, so damage anywhere + * in a window queues redraws, but confine it more below. */ + clutter_actor_iter_init (&iter, actor); + while (clutter_actor_iter_next (&iter, &child)) + { + if (META_IS_WINDOW_ACTOR (child)) + { + MetaWindowActor *window_actor = META_WINDOW_ACTOR (child); + meta_window_actor_set_unobscured_region (window_actor, NULL); + } + } /* Normally we expect an actor to be drawn at it's position on the screen. * However, if we're inside the paint of a ClutterClone, that won't be the @@ -124,17 +137,22 @@ meta_window_group_paint (ClutterActor *actor) paint_x_offset = paint_x_origin - actor_x_origin; paint_y_offset = paint_y_origin - actor_y_origin; + visible_rect.x = visible_rect.y = 0; + visible_rect.width = clutter_actor_get_width (CLUTTER_ACTOR (stage)); + visible_rect.height = clutter_actor_get_height (CLUTTER_ACTOR (stage)); + + unobscured_region = cairo_region_create_rectangle (&visible_rect); + /* Get the clipped redraw bounds from Clutter so that we can avoid * painting shadows on windows that don't need to be painted in this * frame. In the case of a multihead setup with mismatched monitor * sizes, we could intersect this with an accurate union of the * monitors to avoid painting shadows that are visible only in the * holes. */ - stage = clutter_actor_get_stage (actor); clutter_stage_get_redraw_clip_bounds (CLUTTER_STAGE (stage), - &visible_rect); + &clip_rect); - visible_region = cairo_region_create_rectangle (&visible_rect); + clip_region = cairo_region_create_rectangle (&clip_rect); if (info->unredirected_window != NULL) { @@ -142,7 +160,8 @@ meta_window_group_paint (ClutterActor *actor) MetaWindow *window = meta_window_actor_get_meta_window (info->unredirected_window); meta_window_get_outer_rect (window, (MetaRectangle *)&unredirected_rect); - cairo_region_subtract_rectangle (visible_region, &unredirected_rect); + cairo_region_subtract_rectangle (unobscured_region, &unredirected_rect); + cairo_region_subtract_rectangle (clip_region, &unredirected_rect); } /* We walk the list from top to bottom (opposite of painting order), @@ -189,20 +208,28 @@ meta_window_group_paint (ClutterActor *actor) x += paint_x_offset; y += paint_y_offset; - /* Temporarily move to the coordinate system of the actor */ - cairo_region_translate (visible_region, - x, - y); - meta_window_actor_set_visible_region (window_actor, visible_region); + /* Temporarily move to the coordinate system of the actor */ + cairo_region_translate (unobscured_region, - x, - y); + cairo_region_translate (clip_region, - x, - y); + + meta_window_actor_set_unobscured_region (window_actor, unobscured_region); + meta_window_actor_set_clip_region (window_actor, clip_region); if (clutter_actor_get_paint_opacity (CLUTTER_ACTOR (window_actor)) == 0xff) { cairo_region_t *obscured_region = meta_window_actor_get_obscured_region (window_actor); if (obscured_region) - cairo_region_subtract (visible_region, obscured_region); + { + cairo_region_subtract (unobscured_region, obscured_region); + cairo_region_subtract (clip_region, obscured_region); + } } - meta_window_actor_set_visible_region_beneath (window_actor, visible_region); - cairo_region_translate (visible_region, x, y); + meta_window_actor_set_clip_region_beneath (window_actor, clip_region); + + cairo_region_translate (unobscured_region, x, y); + cairo_region_translate (clip_region, x, y); } else if (META_IS_BACKGROUND_ACTOR (child) || META_IS_BACKGROUND_GROUP (child)) @@ -215,17 +242,19 @@ meta_window_group_paint (ClutterActor *actor) x += paint_x_offset; y += paint_y_offset; - cairo_region_translate (visible_region, - x, - y); + cairo_region_translate (clip_region, - x, - y); if (META_IS_BACKGROUND_GROUP (child)) - meta_background_group_set_clip_region (META_BACKGROUND_GROUP (child), visible_region); + meta_background_group_set_clip_region (META_BACKGROUND_GROUP (child), clip_region); else - meta_background_actor_set_clip_region (META_BACKGROUND_ACTOR (child), visible_region); - cairo_region_translate (visible_region, x, y); + meta_background_actor_set_clip_region (META_BACKGROUND_ACTOR (child), clip_region); + + cairo_region_translate (clip_region, x, y); } } - cairo_region_destroy (visible_region); + cairo_region_destroy (unobscured_region); + cairo_region_destroy (clip_region); CLUTTER_ACTOR_CLASS (meta_window_group_parent_class)->paint (actor); @@ -238,7 +267,7 @@ meta_window_group_paint (ClutterActor *actor) if (META_IS_WINDOW_ACTOR (child)) { MetaWindowActor *window_actor = META_WINDOW_ACTOR (child); - meta_window_actor_reset_visible_regions (window_actor); + meta_window_actor_reset_clip_regions (window_actor); } else if (META_IS_BACKGROUND_ACTOR (child)) { diff --git a/src/meta/meta-shaped-texture.h b/src/meta/meta-shaped-texture.h index 28fb5f603..54a917794 100644 --- a/src/meta/meta-shaped-texture.h +++ b/src/meta/meta-shaped-texture.h @@ -69,11 +69,12 @@ ClutterActor *meta_shaped_texture_new (void); void meta_shaped_texture_set_create_mipmaps (MetaShapedTexture *stex, gboolean create_mipmaps); -void meta_shaped_texture_update_area (MetaShapedTexture *stex, - int x, - int y, - int width, - int height); +gboolean meta_shaped_texture_update_area (MetaShapedTexture *stex, + int x, + int y, + int width, + int height, + cairo_region_t *unobscured_region); void meta_shaped_texture_set_pixmap (MetaShapedTexture *stex, Pixmap pixmap);