frames: Remove frame border pixel caching and related optimizations
Since we now cache windows in the X server, we don't really need to cache them here. Since we are redirecting windows in most cases, we're not gaining anything except added memory usage. Additionally, remove the clip to screen optimization - if a window is partially off-screen, we still need to draw the entire thing as redirection means we won't get an expose event for it. Additionally, when introducing invisible borders, something accidentally slipped through: we were getting expose events on the invisible borders, and they weren't in the cached pixels rect, so we were painting the theme for them, even if we didn't actually paint anything with cairo. Make sure to clip out the invisible borders instead of just the client rect so that we don't draw if our expose event is on the invisible borders. https://bugzilla.gnome.org/show_bug.cgi?id=675111
This commit is contained in:
parent
fc87a635b2
commit
6fb857cb23
350
src/ui/frames.c
350
src/ui/frames.c
@ -97,7 +97,6 @@ static MetaFrameControl get_control (MetaFrames *frames,
|
||||
MetaUIFrame *frame,
|
||||
int x,
|
||||
int y);
|
||||
static void invalidate_all_caches (MetaFrames *frames);
|
||||
static void invalidate_whole_window (MetaFrames *frames,
|
||||
MetaUIFrame *frame);
|
||||
|
||||
@ -261,10 +260,6 @@ meta_frames_init (MetaFrames *frames)
|
||||
|
||||
frames->frames = g_hash_table_new (unsigned_long_hash, unsigned_long_equal);
|
||||
|
||||
frames->invalidate_cache_timeout_id = 0;
|
||||
frames->invalidate_frames = NULL;
|
||||
frames->cache = g_hash_table_new (g_direct_hash, g_direct_equal);
|
||||
|
||||
frames->style_variants = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
g_free, g_object_unref);
|
||||
update_style_contexts (frames);
|
||||
@ -331,90 +326,13 @@ meta_frames_finalize (GObject *object)
|
||||
meta_prefs_remove_listener (prefs_changed_callback, frames);
|
||||
|
||||
g_hash_table_destroy (frames->text_heights);
|
||||
|
||||
invalidate_all_caches (frames);
|
||||
if (frames->invalidate_cache_timeout_id)
|
||||
g_source_remove (frames->invalidate_cache_timeout_id);
|
||||
|
||||
g_assert (g_hash_table_size (frames->frames) == 0);
|
||||
g_hash_table_destroy (frames->frames);
|
||||
g_hash_table_destroy (frames->cache);
|
||||
|
||||
G_OBJECT_CLASS (meta_frames_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
cairo_rectangle_int_t rect;
|
||||
cairo_surface_t *pixmap;
|
||||
} CachedFramePiece;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* Caches of the four rendered sides in a MetaFrame.
|
||||
* Order: top (titlebar), left, right, bottom.
|
||||
*/
|
||||
CachedFramePiece piece[4];
|
||||
} CachedPixels;
|
||||
|
||||
static CachedPixels *
|
||||
get_cache (MetaFrames *frames,
|
||||
MetaUIFrame *frame)
|
||||
{
|
||||
CachedPixels *pixels;
|
||||
|
||||
pixels = g_hash_table_lookup (frames->cache, frame);
|
||||
|
||||
if (!pixels)
|
||||
{
|
||||
pixels = g_new0 (CachedPixels, 1);
|
||||
g_hash_table_insert (frames->cache, frame, pixels);
|
||||
}
|
||||
|
||||
return pixels;
|
||||
}
|
||||
|
||||
static void
|
||||
invalidate_cache (MetaFrames *frames,
|
||||
MetaUIFrame *frame)
|
||||
{
|
||||
CachedPixels *pixels = get_cache (frames, frame);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
if (pixels->piece[i].pixmap)
|
||||
cairo_surface_destroy (pixels->piece[i].pixmap);
|
||||
|
||||
g_free (pixels);
|
||||
g_hash_table_remove (frames->cache, frame);
|
||||
}
|
||||
|
||||
static void
|
||||
invalidate_all_caches (MetaFrames *frames)
|
||||
{
|
||||
GList *l;
|
||||
|
||||
for (l = frames->invalidate_frames; l; l = l->next)
|
||||
{
|
||||
MetaUIFrame *frame = l->data;
|
||||
|
||||
invalidate_cache (frames, frame);
|
||||
}
|
||||
|
||||
g_list_free (frames->invalidate_frames);
|
||||
frames->invalidate_frames = NULL;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
invalidate_cache_timeout (gpointer data)
|
||||
{
|
||||
MetaFrames *frames = data;
|
||||
|
||||
invalidate_all_caches (frames);
|
||||
frames->invalidate_cache_timeout_id = 0;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
queue_recalc_func (gpointer key, gpointer value, gpointer data)
|
||||
{
|
||||
@ -719,11 +637,6 @@ meta_frames_unmanage_window (MetaFrames *frames,
|
||||
|
||||
if (frame)
|
||||
{
|
||||
/* invalidating all caches ensures the frame
|
||||
* is not actually referenced anymore
|
||||
*/
|
||||
invalidate_all_caches (frames);
|
||||
|
||||
/* restore the cursor */
|
||||
meta_core_set_screen_cursor (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()),
|
||||
frame->xwindow,
|
||||
@ -1125,7 +1038,6 @@ redraw_control (MetaFrames *frames,
|
||||
rect = control_rect (control, &fgeom);
|
||||
|
||||
gdk_window_invalidate_rect (frame->window, rect, FALSE);
|
||||
invalidate_cache (frames, frame);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@ -1899,222 +1811,50 @@ setup_bg_cr (cairo_t *cr, GdkWindow *window, int x_offset, int y_offset)
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns a pixmap with a piece of the windows frame painted on it.
|
||||
*/
|
||||
|
||||
static cairo_surface_t *
|
||||
generate_pixmap (MetaFrames *frames,
|
||||
MetaUIFrame *frame,
|
||||
cairo_rectangle_int_t *rect)
|
||||
{
|
||||
cairo_surface_t *result;
|
||||
cairo_t *cr;
|
||||
|
||||
/* do not create a pixmap for nonexisting areas */
|
||||
if (rect->width <= 0 || rect->height <= 0)
|
||||
return NULL;
|
||||
|
||||
result = gdk_window_create_similar_surface (frame->window,
|
||||
CAIRO_CONTENT_COLOR,
|
||||
rect->width, rect->height);
|
||||
|
||||
cr = cairo_create (result);
|
||||
cairo_translate (cr, -rect->x, -rect->y);
|
||||
|
||||
setup_bg_cr (cr, frame->window, 0, 0);
|
||||
cairo_paint (cr);
|
||||
|
||||
meta_frames_paint (frames, frame, cr);
|
||||
|
||||
cairo_destroy (cr);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
populate_cache (MetaFrames *frames,
|
||||
MetaUIFrame *frame)
|
||||
{
|
||||
MetaFrameBorders borders;
|
||||
int width, height;
|
||||
int frame_width, frame_height, screen_width, screen_height;
|
||||
CachedPixels *pixels;
|
||||
MetaFrameType frame_type;
|
||||
MetaFrameFlags frame_flags;
|
||||
int i;
|
||||
|
||||
meta_core_get (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()),
|
||||
frame->xwindow,
|
||||
META_CORE_GET_FRAME_WIDTH, &frame_width,
|
||||
META_CORE_GET_FRAME_HEIGHT, &frame_height,
|
||||
META_CORE_GET_SCREEN_WIDTH, &screen_width,
|
||||
META_CORE_GET_SCREEN_HEIGHT, &screen_height,
|
||||
META_CORE_GET_CLIENT_WIDTH, &width,
|
||||
META_CORE_GET_CLIENT_HEIGHT, &height,
|
||||
META_CORE_GET_FRAME_TYPE, &frame_type,
|
||||
META_CORE_GET_FRAME_FLAGS, &frame_flags,
|
||||
META_CORE_GET_END);
|
||||
|
||||
/* don't cache extremely large windows */
|
||||
if (frame_width > 2 * screen_width ||
|
||||
frame_height > 2 * screen_height)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
meta_theme_get_frame_borders (meta_theme_get_current (),
|
||||
frame_type,
|
||||
frame->text_height,
|
||||
frame_flags,
|
||||
&borders);
|
||||
|
||||
pixels = get_cache (frames, frame);
|
||||
|
||||
/* Setup the rectangles for the four visible frame borders. First top, then
|
||||
* left, right and bottom. Top and bottom extend to the invisible borders
|
||||
* while left and right snugly fit in between:
|
||||
* -----
|
||||
* | |
|
||||
* -----
|
||||
*/
|
||||
|
||||
/* width and height refer to the client window's
|
||||
* size without any border added. */
|
||||
|
||||
/* top */
|
||||
pixels->piece[0].rect.x = borders.invisible.left;
|
||||
pixels->piece[0].rect.y = borders.invisible.top;
|
||||
pixels->piece[0].rect.width = width + borders.visible.left + borders.visible.right;
|
||||
pixels->piece[0].rect.height = borders.visible.top;
|
||||
|
||||
/* left */
|
||||
pixels->piece[1].rect.x = borders.invisible.left;
|
||||
pixels->piece[1].rect.y = borders.total.top;
|
||||
pixels->piece[1].rect.height = height;
|
||||
pixels->piece[1].rect.width = borders.visible.left;
|
||||
|
||||
/* right */
|
||||
pixels->piece[2].rect.x = borders.total.left + width;
|
||||
pixels->piece[2].rect.y = borders.total.top;
|
||||
pixels->piece[2].rect.width = borders.visible.right;
|
||||
pixels->piece[2].rect.height = height;
|
||||
|
||||
/* bottom */
|
||||
pixels->piece[3].rect.x = borders.invisible.left;
|
||||
pixels->piece[3].rect.y = borders.total.top + height;
|
||||
pixels->piece[3].rect.width = width + borders.visible.left + borders.visible.right;
|
||||
pixels->piece[3].rect.height = borders.visible.bottom;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
CachedFramePiece *piece = &pixels->piece[i];
|
||||
/* generate_pixmap() returns NULL for 0 width/height pieces, but
|
||||
* does so cheaply so we don't need to cache the NULL return */
|
||||
if (!piece->pixmap)
|
||||
piece->pixmap = generate_pixmap (frames, frame, &piece->rect);
|
||||
}
|
||||
|
||||
if (frames->invalidate_cache_timeout_id)
|
||||
g_source_remove (frames->invalidate_cache_timeout_id);
|
||||
|
||||
frames->invalidate_cache_timeout_id = g_timeout_add (1000, invalidate_cache_timeout, frames);
|
||||
|
||||
if (!g_list_find (frames->invalidate_frames, frame))
|
||||
frames->invalidate_frames =
|
||||
g_list_prepend (frames->invalidate_frames, frame);
|
||||
}
|
||||
|
||||
static void
|
||||
clip_to_screen (cairo_region_t *region,
|
||||
MetaUIFrame *frame)
|
||||
{
|
||||
cairo_rectangle_int_t frame_area;
|
||||
cairo_rectangle_int_t screen_area = { 0, 0, 0, 0 };
|
||||
cairo_region_t *tmp_region;
|
||||
|
||||
/* Chop off stuff outside the screen; this optimization
|
||||
* is crucial to handle huge client windows,
|
||||
* like "xterm -geometry 1000x1000"
|
||||
*/
|
||||
meta_core_get (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()),
|
||||
frame->xwindow,
|
||||
META_CORE_GET_FRAME_X, &frame_area.x,
|
||||
META_CORE_GET_FRAME_Y, &frame_area.y,
|
||||
META_CORE_GET_FRAME_WIDTH, &frame_area.width,
|
||||
META_CORE_GET_FRAME_HEIGHT, &frame_area.height,
|
||||
META_CORE_GET_SCREEN_WIDTH, &screen_area.width,
|
||||
META_CORE_GET_SCREEN_HEIGHT, &screen_area.height,
|
||||
META_CORE_GET_END);
|
||||
|
||||
cairo_region_translate (region, frame_area.x, frame_area.y);
|
||||
|
||||
tmp_region = cairo_region_create_rectangle (&frame_area);
|
||||
cairo_region_intersect (region, tmp_region);
|
||||
cairo_region_destroy (tmp_region);
|
||||
|
||||
tmp_region = cairo_region_create_rectangle (&screen_area);
|
||||
cairo_region_intersect (region, tmp_region);
|
||||
cairo_region_destroy (tmp_region);
|
||||
|
||||
cairo_region_translate (region, - frame_area.x, - frame_area.y);
|
||||
}
|
||||
|
||||
static void
|
||||
subtract_client_area (cairo_region_t *region,
|
||||
MetaUIFrame *frame)
|
||||
clip_region_to_visible_frame_border (cairo_region_t *region,
|
||||
MetaUIFrame *frame)
|
||||
{
|
||||
cairo_rectangle_int_t area;
|
||||
cairo_region_t *frame_border;
|
||||
MetaFrameFlags flags;
|
||||
MetaFrameType type;
|
||||
MetaFrameBorders borders;
|
||||
cairo_region_t *tmp_region;
|
||||
Display *display;
|
||||
int frame_width, frame_height;
|
||||
|
||||
display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
|
||||
|
||||
meta_core_get (display, frame->xwindow,
|
||||
META_CORE_GET_FRAME_FLAGS, &flags,
|
||||
META_CORE_GET_FRAME_TYPE, &type,
|
||||
META_CORE_GET_CLIENT_WIDTH, &area.width,
|
||||
META_CORE_GET_CLIENT_HEIGHT, &area.height,
|
||||
META_CORE_GET_FRAME_WIDTH, &frame_width,
|
||||
META_CORE_GET_FRAME_HEIGHT, &frame_height,
|
||||
META_CORE_GET_END);
|
||||
|
||||
meta_theme_get_frame_borders (meta_theme_get_current (),
|
||||
type, frame->text_height, flags,
|
||||
&borders);
|
||||
|
||||
area.x = borders.total.left;
|
||||
area.y = borders.total.top;
|
||||
/* Visible frame rect */
|
||||
area.x = borders.invisible.left;
|
||||
area.y = borders.invisible.top;
|
||||
area.width = frame_width - borders.invisible.left - borders.invisible.right;
|
||||
area.height = frame_height - borders.invisible.top - borders.invisible.bottom;
|
||||
|
||||
tmp_region = cairo_region_create_rectangle (&area);
|
||||
cairo_region_subtract (region, tmp_region);
|
||||
cairo_region_destroy (tmp_region);
|
||||
}
|
||||
frame_border = cairo_region_create_rectangle (&area);
|
||||
|
||||
static void
|
||||
cached_pixels_draw (CachedPixels *pixels,
|
||||
cairo_t *cr,
|
||||
cairo_region_t *region)
|
||||
{
|
||||
cairo_region_t *region_piece;
|
||||
int i;
|
||||
/* Client rect */
|
||||
area.x += borders.visible.left;
|
||||
area.y += borders.visible.top;
|
||||
area.width -= borders.visible.left + borders.visible.right;
|
||||
area.height -= borders.visible.top + borders.visible.bottom;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
CachedFramePiece *piece;
|
||||
piece = &pixels->piece[i];
|
||||
|
||||
if (piece->pixmap)
|
||||
{
|
||||
cairo_set_source_surface (cr, piece->pixmap,
|
||||
piece->rect.x, piece->rect.y);
|
||||
cairo_paint (cr);
|
||||
|
||||
region_piece = cairo_region_create_rectangle (&piece->rect);
|
||||
cairo_region_subtract (region, region_piece);
|
||||
cairo_region_destroy (region_piece);
|
||||
}
|
||||
}
|
||||
/* Visible frame border */
|
||||
cairo_region_subtract_rectangle (frame_border, &area);
|
||||
cairo_region_intersect (region, frame_border);
|
||||
|
||||
cairo_region_destroy (frame_border);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@ -2123,10 +1863,8 @@ meta_frames_draw (GtkWidget *widget,
|
||||
{
|
||||
MetaUIFrame *frame;
|
||||
MetaFrames *frames;
|
||||
CachedPixels *pixels;
|
||||
cairo_region_t *region;
|
||||
cairo_rectangle_int_t clip;
|
||||
int i, n_areas;
|
||||
cairo_region_t *region;
|
||||
cairo_surface_t *target;
|
||||
|
||||
frames = META_FRAMES (widget);
|
||||
@ -2138,40 +1876,23 @@ meta_frames_draw (GtkWidget *widget,
|
||||
if (frame == NULL)
|
||||
return FALSE;
|
||||
|
||||
populate_cache (frames, frame);
|
||||
|
||||
region = cairo_region_create_rectangle (&clip);
|
||||
|
||||
pixels = get_cache (frames, frame);
|
||||
clip_region_to_visible_frame_border (region, frame);
|
||||
|
||||
cached_pixels_draw (pixels, cr, region);
|
||||
|
||||
clip_to_screen (region, frame);
|
||||
subtract_client_area (region, frame);
|
||||
if (cairo_region_is_empty (region))
|
||||
goto out;
|
||||
|
||||
n_areas = cairo_region_num_rectangles (region);
|
||||
gdk_cairo_region (cr, region);
|
||||
cairo_clip (cr);
|
||||
|
||||
for (i = 0; i < n_areas; i++)
|
||||
{
|
||||
cairo_rectangle_int_t area;
|
||||
cairo_save (cr);
|
||||
setup_bg_cr (cr, frame->window, 0, 0);
|
||||
cairo_paint (cr);
|
||||
cairo_restore (cr);
|
||||
|
||||
cairo_region_get_rectangle (region, i, &area);
|
||||
|
||||
cairo_save (cr);
|
||||
|
||||
cairo_rectangle (cr, area.x, area.y, area.width, area.height);
|
||||
cairo_clip (cr);
|
||||
|
||||
cairo_push_group (cr);
|
||||
|
||||
meta_frames_paint (frames, frame, cr);
|
||||
|
||||
cairo_pop_group_to_source (cr);
|
||||
cairo_paint (cr);
|
||||
|
||||
cairo_restore (cr);
|
||||
}
|
||||
meta_frames_paint (frames, frame, cr);
|
||||
|
||||
out:
|
||||
cairo_region_destroy (region);
|
||||
|
||||
return TRUE;
|
||||
@ -2615,5 +2336,4 @@ invalidate_whole_window (MetaFrames *frames,
|
||||
MetaUIFrame *frame)
|
||||
{
|
||||
gdk_window_invalidate_rect (frame->window, NULL, FALSE);
|
||||
invalidate_cache (frames, frame);
|
||||
}
|
||||
|
@ -97,10 +97,6 @@ struct _MetaFrames
|
||||
|
||||
GtkStyleContext *normal_style;
|
||||
GHashTable *style_variants;
|
||||
|
||||
int invalidate_cache_timeout_id;
|
||||
GList *invalidate_frames;
|
||||
GHashTable *cache;
|
||||
};
|
||||
|
||||
struct _MetaFramesClass
|
||||
|
Loading…
Reference in New Issue
Block a user