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
This commit is contained in:
parent
a454ad5c41
commit
83f8bfd2ca
@ -31,6 +31,8 @@ mutter_SOURCES= \
|
|||||||
compositor/mutter-shaped-texture.c \
|
compositor/mutter-shaped-texture.c \
|
||||||
compositor/mutter-window.c \
|
compositor/mutter-window.c \
|
||||||
compositor/mutter-window-private.h \
|
compositor/mutter-window-private.h \
|
||||||
|
compositor/mutter-window-group.c \
|
||||||
|
compositor/mutter-window-group.h \
|
||||||
compositor/shadow.c \
|
compositor/shadow.c \
|
||||||
compositor/shadow.h \
|
compositor/shadow.h \
|
||||||
compositor/mutter-shaped-texture.h \
|
compositor/mutter-shaped-texture.h \
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include "xprops.h"
|
#include "xprops.h"
|
||||||
#include "prefs.h"
|
#include "prefs.h"
|
||||||
#include "mutter-window-private.h"
|
#include "mutter-window-private.h"
|
||||||
|
#include "mutter-window-group.h"
|
||||||
#include "../core/window-private.h" /* to check window->hidden */
|
#include "../core/window-private.h" /* to check window->hidden */
|
||||||
#include "../core/display-private.h" /* for meta_display_lookup_x_window() */
|
#include "../core/display-private.h" /* for meta_display_lookup_x_window() */
|
||||||
#include <X11/extensions/shape.h>
|
#include <X11/extensions/shape.h>
|
||||||
@ -420,7 +421,7 @@ meta_compositor_manage_screen (MetaCompositor *compositor,
|
|||||||
|
|
||||||
XSelectInput (xdisplay, xwin, event_mask);
|
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->overlay_group = clutter_group_new ();
|
||||||
info->hidden_group = clutter_group_new ();
|
info->hidden_group = clutter_group_new ();
|
||||||
|
|
||||||
|
@ -57,10 +57,13 @@ struct _MutterShapedTexturePrivate
|
|||||||
{
|
{
|
||||||
CoglHandle mask_texture;
|
CoglHandle mask_texture;
|
||||||
CoglHandle material;
|
CoglHandle material;
|
||||||
|
CoglHandle material_unshaped;
|
||||||
#if 1 /* see workaround comment in mutter_shaped_texture_paint */
|
#if 1 /* see workaround comment in mutter_shaped_texture_paint */
|
||||||
CoglHandle material_workaround;
|
CoglHandle material_workaround;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
GdkRegion *clip_region;
|
||||||
|
|
||||||
guint mask_width, mask_height;
|
guint mask_width, mask_height;
|
||||||
|
|
||||||
GArray *rectangles;
|
GArray *rectangles;
|
||||||
@ -106,6 +109,11 @@ mutter_shaped_texture_dispose (GObject *object)
|
|||||||
cogl_material_unref (priv->material);
|
cogl_material_unref (priv->material);
|
||||||
priv->material = COGL_INVALID_HANDLE;
|
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 1 /* see comment in mutter_shaped_texture_paint */
|
||||||
if (priv->material_workaround != COGL_INVALID_HANDLE)
|
if (priv->material_workaround != COGL_INVALID_HANDLE)
|
||||||
{
|
{
|
||||||
@ -114,6 +122,8 @@ mutter_shaped_texture_dispose (GObject *object)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
mutter_shaped_texture_set_clip_region (self, NULL);
|
||||||
|
|
||||||
G_OBJECT_CLASS (mutter_shaped_texture_parent_class)->dispose (object);
|
G_OBJECT_CLASS (mutter_shaped_texture_parent_class)->dispose (object);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,6 +263,9 @@ mutter_shaped_texture_paint (ClutterActor *actor)
|
|||||||
guint depth;
|
guint depth;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (priv->clip_region && gdk_region_empty (priv->clip_region))
|
||||||
|
return;
|
||||||
|
|
||||||
if (!CLUTTER_ACTOR_IS_REALIZED (CLUTTER_ACTOR (stex)))
|
if (!CLUTTER_ACTOR_IS_REALIZED (CLUTTER_ACTOR (stex)))
|
||||||
clutter_actor_realize (CLUTTER_ACTOR (stex));
|
clutter_actor_realize (CLUTTER_ACTOR (stex));
|
||||||
|
|
||||||
@ -264,18 +277,20 @@ mutter_shaped_texture_paint (ClutterActor *actor)
|
|||||||
if (tex_width == 0 || tex_width == 0) /* no contents yet */
|
if (tex_width == 0 || tex_width == 0) /* no contents yet */
|
||||||
return;
|
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)
|
if (paint_tex == COGL_INVALID_HANDLE)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (priv->rectangles->len < 1)
|
||||||
|
{
|
||||||
|
/* If there are no rectangles use a single-layer texture */
|
||||||
|
|
||||||
|
if (priv->material_unshaped == COGL_INVALID_HANDLE)
|
||||||
|
priv->material_unshaped = cogl_material_new ();
|
||||||
|
|
||||||
|
material = priv->material_unshaped;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
mutter_shaped_texture_ensure_mask (stex);
|
mutter_shaped_texture_ensure_mask (stex);
|
||||||
|
|
||||||
if (priv->material == COGL_INVALID_HANDLE)
|
if (priv->material == COGL_INVALID_HANDLE)
|
||||||
@ -317,8 +332,10 @@ mutter_shaped_texture_paint (ClutterActor *actor)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
cogl_material_set_layer (material, 0, paint_tex);
|
|
||||||
cogl_material_set_layer (material, 1, priv->mask_texture);
|
cogl_material_set_layer (material, 1, priv->mask_texture);
|
||||||
|
}
|
||||||
|
|
||||||
|
cogl_material_set_layer (material, 0, paint_tex);
|
||||||
|
|
||||||
{
|
{
|
||||||
CoglColor color;
|
CoglColor color;
|
||||||
@ -330,6 +347,48 @@ mutter_shaped_texture_paint (ClutterActor *actor)
|
|||||||
cogl_set_source (material);
|
cogl_set_source (material);
|
||||||
|
|
||||||
clutter_actor_get_allocation_box (actor, &alloc);
|
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,
|
cogl_rectangle (0, 0,
|
||||||
alloc.x2 - alloc.x1,
|
alloc.x2 - alloc.x1,
|
||||||
alloc.y2 - alloc.y1);
|
alloc.y2 - alloc.y1);
|
||||||
@ -426,3 +485,37 @@ mutter_shaped_texture_add_rectangles (MutterShapedTexture *stex,
|
|||||||
mutter_shaped_texture_dirty_mask (stex);
|
mutter_shaped_texture_dirty_mask (stex);
|
||||||
clutter_actor_queue_redraw (CLUTTER_ACTOR (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;
|
||||||
|
}
|
||||||
|
@ -31,6 +31,8 @@
|
|||||||
#include <clutter/glx/clutter-glx.h>
|
#include <clutter/glx/clutter-glx.h>
|
||||||
#endif /* HAVE_GLX_TEXTURE_PIXMAP */
|
#endif /* HAVE_GLX_TEXTURE_PIXMAP */
|
||||||
|
|
||||||
|
#include <gdk/gdkregion.h>
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
#define MUTTER_TYPE_SHAPED_TEXTURE \
|
#define MUTTER_TYPE_SHAPED_TEXTURE \
|
||||||
@ -90,6 +92,10 @@ void mutter_shaped_texture_add_rectangles (MutterShapedTexture *stex,
|
|||||||
size_t num_rects,
|
size_t num_rects,
|
||||||
const XRectangle *rects);
|
const XRectangle *rects);
|
||||||
|
|
||||||
|
/* Assumes ownership of clip_region */
|
||||||
|
void mutter_shaped_texture_set_clip_region (MutterShapedTexture *stex,
|
||||||
|
GdkRegion *clip_region);
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
||||||
#endif /* __MUTTER_SHAPED_TEXTURE_H__ */
|
#endif /* __MUTTER_SHAPED_TEXTURE_H__ */
|
||||||
|
194
src/compositor/mutter-window-group.c
Normal file
194
src/compositor/mutter-window-group.c
Normal file
@ -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);
|
||||||
|
}
|
52
src/compositor/mutter-window-group.h
Normal file
52
src/compositor/mutter-window-group.h
Normal file
@ -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 */
|
@ -4,6 +4,7 @@
|
|||||||
#define MUTTER_WINDOW_PRIVATE_H
|
#define MUTTER_WINDOW_PRIVATE_H
|
||||||
|
|
||||||
#include <X11/extensions/Xdamage.h>
|
#include <X11/extensions/Xdamage.h>
|
||||||
|
#include <gdk/gdk.h>
|
||||||
#include "compositor-mutter.h"
|
#include "compositor-mutter.h"
|
||||||
|
|
||||||
MutterWindow *mutter_window_new (MetaWindow *window);
|
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_mapped (MutterWindow *cw);
|
||||||
void mutter_window_unmapped (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,
|
void mutter_window_effect_completed (MutterWindow *actor,
|
||||||
gulong event);
|
gulong event);
|
||||||
|
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
/* -*- 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/shape.h>
|
||||||
#include <X11/extensions/Xcomposite.h>
|
#include <X11/extensions/Xcomposite.h>
|
||||||
#include <X11/extensions/Xdamage.h>
|
#include <X11/extensions/Xdamage.h>
|
||||||
@ -18,6 +21,7 @@
|
|||||||
#include "mutter-shaped-texture.h"
|
#include "mutter-shaped-texture.h"
|
||||||
#include "mutter-window-private.h"
|
#include "mutter-window-private.h"
|
||||||
#include "shadow.h"
|
#include "shadow.h"
|
||||||
|
#include "tidy/tidy-texture-frame.h"
|
||||||
|
|
||||||
struct _MutterWindowPrivate
|
struct _MutterWindowPrivate
|
||||||
{
|
{
|
||||||
@ -38,6 +42,12 @@ struct _MutterWindowPrivate
|
|||||||
|
|
||||||
gchar * desc;
|
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
|
* These need to be counters rather than flags, since more plugins
|
||||||
* can implement same effect; the practicality of stacking effects
|
* 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 void mutter_window_detach (MutterWindow *self);
|
||||||
static gboolean mutter_window_has_shadow (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,
|
static gboolean is_shaped (MetaDisplay *display,
|
||||||
Window xwindow);
|
Window xwindow);
|
||||||
@ -388,6 +400,9 @@ mutter_window_dispose (GObject *object)
|
|||||||
|
|
||||||
mutter_window_detach (self);
|
mutter_window_detach (self);
|
||||||
|
|
||||||
|
mutter_window_clear_shape_region (self);
|
||||||
|
mutter_window_clear_bounding_region (self);
|
||||||
|
|
||||||
if (priv->damage != None)
|
if (priv->damage != None)
|
||||||
{
|
{
|
||||||
meta_error_trap_push (display);
|
meta_error_trap_push (display);
|
||||||
@ -1213,6 +1228,214 @@ mutter_window_unmapped (MutterWindow *self)
|
|||||||
priv->needs_repair = FALSE;
|
priv->needs_repair = FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
static void
|
||||||
check_needs_repair (MutterWindow *self)
|
check_needs_repair (MutterWindow *self)
|
||||||
{
|
{
|
||||||
@ -1268,6 +1491,7 @@ check_needs_repair (MutterWindow *self)
|
|||||||
if (priv->back_pixmap == None)
|
if (priv->back_pixmap == None)
|
||||||
{
|
{
|
||||||
meta_verbose ("Unable to get named pixmap for %p\n", self);
|
meta_verbose ("Unable to get named pixmap for %p\n", self);
|
||||||
|
mutter_window_update_bounding_region (self, 0, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1293,6 +1517,8 @@ check_needs_repair (MutterWindow *self)
|
|||||||
if (priv->shadow)
|
if (priv->shadow)
|
||||||
clutter_actor_set_size (priv->shadow, pxm_width, pxm_height);
|
clutter_actor_set_size (priv->shadow, pxm_width, pxm_height);
|
||||||
|
|
||||||
|
mutter_window_update_bounding_region (self, pxm_width, pxm_height);
|
||||||
|
|
||||||
full = TRUE;
|
full = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1389,6 +1615,7 @@ check_needs_reshape (MutterWindow *self)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
mutter_shaped_texture_clear_rectangles (MUTTER_SHAPED_TEXTURE (priv->actor));
|
mutter_shaped_texture_clear_rectangles (MUTTER_SHAPED_TEXTURE (priv->actor));
|
||||||
|
mutter_window_clear_shape_region (self);
|
||||||
|
|
||||||
#ifdef HAVE_SHAPE
|
#ifdef HAVE_SHAPE
|
||||||
if (priv->shaped)
|
if (priv->shaped)
|
||||||
@ -1408,6 +1635,8 @@ check_needs_reshape (MutterWindow *self)
|
|||||||
mutter_shaped_texture_add_rectangles (MUTTER_SHAPED_TEXTURE (priv->actor),
|
mutter_shaped_texture_add_rectangles (MUTTER_SHAPED_TEXTURE (priv->actor),
|
||||||
n_rects, rects);
|
n_rects, rects);
|
||||||
|
|
||||||
|
mutter_window_update_shape_region (self, n_rects, rects);
|
||||||
|
|
||||||
XFree (rects);
|
XFree (rects);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,6 +64,8 @@ struct _TidyTextureFramePrivate
|
|||||||
gfloat bottom;
|
gfloat bottom;
|
||||||
|
|
||||||
CoglHandle material;
|
CoglHandle material;
|
||||||
|
|
||||||
|
guint needs_paint : 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -175,6 +177,9 @@ tidy_texture_frame_paint (ClutterActor *self)
|
|||||||
if (G_UNLIKELY (priv->parent_texture == NULL))
|
if (G_UNLIKELY (priv->parent_texture == NULL))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (!priv->needs_paint)
|
||||||
|
return;
|
||||||
|
|
||||||
/* parent texture may have been hidden, so need to make sure it gets
|
/* parent texture may have been hidden, so need to make sure it gets
|
||||||
* realized
|
* realized
|
||||||
*/
|
*/
|
||||||
@ -608,3 +613,29 @@ tidy_texture_frame_get_frame (TidyTextureFrame *frame,
|
|||||||
if (left)
|
if (left)
|
||||||
*left = priv->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;
|
||||||
|
}
|
||||||
|
@ -76,6 +76,9 @@ void tidy_texture_frame_get_frame (TidyTextureFrame *frame,
|
|||||||
gfloat *bottom,
|
gfloat *bottom,
|
||||||
gfloat *left);
|
gfloat *left);
|
||||||
|
|
||||||
|
void tidy_texture_frame_set_needs_paint (TidyTextureFrame *frame,
|
||||||
|
gboolean needs_paint);
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
||||||
#endif /* _HAVE_TIDY_TEXTURE_FRAME_H */
|
#endif /* _HAVE_TIDY_TEXTURE_FRAME_H */
|
||||||
|
Loading…
Reference in New Issue
Block a user