[mutter/wip/culling: 5/8] cullable: Turn cull_out / reset_culling into a separate interface



commit 47144253e49e13a15a64e8d43577c7e2fdd64b38
Author: Jasper St. Pierre <jstpierre mecheye net>
Date:   Thu Nov 21 15:25:08 2013 -0500

    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 +-
 src/compositor/meta-background-actor-private.h |    3 -
 src/compositor/meta-background-actor.c         |   59 +++++---
 src/compositor/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(-)
---
diff --git a/doc/reference/meta-sections.txt b/doc/reference/meta-sections.txt
index 90f8152..7aa8931 100644
--- a/doc/reference/meta-sections.txt
+++ b/doc/reference/meta-sections.txt
@@ -389,6 +389,23 @@ meta_window_actor_get_type
 </SECTION>
 
 <SECTION>
+<FILE>meta-cullable</FILE>
+<TITLE>MetaCullable</TITLE>
+MetaCullable
+MetaCullableInterface
+meta_cullable_cull_out
+meta_cullable_reset_culling
+meta_cullable_cull_out_children
+meta_cullable_reset_culling_children
+<SUBSECTION Standard>
+META_TYPE_CULLABLE
+META_CULLABLE
+META_IS_CULLABLE
+META_CULLABLE_GET_IFACE
+meta_cullable_get_type
+</SECTION>
+
+<SECTION>
 <FILE>prefs</FILE>
 MetaPreference
 MetaPrefsChangedFunc
diff --git a/src/Makefile.am b/src/Makefile.am
index 52adec3..ba4122e 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 9ab074e..d48fb03 100644
--- a/src/compositor/meta-background-actor-private.h
+++ b/src/compositor/meta-background-actor-private.h
@@ -6,9 +6,6 @@
 #include <meta/screen.h>
 #include <meta/meta-background-actor.h>
 
-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 32b6007..2074458 100644
--- a/src/compositor/meta-background-actor.c
+++ b/src/compositor/meta-background-actor.c
@@ -41,20 +41,35 @@
 #include <meta/errors.h>
 #include <meta/meta-background.h>
 #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;
-
-  g_return_if_fail (META_IS_BACKGROUND_ACTOR (self));
-
-  priv = self->priv;
+  MetaBackgroundActor *self = META_BACKGROUND_ACTOR (cullable);
+  set_clip_region (self, clip_region);
+}
 
-  g_clear_pointer (&priv->clip_region,
-                   (GDestroyNotify)
-                   cairo_region_destroy);
+static void
+meta_background_actor_reset_culling (MetaCullable *cullable)
+{
+  MetaBackgroundActor *self = META_BACKGROUND_ACTOR (cullable);
+  set_clip_region (self, NULL);
+}
 
-  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.c b/src/compositor/meta-background-group.c
index 092dd18..2ff01d3 100644
--- a/src/compositor/meta-background-group.c
+++ b/src/compositor/meta-background-group.c
@@ -16,12 +16,13 @@
 
 #include <config.h>
 
-#include "compositor-private.h"
-#include "clutter-utils.h"
-#include "meta-background-actor-private.h"
-#include "meta-background-group-private.h"
+#include <meta/meta-background-group.h>
+#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 0000000..a139166
--- /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 <otaylor redhat com>
+ *     Ray Strode <rstrode redhat com>
+ *     Jasper St. Pierre <jstpierre mecheye net>
+ */
+
+#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 0000000..5a8c215
--- /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 <otaylor redhat com>
+ *     Ray Strode <rstrode redhat com>
+ *     Jasper St. Pierre <jstpierre mecheye net>
+ */
+
+#ifndef __META_CULLABLE_H__
+#define __META_CULLABLE_H__
+
+#include <clutter/clutter.h>
+
+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 f10276b..441f05b 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 2a45b41..2ae659e 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),
@@ -1893,6 +1892,13 @@ meta_window_actor_reset_culling (MetaWindowActor *self)
 }
 
 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)
 {
   MetaWindowActorPrivate *priv     = self->priv;
diff --git a/src/compositor/meta-window-group.c b/src/compositor/meta-window-group.c
index 1af5a2e..468dd32 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 1c3eebf..d624ac6 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 ())


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]