From 47144253e49e13a15a64e8d43577c7e2fdd64b38 Mon Sep 17 00:00:00 2001 From: "Jasper St. Pierre" Date: Thu, 21 Nov 2013 15:25:08 -0500 Subject: [PATCH] cullable: Turn cull_out / reset_culling into a separate interface Instead of hardcoded knowledge of certain classes in MetaWindowGroup, create a generic interface that all actors can implement to get parts of their regions culled out during redraw, without needing any special knowledge of how to handle a specific actor. The names now are a bit suspect. MetaBackgroundGroup is a simple MetaCullable that knows how to cull children, and MetaWindowGroup is the "toplevel" cullable that computes the initial two regions. A future cleanup here could be to merge MetaWindowGroup / MetaBackgroundGroup so that we only have a generic MetaSimpleCullable, and move the "toplevel" cullability to be a MetaCullableToplevel. https://bugzilla.gnome.org/show_bug.cgi?id=714706 --- doc/reference/meta-sections.txt | 17 ++ src/Makefile.am | 3 +- .../meta-background-actor-private.h | 3 - src/compositor/meta-background-actor.c | 59 +++--- .../meta-background-group-private.h | 11 - src/compositor/meta-background-group.c | 59 +++--- src/compositor/meta-cullable.c | 191 ++++++++++++++++++ src/compositor/meta-cullable.h | 68 +++++++ src/compositor/meta-window-actor-private.h | 5 - src/compositor/meta-window-actor.c | 34 ++-- src/compositor/meta-window-group.c | 116 ++--------- src/compositor/meta-window-group.h | 26 +-- 12 files changed, 379 insertions(+), 213 deletions(-) delete mode 100644 src/compositor/meta-background-group-private.h create mode 100644 src/compositor/meta-cullable.c create mode 100644 src/compositor/meta-cullable.h diff --git a/doc/reference/meta-sections.txt b/doc/reference/meta-sections.txt index 90f815249..7aa893177 100644 --- a/doc/reference/meta-sections.txt +++ b/doc/reference/meta-sections.txt @@ -388,6 +388,23 @@ MetaWindowActorPrivate meta_window_actor_get_type +
+meta-cullable +MetaCullable +MetaCullable +MetaCullableInterface +meta_cullable_cull_out +meta_cullable_reset_culling +meta_cullable_cull_out_children +meta_cullable_reset_culling_children + +META_TYPE_CULLABLE +META_CULLABLE +META_IS_CULLABLE +META_CULLABLE_GET_IFACE +meta_cullable_get_type +
+
prefs MetaPreference diff --git a/src/Makefile.am b/src/Makefile.am index 52adec315..ba4122e9d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -55,7 +55,8 @@ libmutter_la_SOURCES = \ compositor/meta-background-actor.c \ compositor/meta-background-actor-private.h \ compositor/meta-background-group.c \ - compositor/meta-background-group-private.h \ + compositor/meta-cullable.c \ + compositor/meta-cullable.h \ compositor/meta-module.c \ compositor/meta-module.h \ compositor/meta-plugin.c \ diff --git a/src/compositor/meta-background-actor-private.h b/src/compositor/meta-background-actor-private.h index 9ab074ebd..d48fb03d4 100644 --- a/src/compositor/meta-background-actor-private.h +++ b/src/compositor/meta-background-actor-private.h @@ -6,9 +6,6 @@ #include #include -void meta_background_actor_set_clip_region (MetaBackgroundActor *self, - cairo_region_t *clip_region); - cairo_region_t *meta_background_actor_get_clip_region (MetaBackgroundActor *self); #endif /* META_BACKGROUND_ACTOR_PRIVATE_H */ diff --git a/src/compositor/meta-background-actor.c b/src/compositor/meta-background-actor.c index 32b6007b3..20744587a 100644 --- a/src/compositor/meta-background-actor.c +++ b/src/compositor/meta-background-actor.c @@ -41,20 +41,35 @@ #include #include #include "meta-background-actor-private.h" +#include "meta-cullable.h" struct _MetaBackgroundActorPrivate { cairo_region_t *clip_region; }; -G_DEFINE_TYPE (MetaBackgroundActor, meta_background_actor, CLUTTER_TYPE_ACTOR); +static void cullable_iface_init (MetaCullableInterface *iface); + +G_DEFINE_TYPE_WITH_CODE (MetaBackgroundActor, meta_background_actor, CLUTTER_TYPE_ACTOR, + G_IMPLEMENT_INTERFACE (META_TYPE_CULLABLE, cullable_iface_init)); + +static void +set_clip_region (MetaBackgroundActor *self, + cairo_region_t *clip_region) +{ + MetaBackgroundActorPrivate *priv = self->priv; + + g_clear_pointer (&priv->clip_region, (GDestroyNotify) cairo_region_destroy); + if (clip_region) + priv->clip_region = cairo_region_copy (clip_region); +} static void meta_background_actor_dispose (GObject *object) { MetaBackgroundActor *self = META_BACKGROUND_ACTOR (object); - meta_background_actor_set_clip_region (self, NULL); + set_clip_region (self, NULL); G_OBJECT_CLASS (meta_background_actor_parent_class)->dispose (object); } @@ -166,31 +181,27 @@ meta_background_actor_new (void) return CLUTTER_ACTOR (self); } -/** - * meta_background_actor_set_clip_region: - * @self: a #MetaBackgroundActor - * @clip_region: (allow-none): the area of the actor (in allocate-relative - * coordinates) that is visible. - * - * Sets the area of the background that is unobscured by overlapping windows. - * This is used to optimize and only paint the visible portions. - */ -void -meta_background_actor_set_clip_region (MetaBackgroundActor *self, - cairo_region_t *clip_region) +static void +meta_background_actor_cull_out (MetaCullable *cullable, + cairo_region_t *unobscured_region, + cairo_region_t *clip_region) { - MetaBackgroundActorPrivate *priv; + MetaBackgroundActor *self = META_BACKGROUND_ACTOR (cullable); + set_clip_region (self, clip_region); +} - g_return_if_fail (META_IS_BACKGROUND_ACTOR (self)); +static void +meta_background_actor_reset_culling (MetaCullable *cullable) +{ + MetaBackgroundActor *self = META_BACKGROUND_ACTOR (cullable); + set_clip_region (self, NULL); +} - priv = self->priv; - - g_clear_pointer (&priv->clip_region, - (GDestroyNotify) - cairo_region_destroy); - - if (clip_region) - priv->clip_region = cairo_region_copy (clip_region); +static void +cullable_iface_init (MetaCullableInterface *iface) +{ + iface->cull_out = meta_background_actor_cull_out; + iface->reset_culling = meta_background_actor_reset_culling; } /** diff --git a/src/compositor/meta-background-group-private.h b/src/compositor/meta-background-group-private.h deleted file mode 100644 index 1ee7a6737..000000000 --- a/src/compositor/meta-background-group-private.h +++ /dev/null @@ -1,11 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -#ifndef META_BACKGROUND_GROUP_PRIVATE_H -#define META_BACKGROUND_GROUP_PRIVATE_H - -#include -#include - -void meta_background_group_set_clip_region (MetaBackgroundGroup *self, - cairo_region_t *visible_region); -#endif /* META_BACKGROUND_GROUP_PRIVATE_H */ diff --git a/src/compositor/meta-background-group.c b/src/compositor/meta-background-group.c index 092dd1889..2ff01d3d9 100644 --- a/src/compositor/meta-background-group.c +++ b/src/compositor/meta-background-group.c @@ -16,12 +16,13 @@ #include -#include "compositor-private.h" -#include "clutter-utils.h" -#include "meta-background-actor-private.h" -#include "meta-background-group-private.h" +#include +#include "meta-cullable.h" -G_DEFINE_TYPE (MetaBackgroundGroup, meta_background_group, CLUTTER_TYPE_ACTOR); +static void cullable_iface_init (MetaCullableInterface *iface); + +G_DEFINE_TYPE_WITH_CODE (MetaBackgroundGroup, meta_background_group, CLUTTER_TYPE_ACTOR, + G_IMPLEMENT_INTERFACE (META_TYPE_CULLABLE, cullable_iface_init)); static void meta_background_group_class_init (MetaBackgroundGroupClass *klass) @@ -29,43 +30,29 @@ meta_background_group_class_init (MetaBackgroundGroupClass *klass) } static void -meta_background_group_init (MetaBackgroundGroup *self) +meta_background_group_cull_out (MetaCullable *cullable, + cairo_region_t *unobscured_region, + cairo_region_t *clip_region) { + meta_cullable_cull_out_children (cullable, unobscured_region, clip_region); } -/** - * meta_background_group_set_clip_region: - * @self: a #MetaBackgroundGroup - * @region: (allow-none): the parts of the background to paint - * - * Sets the area of the backgrounds that is unobscured by overlapping windows. - * This is used to optimize and only paint the visible portions. - */ -void -meta_background_group_set_clip_region (MetaBackgroundGroup *self, - cairo_region_t *region) +static void +meta_background_group_reset_culling (MetaCullable *cullable) { - ClutterActor *child; - for (child = clutter_actor_get_first_child (CLUTTER_ACTOR (self)); - child != NULL; - child = clutter_actor_get_next_sibling (child)) - { - if (META_IS_BACKGROUND_ACTOR (child)) - { - meta_background_actor_set_clip_region (META_BACKGROUND_ACTOR (child), region); - } - else if (META_IS_BACKGROUND_GROUP (child)) - { - int x, y; + meta_cullable_reset_culling_children (cullable); +} - if (!meta_actor_is_untransformed (child, &x, &y)) - continue; +static void +cullable_iface_init (MetaCullableInterface *iface) +{ + iface->cull_out = meta_background_group_cull_out; + iface->reset_culling = meta_background_group_reset_culling; +} - cairo_region_translate (region, -x, -y); - meta_background_group_set_clip_region (META_BACKGROUND_GROUP (child), region); - cairo_region_translate (region, x, y); - } - } +static void +meta_background_group_init (MetaBackgroundGroup *self) +{ } ClutterActor * diff --git a/src/compositor/meta-cullable.c b/src/compositor/meta-cullable.c new file mode 100644 index 000000000..a139166b5 --- /dev/null +++ b/src/compositor/meta-cullable.c @@ -0,0 +1,191 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* + * Copyright (C) 2013 Red Hat + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Written by: + * Owen Taylor + * Ray Strode + * Jasper St. Pierre + */ + +#include "config.h" +#include "meta-cullable.h" +#include "clutter-utils.h" + +G_DEFINE_INTERFACE (MetaCullable, meta_cullable, CLUTTER_TYPE_ACTOR); + +/** + * SECTION:meta-cullable + * @title: MetaCullable + * @short_description: CPU culling operations for efficient drawing + * + * When we are painting a stack of 5-10 large actors, the standard + * bottom-to-top method of drawing every actor results in a tremendous + * amount of overdraw. If these actors are painting textures like + * windows, it can easily max out the available memory bandwidth on a + * low-end graphics chipset. It's even worse if window textures are + * being accessed over the AGP bus. + * + * #MetaCullable is our solution. The basic technique applied here is to + * do a pre-pass before painting where we walk each actor from top to bottom + * and ask each actor to "cull itself out". We pass in a region it can copy + * to clip its drawing to, and the actor can subtract its fully opaque pixels + * so that actors underneath know not to draw there as well. + */ + +/** + * meta_cullable_cull_out_children: + * @cullable: The #MetaCullable + * @unobscured_region: The unobscured region, as passed into cull_out() + * @clip_region: The clip region, as passed into cull_out() + * + * This is a helper method for actors that want to recurse over their + * child actors, and cull them out. + * + * See #MetaCullable and meta_cullable_cull_out() for more details. + */ +void +meta_cullable_cull_out_children (MetaCullable *cullable, + cairo_region_t *unobscured_region, + cairo_region_t *clip_region) +{ + ClutterActor *actor = CLUTTER_ACTOR (cullable); + ClutterActor *child; + ClutterActorIter iter; + + clutter_actor_iter_init (&iter, actor); + while (clutter_actor_iter_prev (&iter, &child)) + { + int x, y; + + if (!CLUTTER_ACTOR_IS_VISIBLE (child)) + continue; + + /* If an actor has effects applied, then that can change the area + * it paints and the opacity, so we no longer can figure out what + * portion of the actor is obscured and what portion of the screen + * it obscures, so we skip the actor. + * + * This has a secondary beneficial effect: if a ClutterOffscreenEffect + * is applied to an actor, then our clipped redraws interfere with the + * caching of the FBO - even if we only need to draw a small portion + * of the window right now, ClutterOffscreenEffect may use other portions + * of the FBO later. So, skipping actors with effects applied also + * prevents these bugs. + * + * Theoretically, we should check clutter_actor_get_offscreen_redirect() + * as well for the same reason, but omitted for simplicity in the + * hopes that no-one will do that. + */ + if (clutter_actor_has_effects (child)) + continue; + + if (!META_IS_CULLABLE (child)) + continue; + + if (!meta_actor_is_untransformed (child, &x, &y)) + continue; + + /* Temporarily move to the coordinate system of the actor */ + cairo_region_translate (unobscured_region, - x, - y); + cairo_region_translate (clip_region, - x, - y); + + meta_cullable_cull_out (META_CULLABLE (child), unobscured_region, clip_region); + + cairo_region_translate (unobscured_region, x, y); + cairo_region_translate (clip_region, x, y); + } +} + +/** + * meta_cullable_reset_culling_children: + * @cullable: The #MetaCullable + * + * This is a helper method for actors that want to recurse over their + * child actors, and cull them out. + * + * See #MetaCullable and meta_cullable_reset_culling() for more details. + */ +void +meta_cullable_reset_culling_children (MetaCullable *cullable) +{ + ClutterActor *actor = CLUTTER_ACTOR (cullable); + ClutterActor *child; + ClutterActorIter iter; + + clutter_actor_iter_init (&iter, actor); + while (clutter_actor_iter_next (&iter, &child)) + { + if (!META_IS_CULLABLE (child)) + continue; + + meta_cullable_reset_culling (META_CULLABLE (child)); + } +} + +static void +meta_cullable_default_init (MetaCullableInterface *iface) +{ +} + +/** + * meta_cullable_cull_out: + * @cullable: The #MetaCullable + * @unobscured_region: The unobscured region, in @cullable's space. + * @clip_region: The clip region, in @cullable's space. + * + * When #MetaWindowGroup is painted, we walk over its direct cullable + * children from top to bottom and ask themselves to "cull out". Cullables + * can use @unobscured_region and @clip_region to clip their drawing. Actors + * interested in eliminating overdraw should copy the @clip_region and only + * paint those parts, as everything else has been obscured by actors above it. + * + * Actors that may have fully opaque parts should also subtract out a region + * that is fully opaque from @unobscured_region and @clip_region. + * + * @unobscured_region and @clip_region are extremely similar. The difference + * is that @clip_region starts off with the stage's clip, if Clutter detects + * that we're doing a clipped redraw. @unobscured_region, however, starts off + * with the full stage size, so actors that may want to record what parts of + * their window are unobscured for e.g. scheduling repaints can do so. + * + * Actors that have children can also use the meta_cullable_cull_out_children() + * helper method to do a simple cull across all their children. + */ +void +meta_cullable_cull_out (MetaCullable *cullable, + cairo_region_t *unobscured_region, + cairo_region_t *clip_region) +{ + META_CULLABLE_GET_IFACE (cullable)->cull_out (cullable, unobscured_region, clip_region); +} + +/** + * meta_cullable_reset_culling: + * @cullable: The #MetaCullable + * + * Actors that copied data in their cull_out() implementation can now + * reset their data, as the paint is now over. Additional paints may be + * done by #ClutterClone or similar, and they should not be affected by + * the culling operation. + */ +void +meta_cullable_reset_culling (MetaCullable *cullable) +{ + META_CULLABLE_GET_IFACE (cullable)->reset_culling (cullable); +} diff --git a/src/compositor/meta-cullable.h b/src/compositor/meta-cullable.h new file mode 100644 index 000000000..5a8c215f1 --- /dev/null +++ b/src/compositor/meta-cullable.h @@ -0,0 +1,68 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + +/* + * Copyright (C) 2013 Red Hat + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Written by: + * Owen Taylor + * Ray Strode + * Jasper St. Pierre + */ + +#ifndef __META_CULLABLE_H__ +#define __META_CULLABLE_H__ + +#include + +G_BEGIN_DECLS + +#define META_TYPE_CULLABLE (meta_cullable_get_type ()) +#define META_CULLABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_CULLABLE, MetaCullable)) +#define META_IS_CULLABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_CULLABLE)) +#define META_CULLABLE_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), META_TYPE_CULLABLE, MetaCullableInterface)) + +typedef struct _MetaCullable MetaCullable; +typedef struct _MetaCullableInterface MetaCullableInterface; + +struct _MetaCullableInterface +{ + GTypeInterface g_iface; + + void (* cull_out) (MetaCullable *cullable, + cairo_region_t *unobscured_region, + cairo_region_t *clip_region); + void (* reset_culling) (MetaCullable *cullable); +}; + +GType meta_cullable_get_type (void); + +void meta_cullable_cull_out (MetaCullable *cullable, + cairo_region_t *unobscured_region, + cairo_region_t *clip_region); +void meta_cullable_reset_culling (MetaCullable *cullable); + +/* Utility methods for implementations */ +void meta_cullable_cull_out_children (MetaCullable *cullable, + cairo_region_t *unobscured_region, + cairo_region_t *clip_region); +void meta_cullable_reset_culling_children (MetaCullable *cullable); + +G_END_DECLS + +#endif /* __META_CULLABLE_H__ */ + diff --git a/src/compositor/meta-window-actor-private.h b/src/compositor/meta-window-actor-private.h index f10276b0f..441f05b2e 100644 --- a/src/compositor/meta-window-actor-private.h +++ b/src/compositor/meta-window-actor-private.h @@ -55,11 +55,6 @@ void meta_window_actor_set_updates_frozen (MetaWindowActor *self, void meta_window_actor_queue_frame_drawn (MetaWindowActor *self, gboolean no_delay_frame); -void meta_window_actor_cull_out (MetaWindowActor *self, - cairo_region_t *unobscured_region, - cairo_region_t *clip_region); -void meta_window_actor_reset_culling (MetaWindowActor *self); - void meta_window_actor_set_unobscured_region (MetaWindowActor *self, cairo_region_t *unobscured_region); diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c index 2a45b4196..2ae659ec8 100644 --- a/src/compositor/meta-window-actor.c +++ b/src/compositor/meta-window-actor.c @@ -32,6 +32,7 @@ #include "meta-texture-rectangle.h" #include "region-utils.h" #include "monitor-private.h" +#include "meta-cullable.h" enum { POSITION_CHANGED, @@ -191,7 +192,10 @@ static void do_send_frame_timings (MetaWindowActor *self, gint refresh_interval, gint64 presentation_time); -G_DEFINE_TYPE (MetaWindowActor, meta_window_actor, CLUTTER_TYPE_ACTOR); +static void cullable_iface_init (MetaCullableInterface *iface); + +G_DEFINE_TYPE_WITH_CODE (MetaWindowActor, meta_window_actor, CLUTTER_TYPE_ACTOR, + G_IMPLEMENT_INTERFACE (META_TYPE_CULLABLE, cullable_iface_init)); static void frame_data_free (FrameData *frame) @@ -1848,11 +1852,12 @@ meta_window_actor_set_clip_region_beneath (MetaWindowActor *self, } } -void -meta_window_actor_cull_out (MetaWindowActor *self, - cairo_region_t *unobscured_region, - cairo_region_t *clip_region) +static void +meta_window_actor_cull_out (MetaCullable *cullable, + cairo_region_t *unobscured_region, + cairo_region_t *clip_region) { + MetaWindowActor *self = META_WINDOW_ACTOR (cullable); MetaCompScreen *info = meta_screen_get_compositor_data (self->priv->screen); /* Don't do any culling for the unredirected window */ @@ -1875,16 +1880,10 @@ meta_window_actor_cull_out (MetaWindowActor *self, meta_window_actor_set_clip_region_beneath (self, clip_region); } -/** - * meta_window_actor_reset_culling: - * @self: a #MetaWindowActor - * - * Unsets the regions set by meta_window_actor_set_clip_region() and - * meta_window_actor_set_clip_region_beneath() - */ -void -meta_window_actor_reset_culling (MetaWindowActor *self) +static void +meta_window_actor_reset_culling (MetaCullable *cullable) { + MetaWindowActor *self = META_WINDOW_ACTOR (cullable); MetaWindowActorPrivate *priv = self->priv; meta_shaped_texture_set_clip_region (META_SHAPED_TEXTURE (priv->actor), @@ -1892,6 +1891,13 @@ meta_window_actor_reset_culling (MetaWindowActor *self) g_clear_pointer (&priv->shadow_clip, cairo_region_destroy); } +static void +cullable_iface_init (MetaCullableInterface *iface) +{ + iface->cull_out = meta_window_actor_cull_out; + iface->reset_culling = meta_window_actor_reset_culling; +} + static void check_needs_pixmap (MetaWindowActor *self) { diff --git a/src/compositor/meta-window-group.c b/src/compositor/meta-window-group.c index 1af5a2e61..468dd3223 100644 --- a/src/compositor/meta-window-group.c +++ b/src/compositor/meta-window-group.c @@ -11,8 +11,8 @@ #include "compositor-private.h" #include "meta-window-actor-private.h" #include "meta-window-group.h" -#include "meta-background-actor-private.h" -#include "meta-background-group-private.h" +#include "window-private.h" +#include "meta-cullable.h" struct _MetaWindowGroupClass { @@ -26,7 +26,10 @@ struct _MetaWindowGroup MetaScreen *screen; }; -G_DEFINE_TYPE (MetaWindowGroup, meta_window_group, CLUTTER_TYPE_ACTOR); +static void cullable_iface_init (MetaCullableInterface *iface); + +G_DEFINE_TYPE_WITH_CODE (MetaWindowGroup, meta_window_group, CLUTTER_TYPE_ACTOR, + G_IMPLEMENT_INTERFACE (META_TYPE_CULLABLE, cullable_iface_init)); /* Help macros to scale from OpenGL <-1,1> coordinates system to * window coordinates ranging [0,window-size]. Borrowed from clutter-utils.c @@ -87,103 +90,24 @@ painting_untransformed (MetaWindowGroup *window_group, } static void -meta_window_group_cull_out (MetaWindowGroup *group, - cairo_region_t *unobscured_region, - cairo_region_t *clip_region) +meta_window_group_cull_out (MetaCullable *cullable, + cairo_region_t *unobscured_region, + cairo_region_t *clip_region) { - ClutterActor *actor = CLUTTER_ACTOR (group); - ClutterActor *child; - ClutterActorIter iter; - - /* We walk the list from top to bottom (opposite of painting order), - * and subtract the opaque area of each window out of the visible - * region that we pass to the windows below. - */ - clutter_actor_iter_init (&iter, actor); - while (clutter_actor_iter_prev (&iter, &child)) - { - if (!CLUTTER_ACTOR_IS_VISIBLE (child)) - continue; - - /* If an actor has effects applied, then that can change the area - * it paints and the opacity, so we no longer can figure out what - * portion of the actor is obscured and what portion of the screen - * it obscures, so we skip the actor. - * - * This has a secondary beneficial effect: if a ClutterOffscreenEffect - * is applied to an actor, then our clipped redraws interfere with the - * caching of the FBO - even if we only need to draw a small portion - * of the window right now, ClutterOffscreenEffect may use other portions - * of the FBO later. So, skipping actors with effects applied also - * prevents these bugs. - * - * Theoretically, we should check clutter_actor_get_offscreen_redirect() - * as well for the same reason, but omitted for simplicity in the - * hopes that no-one will do that. - */ - if (clutter_actor_has_effects (child)) - continue; - - if (META_IS_WINDOW_ACTOR (child)) - { - int x, y; - - if (!meta_actor_is_untransformed (child, &x, &y)) - continue; - - /* 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_cull_out (META_WINDOW_ACTOR (child), unobscured_region, 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)) - { - int x, y; - - if (!meta_actor_is_untransformed (child, &x, &y)) - continue; - - cairo_region_translate (clip_region, - x, - y); - - if (META_IS_BACKGROUND_GROUP (child)) - meta_background_group_set_clip_region (META_BACKGROUND_GROUP (child), clip_region); - else - meta_background_actor_set_clip_region (META_BACKGROUND_ACTOR (child), clip_region); - - cairo_region_translate (clip_region, x, y); - } - } + meta_cullable_cull_out_children (cullable, unobscured_region, clip_region); } static void -meta_window_group_reset_culling (MetaWindowGroup *group) +meta_window_group_reset_culling (MetaCullable *cullable) { - ClutterActor *actor = CLUTTER_ACTOR (group); - ClutterActor *child; - ClutterActorIter iter; + meta_cullable_reset_culling_children (cullable); +} - /* Now that we are done painting, unset the visible regions (they will - * mess up painting clones of our actors) - */ - 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_reset_culling (window_actor); - } - else if (META_IS_BACKGROUND_ACTOR (child)) - { - MetaBackgroundActor *background_actor = META_BACKGROUND_ACTOR (child); - meta_background_actor_set_clip_region (background_actor, NULL); - } - } +static void +cullable_iface_init (MetaCullableInterface *iface) +{ + iface->cull_out = meta_window_group_cull_out; + iface->reset_culling = meta_window_group_reset_culling; } static void @@ -265,14 +189,14 @@ meta_window_group_paint (ClutterActor *actor) cairo_region_subtract_rectangle (clip_region, &unredirected_rect); } - meta_window_group_cull_out (window_group, unobscured_region, clip_region); + meta_cullable_cull_out (META_CULLABLE (window_group), unobscured_region, clip_region); cairo_region_destroy (unobscured_region); cairo_region_destroy (clip_region); CLUTTER_ACTOR_CLASS (meta_window_group_parent_class)->paint (actor); - meta_window_group_reset_culling (window_group); + meta_cullable_reset_culling (META_CULLABLE (window_group)); } static gboolean diff --git a/src/compositor/meta-window-group.h b/src/compositor/meta-window-group.h index 1c3eebf5c..d624ac68c 100644 --- a/src/compositor/meta-window-group.h +++ b/src/compositor/meta-window-group.h @@ -11,29 +11,9 @@ * MetaWindowGroup: * * This class is a subclass of ClutterActor with special handling for - * MetaWindowActor/MetaBackgroundActor/MetaBackgroundGroup when painting - * children. - * - * When we are painting a stack of 5-10 maximized windows, the - * standard bottom-to-top method of drawing every actor results in a - * tremendous amount of overdraw and can easily max out the available - * memory bandwidth on a low-end graphics chipset. It's even worse if - * window textures are being accessed over the AGP bus. - * - * The basic technique applied here is to do a pre-pass before painting - * where we walk window from top to bottom and compute the visible area - * at each step by subtracting out the windows above it. The visible - * area is passed to MetaWindowActor which uses it to clip the portion of - * the window which drawn and avoid redrawing the shadow if it is completely - * obscured. - * - * A caveat is that this is ineffective if applications are using ARGB - * visuals, since we have no way of knowing whether a window obscures - * the windows behind it or not. Alternate approaches using the depth - * or stencil buffer rather than client side regions might be able to - * handle alpha windows, but the combination of glAlphaFunc and stenciling - * tends not to be efficient except on newer cards. (And on newer cards - * we have lots of memory and bandwidth.) + * #MetaCullable when painting children. It uses code similar to + * meta_cullable_cull_out_children(), but also has additional special + * cases for the undirected window, and similar. */ #define META_TYPE_WINDOW_GROUP (meta_window_group_get_type ())