mutter-window: stream raw updates to ClutterX11TexturePixmap

This changes the way we handle Damage events so instead of getting an
event when the damage region of a pixmap becomes non-empty we now get
sent all damage rectangles and stream those all though to
ClutterX11TexturePixmap using clutter_x11_texture_pixmap_update_area()

For Clutter 1.2, ClutterGLXTexturePixmap was updated so that calls to
clutter_x11_texture_pixmap_update_area are now cheap (glXBindTexImageEXT
calls are now deferred until just before painting) and since
ClutterGLXTexturePixmap is now capable of queueing clipped redraws that
can result in only updating a sub-region of the stage during a repaint
cycle (and using glXCopySubBufferMESA to present the sub-region redraw
to the front buffer) this should improve performance and reduced power
consumption for a range of use cases. (For example viewing a website
that has animated adverts doesn't force the whole screen to be redrawn
for each frame of the advert)

Besides being able to take advantage of glXCopySubBuffer to only update
a small region of the stage the fact that this patch makes Mutter now
request RawRectangles from the X server means we no longer do a
synchronous X request for a complete Damage Region for every window
damaged each frame. This should also improve performance.

CLUTTER_PAINT=redraws can be used to visualize what parts of the stage
are redrawn and with this patch applied I can open a terminal and as I
type I see that only the damaged areas of the terminal are being
redrawn.
This commit is contained in:
Robert Bragg 2010-03-02 18:02:28 +00:00
parent cfa30f9876
commit 0c14640352

View File

