[mutter] Reduce overpaint in the window group
- From: Owen Taylor <otaylor src gnome org>
- To: svn-commits-list gnome org
- Subject: [mutter] Reduce overpaint in the window group
- Date: Thu, 9 Jul 2009 15:56:42 +0000 (UTC)
commit 83f8bfd2caa5b6001749b081372d97c21791fc8f
Author: Owen W. Taylor <otaylor fishsoup net>
Date: Mon Jun 29 14:30:26 2009 -0400
Reduce overpaint in the window group
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.
When we have opaque windows, we can go ahead and compute visibility
ourselves (in classic X-server fashion) and use that information to
restrict drawing obscured actors.
* Add MutterWindowGroup - a ClutterGroup subclass with logic
for figuring out obscured regions.
* Add mutter_window_get_obscured_region() to get the region
obscured by that window.
* Add mutter_shaped_texture_set_clip_region() to hint
a clip region to the painting code; this is set based on
the computed visible region of MutterWindowGroup.
* Add tidy_texture_frame_set_needs_paint() to hint that the
paint can be skipped entirely; this is used when we detect
that the window shadow is entirely obscured.
http://bugzilla.gnome.org/show_bug.cgi?id=587344
src/Makefile.am | 2 +
src/compositor/compositor.c | 3 +-
src/compositor/mutter-shaped-texture.c | 179 ++++++++++++++++++------
src/compositor/mutter-shaped-texture.h | 6 +
src/compositor/mutter-window-group.c | 194 +++++++++++++++++++++++++
src/compositor/mutter-window-group.h | 52 +++++++
src/compositor/mutter-window-private.h | 9 ++
src/compositor/mutter-window.c | 229 ++++++++++++++++++++++++++++++
src/compositor/tidy/tidy-texture-frame.c | 31 ++++
src/compositor/tidy/tidy-texture-frame.h | 3 +
10 files changed, 664 insertions(+), 44 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index 8c4a13b..5bb0fa6 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -31,6 +31,8 @@ mutter_SOURCES= \
compositor/mutter-shaped-texture.c \
compositor/mutter-window.c \
compositor/mutter-window-private.h \
+ compositor/mutter-window-group.c \
+ compositor/mutter-window-group.h \
compositor/shadow.c \
compositor/shadow.h \
compositor/mutter-shaped-texture.h \
diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c
index 9bcdb38..12f2aa2 100644
--- a/src/compositor/compositor.c
+++ b/src/compositor/compositor.c
@@ -12,6 +12,7 @@
#include "xprops.h"
#include "prefs.h"
#include "mutter-window-private.h"
+#include "mutter-window-group.h"
#include "../core/window-private.h" /* to check window->hidden */
#include "../core/display-private.h" /* for meta_display_lookup_x_window() */
#include <X11/extensions/shape.h>
@@ -420,7 +421,7 @@ meta_compositor_manage_screen (MetaCompositor *compositor,
XSelectInput (xdisplay, xwin, event_mask);
- info->window_group = clutter_group_new ();
+ info->window_group = mutter_window_group_new (screen);
info->overlay_group = clutter_group_new ();
info->hidden_group = clutter_group_new ();
diff --git a/src/compositor/mutter-shaped-texture.c b/src/compositor/mutter-shaped-texture.c
index 9a91150..fd35a61 100755
--- a/src/compositor/mutter-shaped-texture.c
+++ b/src/compositor/mutter-shaped-texture.c
@@ -57,10 +57,13 @@ struct _MutterShapedTexturePrivate
{
CoglHandle mask_texture;
CoglHandle material;
+ CoglHandle material_unshaped;
#if 1 /* see workaround comment in mutter_shaped_texture_paint */
CoglHandle material_workaround;
#endif
+ GdkRegion *clip_region;
+
guint mask_width, mask_height;
GArray *rectangles;
@@ -106,6 +109,11 @@ mutter_shaped_texture_dispose (GObject *object)
cogl_material_unref (priv->material);
priv->material = COGL_INVALID_HANDLE;
}
+ if (priv->material_unshaped != COGL_INVALID_HANDLE)
+ {
+ cogl_material_unref (priv->material_unshaped);
+ priv->material_unshaped = COGL_INVALID_HANDLE;
+ }
#if 1 /* see comment in mutter_shaped_texture_paint */
if (priv->material_workaround != COGL_INVALID_HANDLE)
{
@@ -114,6 +122,8 @@ mutter_shaped_texture_dispose (GObject *object)
}
#endif
+ mutter_shaped_texture_set_clip_region (self, NULL);
+
G_OBJECT_CLASS (mutter_shaped_texture_parent_class)->dispose (object);
}
@@ -253,6 +263,9 @@ mutter_shaped_texture_paint (ClutterActor *actor)
guint depth;
#endif
+ if (priv->clip_region && gdk_region_empty (priv->clip_region))
+ return;
+
if (!CLUTTER_ACTOR_IS_REALIZED (CLUTTER_ACTOR (stex)))
clutter_actor_realize (CLUTTER_ACTOR (stex));
@@ -264,61 +277,65 @@ mutter_shaped_texture_paint (ClutterActor *actor)
if (tex_width == 0 || tex_width == 0) /* no contents yet */
return;
- /* If there are no rectangles fallback to the regular paint
- method */
- if (priv->rectangles->len < 1)
- {
- CLUTTER_ACTOR_CLASS (mutter_shaped_texture_parent_class)
- ->paint (actor);
- return;
- }
-
if (paint_tex == COGL_INVALID_HANDLE)
return;
- mutter_shaped_texture_ensure_mask (stex);
-
- if (priv->material == COGL_INVALID_HANDLE)
+ if (priv->rectangles->len < 1)
{
- priv->material = cogl_material_new ();
+ /* If there are no rectangles use a single-layer texture */
- cogl_material_set_layer_combine (priv->material, 1,
- "RGBA = MODULATE (PREVIOUS, TEXTURE[A])",
- NULL);
- }
- material = priv->material;
+ if (priv->material_unshaped == COGL_INVALID_HANDLE)
+ priv->material_unshaped = cogl_material_new ();
-#if 1
- /* This was added as a workaround. It seems that with the intel
- * drivers when multi-texturing using an RGB TFP texture, the
- * texture is actually setup internally as an RGBA texture, where
- * the alpha channel is mostly 0.0 so you only see a shimmer of the
- * window. This workaround forcibly defines the alpha channel as
- * 1.0. Maybe there is some clutter/cogl state that is interacting
- * with this that is being overlooked, but for now this seems to
- * work. */
- g_object_get (stex, "pixmap-depth", &depth, NULL);
- if (depth == 24)
+ material = priv->material_unshaped;
+ }
+ else
{
- if (priv->material_workaround == COGL_INVALID_HANDLE)
- {
- material = priv->material_workaround = cogl_material_new ();
+ mutter_shaped_texture_ensure_mask (stex);
- cogl_material_set_layer_combine (material, 0,
- "RGB = MODULATE (TEXTURE, PREVIOUS)"
- "A = REPLACE (PREVIOUS)",
- NULL);
- cogl_material_set_layer_combine (material, 1,
+ if (priv->material == COGL_INVALID_HANDLE)
+ {
+ priv->material = cogl_material_new ();
+
+ cogl_material_set_layer_combine (priv->material, 1,
"RGBA = MODULATE (PREVIOUS, TEXTURE[A])",
NULL);
- }
+ }
+ material = priv->material;
- material = priv->material_workaround;
- }
+#if 1
+ /* This was added as a workaround. It seems that with the intel
+ * drivers when multi-texturing using an RGB TFP texture, the
+ * texture is actually setup internally as an RGBA texture, where
+ * the alpha channel is mostly 0.0 so you only see a shimmer of the
+ * window. This workaround forcibly defines the alpha channel as
+ * 1.0. Maybe there is some clutter/cogl state that is interacting
+ * with this that is being overlooked, but for now this seems to
+ * work. */
+ g_object_get (stex, "pixmap-depth", &depth, NULL);
+ if (depth == 24)
+ {
+ if (priv->material_workaround == COGL_INVALID_HANDLE)
+ {
+ material = priv->material_workaround = cogl_material_new ();
+
+ cogl_material_set_layer_combine (material, 0,
+ "RGB = MODULATE (TEXTURE, PREVIOUS)"
+ "A = REPLACE (PREVIOUS)",
+ NULL);
+ cogl_material_set_layer_combine (material, 1,
+ "RGBA = MODULATE (PREVIOUS, TEXTURE[A])",
+ NULL);
+ }
+
+ material = priv->material_workaround;
+ }
#endif
+ cogl_material_set_layer (material, 1, priv->mask_texture);
+ }
+
cogl_material_set_layer (material, 0, paint_tex);
- cogl_material_set_layer (material, 1, priv->mask_texture);
{
CoglColor color;
@@ -330,9 +347,51 @@ mutter_shaped_texture_paint (ClutterActor *actor)
cogl_set_source (material);
clutter_actor_get_allocation_box (actor, &alloc);
+
+ if (priv->clip_region)
+ {
+ GdkRectangle *rects;
+ int n_rects;
+ int i;
+
+ /* Limit to how many separate rectangles we'll draw; beyond this just
+ * fall back and draw the whole thing */
+# define MAX_RECTS 16
+
+ /* Would be nice to be able to check the number of rects first */
+ gdk_region_get_rectangles (priv->clip_region, &rects, &n_rects);
+ if (n_rects > MAX_RECTS)
+ {
+ g_free (rects);
+ /* Fall through to following code */
+ }
+ else
+ {
+ float coords[MAX_RECTS * 8];
+ for (i = 0; i < n_rects; i++)
+ {
+ GdkRectangle *rect = &rects[i];
+
+ coords[i * 8 + 0] = rect->x;
+ coords[i * 8 + 1] = rect->y;
+ coords[i * 8 + 2] = rect->x + rect->width;
+ coords[i * 8 + 3] = rect->y + rect->height;
+ coords[i * 8 + 4] = rect->x / (alloc.x2 - alloc.x1);
+ coords[i * 8 + 5] = rect->y / (alloc.y2 - alloc.y1);
+ coords[i * 8 + 6] = (rect->x + rect->width) / (alloc.x2 - alloc.x1);
+ coords[i * 8 + 7] = (rect->y + rect->height) / (alloc.y2 - alloc.y1);
+ }
+
+ g_free (rects);
+
+ cogl_rectangles_with_texture_coords (coords, n_rects);
+ return;
+ }
+ }
+
cogl_rectangle (0, 0,
- alloc.x2 - alloc.x1,
- alloc.y2 - alloc.y1);
+ alloc.x2 - alloc.x1,
+ alloc.y2 - alloc.y1);
}
static void
@@ -426,3 +485,37 @@ mutter_shaped_texture_add_rectangles (MutterShapedTexture *stex,
mutter_shaped_texture_dirty_mask (stex);
clutter_actor_queue_redraw (CLUTTER_ACTOR (stex));
}
+
+/**
+ * mutter_shaped_texture_set_clip_region:
+ * @frame: a #TidyTextureframe
+ * @clip_region: (transfer full): the region of the texture that
+ * is visible and should be painted. OWNERSHIP IS ASSUMED BY
+ * THE FUNCTION (for efficiency to avoid a copy.)
+ *
+ * Provides a hint to the texture about what areas of the texture
+ * are not completely obscured and thus need to be painted. This
+ * is an optimization and is not supposed to have any effect on
+ * the output.
+ *
+ * Typically a parent container will set the clip region before
+ * painting its children, and then unset it afterwards.
+ */
+void
+mutter_shaped_texture_set_clip_region (MutterShapedTexture *stex,
+ GdkRegion *clip_region)
+{
+ MutterShapedTexturePrivate *priv;
+
+ g_return_if_fail (MUTTER_IS_SHAPED_TEXTURE (stex));
+
+ priv = stex->priv;
+
+ if (priv->clip_region)
+ {
+ gdk_region_destroy (priv->clip_region);
+ priv->clip_region = NULL;
+ }
+
+ priv->clip_region = clip_region;
+}
diff --git a/src/compositor/mutter-shaped-texture.h b/src/compositor/mutter-shaped-texture.h
index 7e55309..3310bc5 100644
--- a/src/compositor/mutter-shaped-texture.h
+++ b/src/compositor/mutter-shaped-texture.h
@@ -31,6 +31,8 @@
#include <clutter/glx/clutter-glx.h>
#endif /* HAVE_GLX_TEXTURE_PIXMAP */
+#include <gdk/gdkregion.h>
+
G_BEGIN_DECLS
#define MUTTER_TYPE_SHAPED_TEXTURE \
@@ -90,6 +92,10 @@ void mutter_shaped_texture_add_rectangles (MutterShapedTexture *stex,
size_t num_rects,
const XRectangle *rects);
+/* Assumes ownership of clip_region */
+void mutter_shaped_texture_set_clip_region (MutterShapedTexture *stex,
+ GdkRegion *clip_region);
+
G_END_DECLS
#endif /* __MUTTER_SHAPED_TEXTURE_H__ */
diff --git a/src/compositor/mutter-window-group.c b/src/compositor/mutter-window-group.c
new file mode 100644
index 0000000..2436edf
--- /dev/null
+++ b/src/compositor/mutter-window-group.c
@@ -0,0 +1,194 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+#define _ISOC99_SOURCE /* for roundf */
+#include <math.h>
+
+#include "mutter-window-private.h"
+#include "mutter-window-group.h"
+
+struct _MutterWindowGroupClass
+{
+ ClutterGroupClass parent_class;
+};
+
+struct _MutterWindowGroup
+{
+ ClutterGroup parent;
+
+ MetaScreen *screen;
+};
+
+G_DEFINE_TYPE (MutterWindowGroup, mutter_window_group, CLUTTER_TYPE_GROUP);
+
+/* We want to find out if the window is "close enough" to
+ * 1:1 transform. We do that by converting the transformed coordinates
+ * to 24.8 fixed-point before checking if they look right.
+ */
+static inline int
+round_to_fixed (float x)
+{
+ return roundf (x * 256);
+}
+
+/* We can only (easily) apply our logic for figuring out what a window
+ * obscures if is not transformed. This function does that check and
+ * as a side effect gets the position of the upper-left corner of the
+ * actors.
+ *
+ * (We actually could handle scaled and non-integrally positioned actors
+ * too as long as they weren't shaped - no filtering is done at the
+ * edges so a rectangle stays a rectangle. But the gain from that is
+ * small, especally since most of our windows are shaped. The simple
+ * case we handle here is the case that matters when the user is just
+ * using the desktop normally.)
+ *
+ * If we assume that the window group is untransformed (it better not
+ * be!) then we could also make this determination by checking directly
+ * if the actor itself is rotated, scaled, or at a non-integral position.
+ * However, the criterion for "close enough" in that case get trickier,
+ * since, for example, the allowed rotation depends on the size of
+ * actor. The approach we take here is to just require everything
+ * to be within 1/256th of a pixel.
+ */
+static gboolean
+actor_is_untransformed (ClutterActor *actor,
+ int *x_origin,
+ int *y_origin)
+{
+ gfloat widthf, heightf;
+ int width, height;
+ ClutterVertex verts[4];
+ int v0x, v0y, v1x, v1y, v2x, v2y, v3x, v3y;
+ int x, y;
+
+ clutter_actor_get_size (actor, &widthf, &heightf);
+ width = round_to_fixed (widthf); height = round_to_fixed (heightf);
+
+ clutter_actor_get_abs_allocation_vertices (actor, verts);
+ v0x = round_to_fixed (verts[0].x); v0y = round_to_fixed (verts[0].y);
+ v1x = round_to_fixed (verts[1].x); v1y = round_to_fixed (verts[1].y);
+ v2x = round_to_fixed (verts[2].x); v2y = round_to_fixed (verts[2].y);
+ v3x = round_to_fixed (verts[3].x); v3y = round_to_fixed (verts[3].y);
+
+ /* Using shifting for converting fixed => int, gets things right for
+ * negative values. / 256. wouldn't do the same
+ */
+ x = v0x >> 8;
+ y = v0y >> 8;
+
+ /* At integral coordinates? */
+ if (x * 256 != v0x || y * 256 != v0y)
+ return FALSE;
+
+ /* Not scaled? */
+ if (v1x - v0x != width || v2y - v0y != height)
+ return FALSE;
+
+ /* Not rotated/skewed? */
+ if (v0x != v2x || v0y != v1y ||
+ v3x != v1x || v3y != v2y)
+ return FALSE;
+
+ *x_origin = x;
+ *y_origin = y;
+
+ return TRUE;
+}
+
+static void
+mutter_window_group_paint (ClutterActor *actor)
+{
+ MutterWindowGroup *window_group = MUTTER_WINDOW_GROUP (actor);
+ GdkRegion *visible_region;
+ GdkRectangle screen_rect = { 0 };
+ GList *children, *l;
+
+ /* 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.
+ */
+ children = clutter_container_get_children (CLUTTER_CONTAINER (actor));
+ children = g_list_reverse (children);
+
+ /* Start off with the full screen area (for a multihead setup, we
+ * might want to use a more accurate union of the monitors to avoid
+ * painting in holes from mismatched monitor sizes. That's just an
+ * optimization, however.)
+ */
+ meta_screen_get_size (window_group->screen, &screen_rect.width, &screen_rect.height);
+ visible_region = gdk_region_rectangle (&screen_rect);
+
+ for (l = children; l; l = l->next)
+ {
+ MutterWindow *cw;
+ gboolean x, y;
+
+ if (!MUTTER_IS_WINDOW (l->data) || !CLUTTER_ACTOR_IS_VISIBLE (l->data))
+ continue;
+
+ cw = l->data;
+
+ if (!actor_is_untransformed (CLUTTER_ACTOR (cw), &x, &y))
+ continue;
+
+ /* Temporarily move to the coordinate system of the actor */
+ gdk_region_offset (visible_region, - x, - y);
+
+ mutter_window_set_visible_region (cw, visible_region);
+
+ if (clutter_actor_get_paint_opacity (actor) == 0xff)
+ {
+ GdkRegion *obscured_region = mutter_window_get_obscured_region (cw);
+ if (obscured_region)
+ gdk_region_subtract (visible_region, obscured_region);
+ }
+
+ mutter_window_set_visible_region_beneath (cw, visible_region);
+ gdk_region_offset (visible_region, x, y);
+ }
+
+ gdk_region_destroy (visible_region);
+
+ CLUTTER_ACTOR_CLASS (mutter_window_group_parent_class)->paint (actor);
+
+ /* Now that we are done painting, unset the visible regions (they will
+ * mess up painting clones of our actors)
+ */
+ for (l = children; l; l = l->next)
+ {
+ MutterWindow *cw;
+
+ if (!MUTTER_IS_WINDOW (l->data))
+ continue;
+
+ cw = l->data;
+ mutter_window_reset_visible_regions (cw);
+ }
+
+ g_list_free (children);
+}
+
+static void
+mutter_window_group_class_init (MutterWindowGroupClass *klass)
+{
+ ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+
+ actor_class->paint = mutter_window_group_paint;
+}
+
+static void
+mutter_window_group_init (MutterWindowGroup *window_group)
+{
+}
+
+ClutterActor *
+mutter_window_group_new (MetaScreen *screen)
+{
+ MutterWindowGroup *window_group;
+
+ window_group = g_object_new (MUTTER_TYPE_WINDOW_GROUP, NULL);
+
+ window_group->screen = screen;
+
+ return CLUTTER_ACTOR (window_group);
+}
diff --git a/src/compositor/mutter-window-group.h b/src/compositor/mutter-window-group.h
new file mode 100644
index 0000000..59edc3b
--- /dev/null
+++ b/src/compositor/mutter-window-group.h
@@ -0,0 +1,52 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+#ifndef MUTTER_WINDOW_GROUP_H
+#define MUTTER_WINDOW_GROUP_H
+
+#include <clutter/clutter.h>
+
+#include "screen.h"
+
+/**
+ * MutterWindowGroup:
+ *
+ * This class is a subclass of ClutterGroup with special handling for
+ * MutterWindow when painting the group. 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 MutterWindow 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.)
+ */
+
+#define MUTTER_TYPE_WINDOW_GROUP (mutter_window_group_get_type ())
+#define MUTTER_WINDOW_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MUTTER_TYPE_WINDOW_GROUP, MutterWindowGroup))
+#define MUTTER_WINDOW_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MUTTER_TYPE_WINDOW_GROUP, MutterWindowGroupClass))
+#define MUTTER_IS_WINDOW_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MUTTER_TYPE_WINDOW_GROUP))
+#define MUTTER_IS_WINDOW_GROU_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MUTTER_TYPE_WINDOW_GROUP))
+#define MUTTER_WINDOW_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MUTTER_TYPE_WINDOW_GROUP, MutterWindowGroupClass))
+
+typedef struct _MutterWindowGroup MutterWindowGroup;
+typedef struct _MutterWindowGroupClass MutterWindowGroupClass;
+typedef struct _MutterWindowGroupPrivate MutterWindowGroupPrivate;
+
+GType mutter_window_group_get_type (void);
+
+ClutterActor *mutter_window_group_new (MetaScreen *screen);
+
+#endif /* MUTTER_WINDOW_GROUP_H */
diff --git a/src/compositor/mutter-window-private.h b/src/compositor/mutter-window-private.h
index 2cf5d98..3c0d0f9 100644
--- a/src/compositor/mutter-window-private.h
+++ b/src/compositor/mutter-window-private.h
@@ -4,6 +4,7 @@
#define MUTTER_WINDOW_PRIVATE_H
#include <X11/extensions/Xdamage.h>
+#include <gdk/gdk.h>
#include "compositor-mutter.h"
MutterWindow *mutter_window_new (MetaWindow *window);
@@ -36,6 +37,14 @@ void mutter_window_update_opacity (MutterWindow *cw);
void mutter_window_mapped (MutterWindow *cw);
void mutter_window_unmapped (MutterWindow *cw);
+GdkRegion *mutter_window_get_obscured_region (MutterWindow *cw);
+
+void mutter_window_set_visible_region (MutterWindow *cw,
+ GdkRegion *visible_region);
+void mutter_window_set_visible_region_beneath (MutterWindow *cw,
+ GdkRegion *beneath_region);
+void mutter_window_reset_visible_regions (MutterWindow *cw);
+
void mutter_window_effect_completed (MutterWindow *actor,
gulong event);
diff --git a/src/compositor/mutter-window.c b/src/compositor/mutter-window.c
index 3270190..0bcd222 100644
--- a/src/compositor/mutter-window.c
+++ b/src/compositor/mutter-window.c
@@ -1,5 +1,8 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#define _ISOC99_SOURCE /* for roundf */
+#include <math.h>
+
#include <X11/extensions/shape.h>
#include <X11/extensions/Xcomposite.h>
#include <X11/extensions/Xdamage.h>
@@ -18,6 +21,7 @@
#include "mutter-shaped-texture.h"
#include "mutter-window-private.h"
#include "shadow.h"
+#include "tidy/tidy-texture-frame.h"
struct _MutterWindowPrivate
{
@@ -38,6 +42,12 @@ struct _MutterWindowPrivate
gchar * desc;
+ /* If the window is shaped, a region that matches the shape */
+ GdkRegion *shape_region;
+ /* A rectangular region with the unshaped extends of the window
+ * texture */
+ GdkRegion *bounding_region;
+
/*
* These need to be counters rather than flags, since more plugins
* can implement same effect; the practicality of stacking effects
@@ -91,6 +101,8 @@ static void mutter_window_get_property (GObject *object,
static void mutter_window_detach (MutterWindow *self);
static gboolean mutter_window_has_shadow (MutterWindow *self);
+static void mutter_window_clear_shape_region (MutterWindow *self);
+static void mutter_window_clear_bounding_region (MutterWindow *self);
static gboolean is_shaped (MetaDisplay *display,
Window xwindow);
@@ -388,6 +400,9 @@ mutter_window_dispose (GObject *object)
mutter_window_detach (self);
+ mutter_window_clear_shape_region (self);
+ mutter_window_clear_bounding_region (self);
+
if (priv->damage != None)
{
meta_error_trap_push (display);
@@ -1214,6 +1229,214 @@ mutter_window_unmapped (MutterWindow *self)
}
static void
+mutter_window_clear_shape_region (MutterWindow *self)
+{
+ MutterWindowPrivate *priv = self->priv;
+
+ if (priv->shape_region)
+ {
+ gdk_region_destroy (priv->shape_region);
+ priv->shape_region = NULL;
+ }
+}
+
+static void
+mutter_window_clear_bounding_region (MutterWindow *self)
+{
+ MutterWindowPrivate *priv = self->priv;
+
+ if (priv->bounding_region)
+ {
+ gdk_region_destroy (priv->bounding_region);
+ priv->bounding_region = NULL;
+ }
+}
+
+static void
+mutter_window_update_bounding_region (MutterWindow *self,
+ int width,
+ int height)
+{
+ MutterWindowPrivate *priv = self->priv;
+ GdkRectangle bounding_rectangle = { 0, 0, width, height };
+
+ mutter_window_clear_bounding_region (self);
+
+ priv->bounding_region = gdk_region_rectangle (&bounding_rectangle);
+}
+
+static void
+mutter_window_update_shape_region (MutterWindow *self,
+ int n_rects,
+ XRectangle *rects)
+{
+ MutterWindowPrivate *priv = self->priv;
+ int i;
+
+ mutter_window_clear_shape_region (self);
+
+ priv->shape_region = gdk_region_new ();
+ for (i = 0; i < n_rects; i++)
+ {
+ GdkRectangle rect = { rects[i].x, rects[i].y, rects[i].width, rects[i].height };
+ gdk_region_union_with_rect (priv->shape_region, &rect);
+ }
+}
+
+/**
+ * mutter_window_get_obscured_region:
+ * @self: a #MutterWindow
+ *
+ * Gets the region that is completely obscured by the window. Coordinates
+ * are relative to the upper-left of the window.
+ *
+ * Return value: (transfer none): the area obscured by the window,
+ * %NULL is the same as an empty region.
+ */
+GdkRegion *
+mutter_window_get_obscured_region (MutterWindow *self)
+{
+ MutterWindowPrivate *priv = self->priv;
+
+ if (!priv->argb32 && priv->back_pixmap)
+ {
+ if (priv->shaped)
+ return priv->shape_region;
+ else
+ return priv->bounding_region;
+ }
+ else
+ return NULL;
+}
+
+#if 0
+/* Print out a region; useful for debugging */
+static void
+dump_region (GdkRegion *region)
+{
+ GdkRectangle *rects;
+ int n_rects;
+ int i;
+
+ gdk_region_get_rectangles (region, &rects, &n_rects);
+ g_print ("[");
+ for (i = 0; i < n_rects; i++)
+ {
+ g_print ("+%d+%dx%dx%d ",
+ rects[i].x, rects[i].y, rects[i].width, rects[i].height);
+ }
+ g_print ("]\n");
+ g_free (rects);
+}
+#endif
+
+/**
+ * mutter_window_set_visible_region:
+ * @self: a #MutterWindow
+ * @visible_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.
+ * This will be set before painting then unset afterwards.
+ */
+void
+mutter_window_set_visible_region (MutterWindow *self,
+ GdkRegion *visible_region)
+{
+ MutterWindowPrivate *priv = self->priv;
+ GdkRegion *texture_clip_region = NULL;
+
+ /* Get the area of the window texture that would be drawn if
+ * we weren't obscured at all
+ */
+ if (priv->shaped)
+ {
+ if (priv->shape_region)
+ texture_clip_region = gdk_region_copy (priv->shape_region);
+ }
+ else
+ {
+ if (priv->bounding_region)
+ texture_clip_region = gdk_region_copy (priv->bounding_region);
+ }
+
+ if (!texture_clip_region)
+ texture_clip_region = gdk_region_new ();
+
+ /* Then intersect that with the visible region to get the region
+ * that we actually need to redraw.
+ */
+ gdk_region_intersect (texture_clip_region, visible_region);
+
+ /* Assumes ownership */
+ mutter_shaped_texture_set_clip_region (MUTTER_SHAPED_TEXTURE (priv->actor),
+ texture_clip_region);
+}
+
+/**
+ * mutter_window_set_visible_region_beneath:
+ * @self: a #MutterWindow
+ * @visible_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
+ * 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
+mutter_window_set_visible_region_beneath (MutterWindow *self,
+ GdkRegion *beneath_region)
+{
+ MutterWindowPrivate *priv = self->priv;
+
+ if (priv->shadow)
+ {
+ GdkRectangle shadow_rect;
+ ClutterActorBox box;
+ GdkOverlapType overlap;
+
+ /* We could compute an full clip region as we do for the window
+ * texture, but the shadow is relatively cheap to draw, and
+ * a little more complex to clip, so we just catch the case where
+ * the shadow is completely obscured and doesn't need to be drawn
+ * at all.
+ */
+ clutter_actor_get_allocation_box (priv->shadow, &box);
+
+ shadow_rect.x = roundf (box.x1);
+ shadow_rect.y = roundf (box.y1);
+ shadow_rect.width = roundf (box.x2 - box.x1);
+ shadow_rect.height = roundf (box.y2 - box.y1);
+
+ overlap = gdk_region_rect_in (beneath_region, &shadow_rect);
+
+ tidy_texture_frame_set_needs_paint (TIDY_TEXTURE_FRAME (priv->shadow),
+ overlap != GDK_OVERLAP_RECTANGLE_OUT);
+ }
+}
+
+/**
+ * mutter_window_reset_visible_regions:
+ * @self: a #MutterWindow
+ *
+ * Unsets the regions set by mutter_window_reset_visible_region() and
+ *mutter_window_reset_visible_region_beneath()
+ */
+void
+mutter_window_reset_visible_regions (MutterWindow *self)
+{
+ MutterWindowPrivate *priv = self->priv;
+
+ mutter_shaped_texture_set_clip_region (MUTTER_SHAPED_TEXTURE (priv->actor),
+ NULL);
+ if (priv->shadow)
+ tidy_texture_frame_set_needs_paint (TIDY_TEXTURE_FRAME (priv->shadow), TRUE);
+}
+
+static void
check_needs_repair (MutterWindow *self)
{
MutterWindowPrivate *priv = self->priv;
@@ -1268,6 +1491,7 @@ check_needs_repair (MutterWindow *self)
if (priv->back_pixmap == None)
{
meta_verbose ("Unable to get named pixmap for %p\n", self);
+ mutter_window_update_bounding_region (self, 0, 0);
return;
}
@@ -1293,6 +1517,8 @@ check_needs_repair (MutterWindow *self)
if (priv->shadow)
clutter_actor_set_size (priv->shadow, pxm_width, pxm_height);
+ mutter_window_update_bounding_region (self, pxm_width, pxm_height);
+
full = TRUE;
}
@@ -1389,6 +1615,7 @@ check_needs_reshape (MutterWindow *self)
return;
mutter_shaped_texture_clear_rectangles (MUTTER_SHAPED_TEXTURE (priv->actor));
+ mutter_window_clear_shape_region (self);
#ifdef HAVE_SHAPE
if (priv->shaped)
@@ -1408,6 +1635,8 @@ check_needs_reshape (MutterWindow *self)
mutter_shaped_texture_add_rectangles (MUTTER_SHAPED_TEXTURE (priv->actor),
n_rects, rects);
+ mutter_window_update_shape_region (self, n_rects, rects);
+
XFree (rects);
}
}
diff --git a/src/compositor/tidy/tidy-texture-frame.c b/src/compositor/tidy/tidy-texture-frame.c
index 879cb61..1ea70ec 100644
--- a/src/compositor/tidy/tidy-texture-frame.c
+++ b/src/compositor/tidy/tidy-texture-frame.c
@@ -64,6 +64,8 @@ struct _TidyTextureFramePrivate
gfloat bottom;
CoglHandle material;
+
+ guint needs_paint : 1;
};
static void
@@ -175,6 +177,9 @@ tidy_texture_frame_paint (ClutterActor *self)
if (G_UNLIKELY (priv->parent_texture == NULL))
return;
+ if (!priv->needs_paint)
+ return;
+
/* parent texture may have been hidden, so need to make sure it gets
* realized
*/
@@ -608,3 +613,29 @@ tidy_texture_frame_get_frame (TidyTextureFrame *frame,
if (left)
*left = priv->left;
}
+
+/**
+ * tidy_texture_frame_set_needs_paint:
+ * @frame: a #TidyTextureframe
+ * @needs_paint: if %FALSE, the paint will be skipped
+ *
+ * Provides a hint to the texture frame that it is totally obscured
+ * and doesn't need to be painted. This would typically be called
+ * by a parent container if it detects the condition prior to
+ * painting its children and then unset afterwards.
+ *
+ * Since it is not supposed to have any effect on display, it does
+ * not queue a repaint.
+ */
+void
+tidy_texture_frame_set_needs_paint (TidyTextureFrame *frame,
+ gboolean needs_paint)
+{
+ TidyTextureFramePrivate *priv;
+
+ g_return_if_fail (TIDY_IS_TEXTURE_FRAME (frame));
+
+ priv = frame->priv;
+
+ priv->needs_paint = needs_paint;
+}
diff --git a/src/compositor/tidy/tidy-texture-frame.h b/src/compositor/tidy/tidy-texture-frame.h
index 0c54bfe..71dd40c 100644
--- a/src/compositor/tidy/tidy-texture-frame.h
+++ b/src/compositor/tidy/tidy-texture-frame.h
@@ -76,6 +76,9 @@ void tidy_texture_frame_get_frame (TidyTextureFrame *frame,
gfloat *bottom,
gfloat *left);
+void tidy_texture_frame_set_needs_paint (TidyTextureFrame *frame,
+ gboolean needs_paint);
+
G_END_DECLS
#endif /* _HAVE_TIDY_TEXTURE_FRAME_H */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]