@ -47,6 +47,8 @@ struct _MutterWindowPrivate
* texture */ * texture */
GdkRegion *bounding_region; GdkRegion *bounding_region;
guint freeze_count;
/* /*
* 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
@ -65,7 +67,9 @@ struct _MutterWindowPrivate
guint disposed : 1; guint disposed : 1;
guint redecorating : 1; guint redecorating : 1;
guint needs_repair : 1; guint needs_damage_all : 1;
guint needs_pixmap : 1;
guint needs_reshape : 1; guint needs_reshape : 1;
guint size_changed : 1; guint size_changed : 1;
@ -332,7 +336,8 @@ mutter_window_constructed (GObject *object)
if (priv->attrs.class == InputOnly) if (priv->attrs.class == InputOnly)
priv->damage = None; priv->damage = None;
else else
priv->damage = XDamageCreate (xdisplay, xwindow, XDamageReportNonEmpty); priv->damage = XDamageCreate (xdisplay, xwindow,
XDamageReportRawRectangles);
format = XRenderFindVisualFormat (xdisplay, priv->attrs.visual); format = XRenderFindVisualFormat (xdisplay, priv->attrs.visual);
@ -765,6 +770,60 @@ mutter_window_showing_on_its_workspace (MutterWindow *self)
return meta_window_showing_on_its_workspace (self->priv->window); return meta_window_showing_on_its_workspace (self->priv->window);
} }
static void
mutter_window_freeze (MutterWindow *self)
{
self->priv->freeze_count++;
}
static void
mutter_window_damage_all (MutterWindow *self)
{
MutterWindowPrivate *priv = self->priv;
ClutterX11TexturePixmap *texture_x11 = CLUTTER_X11_TEXTURE_PIXMAP (priv->actor);
guint pixmap_width = 0;
guint pixmap_height = 0;
if (!priv->needs_damage_all)
return;
g_object_get (texture_x11,
"pixmap-width", &pixmap_width,
"pixmap-height", &pixmap_height,
NULL);
clutter_x11_texture_pixmap_update_area (texture_x11,
0,
0,
pixmap_width,
pixmap_height);
priv->needs_damage_all = FALSE;
}
static void
mutter_window_thaw (MutterWindow *self)
{
self->priv->freeze_count--;
if (G_UNLIKELY (self->priv->freeze_count < 0))
{
g_warning ("Error in freeze/thaw accounting.");
self->priv->freeze_count = 0;
return;
}
if (self->priv->freeze_count)
return;
/* Since we ignore damage events while a window is frozen for certain effects
* we may need to issue an update_area() covering the whole pixmap if we
* don't know what real damage has happened. */
if (self->priv->needs_damage_all)
mutter_window_damage_all (self);
}
gboolean gboolean
mutter_window_effect_in_progress (MutterWindow *self) mutter_window_effect_in_progress (MutterWindow *self)
{ {
@ -776,11 +835,11 @@ mutter_window_effect_in_progress (MutterWindow *self)
} }
static void static void
mutter_window_mark_for_repair (MutterWindow *self) mutter_window_queue_create_pixmap (MutterWindow *self)
{ {
MutterWindowPrivate *priv = self->priv; MutterWindowPrivate *priv = self->priv;
priv->needs_repair = TRUE; priv->needs_pixmap = TRUE;
if (!priv->mapped) if (!priv->mapped)
return; return;
@ -796,6 +855,21 @@ mutter_window_mark_for_repair (MutterWindow *self)
clutter_actor_queue_redraw (priv->actor); clutter_actor_queue_redraw (priv->actor);
} }
static gboolean
is_freeze_thaw_effect (gulong event)
{
switch (event)
{
case MUTTER_PLUGIN_DESTROY:
case MUTTER_PLUGIN_MAXIMIZE:
case MUTTER_PLUGIN_UNMAXIMIZE:
return TRUE;
break;
default:
return FALSE;
}
}
static gboolean static gboolean
start_simple_effect (MutterWindow *self, start_simple_effect (MutterWindow *self,
gulong event) gulong event)
@ -803,6 +877,7 @@ start_simple_effect (MutterWindow *self,
MutterWindowPrivate *priv = self->priv; MutterWindowPrivate *priv = self->priv;
MetaCompScreen *info = meta_screen_get_compositor_data (priv->screen); MetaCompScreen *info = meta_screen_get_compositor_data (priv->screen);
gint *counter = NULL; gint *counter = NULL;
gboolean use_freeze_thaw = FALSE;
if (!info->plugin_mgr) if (!info->plugin_mgr)
return FALSE; return FALSE;
@ -827,6 +902,11 @@ start_simple_effect (MutterWindow *self,
g_assert (counter); g_assert (counter);
use_freeze_thaw = is_freeze_thaw_effect (event);
if (use_freeze_thaw)
mutter_window_freeze (self);
(*counter)++; (*counter)++;
if (!mutter_plugin_manager_event_simple (info->plugin_mgr, if (!mutter_plugin_manager_event_simple (info->plugin_mgr,
@ -834,6 +914,8 @@ start_simple_effect (MutterWindow *self,
event)) event))
{ {
(*counter)--; (*counter)--;
if (use_freeze_thaw)
mutter_window_thaw (self);
return FALSE; return FALSE;
} }
@ -857,7 +939,7 @@ mutter_window_after_effects (MutterWindow *self)
if (!meta_window_is_mapped (priv->window)) if (!meta_window_is_mapped (priv->window))
mutter_window_detach (self); mutter_window_detach (self);
if (priv->needs_repair) if (priv->needs_pixmap)
clutter_actor_queue_redraw (priv->actor); clutter_actor_queue_redraw (priv->actor);
} }
@ -926,6 +1008,9 @@ mutter_window_effect_completed (MutterWindow *self,
break; break;
} }
if (is_freeze_thaw_effect (event))
mutter_window_thaw (self);
if (!mutter_window_effect_in_progress (self)) if (!mutter_window_effect_in_progress (self))
mutter_window_after_effects (self); mutter_window_after_effects (self);
} }
@ -951,7 +1036,7 @@ mutter_window_detach (MutterWindow *self)
clutter_x11_texture_pixmap_set_pixmap (CLUTTER_X11_TEXTURE_PIXMAP (priv->actor), clutter_x11_texture_pixmap_set_pixmap (CLUTTER_X11_TEXTURE_PIXMAP (priv->actor),
None); None);
mutter_window_mark_for_repair (self); mutter_window_queue_create_pixmap (self);
} }
void void
@ -1014,7 +1099,7 @@ mutter_window_sync_actor_position (MutterWindow *self)
priv->attrs.height != window_rect.height) priv->attrs.height != window_rect.height)
{ {
priv->size_changed = TRUE; priv->size_changed = TRUE;
mutter_window_mark_for_repair (self); mutter_window_queue_create_pixmap (self);
} }
/* XXX deprecated: please use meta_window_get_outer_rect instead */ /* XXX deprecated: please use meta_window_get_outer_rect instead */
@ -1206,7 +1291,7 @@ mutter_window_new (MetaWindow *window)
priv->mapped = meta_window_toplevel_is_mapped (priv->window); priv->mapped = meta_window_toplevel_is_mapped (priv->window);
if (priv->mapped) if (priv->mapped)
mutter_window_mark_for_repair (self); mutter_window_queue_create_pixmap (self);
mutter_window_sync_actor_position (self); mutter_window_sync_actor_position (self);
@ -1234,7 +1319,7 @@ mutter_window_mapped (MutterWindow *self)
priv->mapped = TRUE; priv->mapped = TRUE;
mutter_window_mark_for_repair (self); mutter_window_queue_create_pixmap (self);
} }
void void
@ -1250,7 +1335,7 @@ mutter_window_unmapped (MutterWindow *self)
return; return;
mutter_window_detach (self); mutter_window_detach (self);
priv->needs_repair = FALSE; priv->needs_pixmap = FALSE;
} }
static void static void
@ -1462,7 +1547,7 @@ mutter_window_reset_visible_regions (MutterWindow *self)
} }
static void static void
check_needs_repair (MutterWindow *self) check_needs_pixmap (MutterWindow *self)
{ {
MutterWindowPrivate *priv = self->priv; MutterWindowPrivate *priv = self->priv;
MetaScreen *screen = priv->screen; MetaScreen *screen = priv->screen;
@ -1473,7 +1558,7 @@ check_needs_repair (MutterWindow *self)
Window xwindow = priv->xwindow; Window xwindow = priv->xwindow;
gboolean full = FALSE; gboolean full = FALSE;
if (!priv->needs_repair) if (!priv->needs_pixmap)
return; return;
if (!priv->mapped) if (!priv->mapped)
@ -1545,74 +1630,50 @@ check_needs_repair (MutterWindow *self)
full = TRUE; full = TRUE;
} }
/*
* TODO -- on some gfx hardware updating the whole texture instead of
* the individual rectangles is actually quicker, so we might want to
* make this a configurable option (on desktop HW with multiple pipelines
* it is usually quicker to just update the damaged parts).
*
* If we are using TFP we update the whole texture (this simply trigers
* the texture rebind).
*/
if (full
#ifdef HAVE_GLX_TEXTURE_PIXMAP
|| (CLUTTER_GLX_IS_TEXTURE_PIXMAP (priv->actor) &&
clutter_glx_texture_pixmap_using_extension
(CLUTTER_GLX_TEXTURE_PIXMAP (priv->actor)))
#endif /* HAVE_GLX_TEXTURE_PIXMAP */
)
{
XDamageSubtract (xdisplay, priv->damage, None, None);
clutter_x11_texture_pixmap_update_area
(CLUTTER_X11_TEXTURE_PIXMAP (priv->actor),
0,
0,
clutter_actor_get_width (priv->actor),
clutter_actor_get_height (priv->actor));
}
else
{
XRectangle *r_damage;
XRectangle r_bounds;
XserverRegion parts;
int i, r_count;
parts = XFixesCreateRegion (xdisplay, 0, 0);
XDamageSubtract (xdisplay, priv->damage, None, parts);
r_damage = XFixesFetchRegionAndBounds (xdisplay,
parts,
&r_count,
&r_bounds);
if (r_damage)
{
for (i = 0; i < r_count; ++i)
{
clutter_x11_texture_pixmap_update_area
(CLUTTER_X11_TEXTURE_PIXMAP (priv->actor),
r_damage[i].x,
r_damage[i].y,
r_damage[i].width,
r_damage[i].height);
}
}
XFree (r_damage);
XFixesDestroyRegion (xdisplay, parts);
}
meta_error_trap_pop (display, FALSE); meta_error_trap_pop (display, FALSE);
priv->needs_repair = FALSE; priv->needs_pixmap = FALSE;
}
static gboolean
is_frozen (MutterWindow *self)
{
return self->priv->freeze_count ? TRUE : FALSE;
} }
void void
mutter_window_process_damage (MutterWindow *self, mutter_window_process_damage (MutterWindow *self,
XDamageNotifyEvent *event) XDamageNotifyEvent *event)
{ {
mutter_window_mark_for_repair (self); MutterWindowPrivate *priv = self->priv;
ClutterX11TexturePixmap *texture_x11 = CLUTTER_X11_TEXTURE_PIXMAP (priv->actor);
if (is_frozen (self))
{
/* The window is frozen due to an effect in progress: we ignore damage
* here on the off chance that this will stop the corresponding
* texture_from_pixmap from being update.
*
* needs_damage_all tracks that some unknown damage happened while the
* window was frozen so that when the window becomes unfrozen we can
* issue a full window update to cover any lost damage.
*
* It should be noted that this is an unreliable mechanism since it's
* quite likely that drivers will aim to provide a zero-copy
* implementation of the texture_from_pixmap extension and in those cases
* any drawing done to the window is always immediately reflected in the
* texture regardless of damage event handling.
*/
priv->needs_damage_all = TRUE;
return;
}
clutter_x11_texture_pixmap_update_area (texture_x11,
event->area.x,
event->area.y,
event->area.width,
event->area.height);
} }
void void
@ -1683,17 +1744,15 @@ mutter_window_update_shape (MutterWindow *self,
void void
mutter_window_pre_paint (MutterWindow *self) mutter_window_pre_paint (MutterWindow *self)
{ {
MutterWindowPrivate *priv = self->priv; if (is_frozen (self))
{
/* The window is frozen due to a pending animation: we'll wait until /* The window is frozen due to a pending animation: we'll wait until
* the animation finishes to reshape and repair the window */ * the animation finishes to reshape and repair the window */
if (priv->destroy_in_progress || return;
priv->maximize_in_progress || }
priv->unmaximize_in_progress)
return;
check_needs_reshape (self); check_needs_reshape (self);
check_needs_repair (self); check_needs_pixmap (self);
} }
void void