From c0e01d9cd71bd958e1b31a03cea4c08a1bdf4926 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 18 Jun 2009 14:32:53 +0100 Subject: [PATCH] [xlib] Improve GC caching efficacy Shrink the overall size of the per-screen GC cache, but allow multiple GCs per depth, as it quite common to need up to two temporary GCs along some drawing paths. Decrease the number of GCs we obtain in total by returning clean (i.e. a GC without a clip set) back to the screen pool after use. Compensate for the increased number of put/get by performing the query using atomic operations where available. So overall we see a dramatic reduction on the numbers of XCreateGC and XFreeGC, of even greater benefit for RENDER-less servers. --- src/cairo-xlib-private.h | 11 ++- src/cairo-xlib-screen.c | 206 +++++++++++++++++++++++++++++---------- src/cairo-xlib-surface.c | 58 +++++++---- 3 files changed, 200 insertions(+), 75 deletions(-) diff --git a/src/cairo-xlib-private.h b/src/cairo-xlib-private.h index 4995bc6f9..c79617bf5 100644 --- a/src/cairo-xlib-private.h +++ b/src/cairo-xlib-private.h @@ -103,8 +103,8 @@ struct _cairo_xlib_screen_info { cairo_bool_t has_font_options; cairo_font_options_t font_options; - GC gc[9]; - unsigned int gc_needs_clip_reset; + GC gc[4]; + int gc_depths; /* 4 x uint8_t, high bit == needs reset */ cairo_array_t visuals; }; @@ -154,12 +154,13 @@ _cairo_xlib_screen_info_close_display (cairo_xlib_screen_info_t *info); cairo_private GC _cairo_xlib_screen_get_gc (cairo_xlib_screen_info_t *info, - int depth, + unsigned int depth, + Drawable drawable, unsigned int *need_reset); -cairo_private cairo_status_t +cairo_private void _cairo_xlib_screen_put_gc (cairo_xlib_screen_info_t *info, - int depth, + unsigned int depth, GC gc, cairo_bool_t reset_clip); diff --git a/src/cairo-xlib-screen.c b/src/cairo-xlib-screen.c index 8f1e94971..e491a3337 100644 --- a/src/cairo-xlib-screen.c +++ b/src/cairo-xlib-screen.c @@ -268,19 +268,29 @@ void _cairo_xlib_screen_info_close_display (cairo_xlib_screen_info_t *info) { cairo_xlib_visual_info_t **visuals; - int i; + Display *dpy; + int i, old; CAIRO_MUTEX_LOCK (info->mutex); + + dpy = info->display->display; + +#if HAS_ATOMIC_OPS + do { + old = info->gc_depths; + } while (_cairo_atomic_int_cmpxchg (&info->gc_depths, old, 0) != old); +#else + old = info->gc_depths; +#endif + for (i = 0; i < ARRAY_LENGTH (info->gc); i++) { - if (info->gc[i] != NULL) { - XFreeGC (info->display->display, info->gc[i]); - info->gc[i] = NULL; - } + if (old >> (8*i) & 0x7f) + XFreeGC (dpy, info->gc[i]); } visuals = _cairo_array_index (&info->visuals, 0); for (i = 0; i < _cairo_array_num_elements (&info->visuals); i++) - _cairo_xlib_visual_info_destroy (info->display->display, visuals[i]); + _cairo_xlib_visual_info_destroy (dpy, visuals[i]); _cairo_array_truncate (&info->visuals, 0); CAIRO_MUTEX_UNLOCK (info->mutex); @@ -358,8 +368,8 @@ _cairo_xlib_screen_info_get (cairo_xlib_display_t *display, info->screen = screen; info->has_render = FALSE; info->has_font_options = FALSE; + info->gc_depths = 0; memset (info->gc, 0, sizeof (info->gc)); - info->gc_needs_clip_reset = 0; _cairo_array_init (&info->visuals, sizeof (cairo_xlib_visual_info_t*)); @@ -386,71 +396,165 @@ _cairo_xlib_screen_info_get (cairo_xlib_display_t *display, return CAIRO_STATUS_SUCCESS; } -static int -depth_to_index (int depth) -{ - switch(depth){ - case 1: return 1; - case 8: return 2; - case 12: return 3; - case 15: return 4; - case 16: return 5; - case 24: return 6; - case 30: return 7; - case 32: return 8; - } - return 0; -} - +#if HAS_ATOMIC_OPS GC _cairo_xlib_screen_get_gc (cairo_xlib_screen_info_t *info, - int depth, + unsigned int depth, + Drawable drawable, unsigned int *dirty) { + XGCValues gcv; + int i, new, old; GC gc; - cairo_bool_t needs_reset; - depth = depth_to_index (depth); + do { + gc = NULL; + old = info->gc_depths; + if (old == 0) + break; + + if (((old >> 0) & 0x7f) == depth) + i = 0; + else if (((old >> 8) & 0x7f) == depth) + i = 1; + else if (((old >> 16) & 0x7f) == depth) + i = 2; + else if (((old >> 24) & 0x7f) == depth) + i = 3; + else + break; + + gc = info->gc[i]; + new = old & ~(0xff << (8*i)); + } while (_cairo_atomic_int_cmpxchg (&info->gc_depths, old, new) != old); + + if (likely (gc != NULL)) { + (void) _cairo_atomic_ptr_cmpxchg (&info->gc[i], gc, NULL); + + if (old & 0x80 << (8 * i)) + *dirty |= CAIRO_XLIB_SURFACE_CLIP_DIRTY_GC; + + return gc; + } + + gcv.graphics_exposures = False; + return XCreateGC (info->display->display, drawable, + GCGraphicsExposures, &gcv); +} + +void +_cairo_xlib_screen_put_gc (cairo_xlib_screen_info_t *info, + unsigned int depth, + GC gc, + cairo_bool_t reset_clip) +{ + int i, old, new; + + depth |= reset_clip ? 0x80 : 0; + do { + do { + i = -1; + old = info->gc_depths; + + if (((old >> 0) & 0x7f) == 0) + i = 0; + else if (((old >> 8) & 0x7f) == 0) + i = 1; + else if (((old >> 16) & 0x7f) == 0) + i = 2; + else if (((old >> 24) & 0x7f) == 0) + i = 3; + else + goto out; + + new = old | (depth << (8*i)); + } while (_cairo_atomic_ptr_cmpxchg (&info->gc[i], NULL, gc) != NULL); + } while (_cairo_atomic_int_cmpxchg (&info->gc_depths, old, new) != old); + + return; + +out: + if (unlikely (_cairo_xlib_display_queue_work (info->display, + (cairo_xlib_notify_func) XFreeGC, + gc, + NULL))) + { + /* leak the server side resource... */ + XFree ((char *) gc); + } +} +#else +GC +_cairo_xlib_screen_get_gc (cairo_xlib_screen_info_t *info, + unsigned int depth, + Drawable drawable, + unsigned int *dirty) +{ + GC gc = NULL; + int i; CAIRO_MUTEX_LOCK (info->mutex); - gc = info->gc[depth]; - info->gc[depth] = NULL; - needs_reset = info->gc_needs_clip_reset & (1 << depth); - info->gc_needs_clip_reset &= ~(1 << depth); + for (i = 0; i < ARRAY_LENGTH (info->gc); i++) { + if (((info->gc_depths >> (8*i)) & 0x7f) == depth) { + if (info->gc_depths & 0x80 << (8*i)) + *dirty |= CAIRO_XLIB_SURFACE_CLIP_DIRTY_GC; + + info->gc_depths &= ~(0xff << (8*i)); + gc = info->gc[i]; + break; + } + } CAIRO_MUTEX_UNLOCK (info->mutex); - if (needs_reset) - *dirty |= CAIRO_XLIB_SURFACE_CLIP_DIRTY_GC; + if (gc == NULL) { + XGCValues gcv; + + gcv.graphics_exposures = False; + gc = XCreateGC (info->display->display, drawable, + GCGraphicsExposures, &gcv); + } return gc; } -cairo_status_t -_cairo_xlib_screen_put_gc (cairo_xlib_screen_info_t *info, int depth, GC gc, cairo_bool_t reset_clip) +void +_cairo_xlib_screen_put_gc (cairo_xlib_screen_info_t *info, + unsigned int depth, + GC gc, + cairo_bool_t reset_clip) { - cairo_status_t status = CAIRO_STATUS_SUCCESS; - GC oldgc; + int i; - depth = depth_to_index (depth); + depth |= reset_clip ? 0x80 : 0; CAIRO_MUTEX_LOCK (info->mutex); - oldgc = info->gc[depth]; - info->gc[depth] = gc; - if (reset_clip) - info->gc_needs_clip_reset |= 1 << depth; - else - info->gc_needs_clip_reset &= ~(1 << depth); - CAIRO_MUTEX_UNLOCK (info->mutex); - - if (oldgc != NULL) { - status = _cairo_xlib_display_queue_work (info->display, - (cairo_xlib_notify_func) XFreeGC, - oldgc, - NULL); + for (i = 0; i < ARRAY_LENGTH (info->gc); i++) { + if (((info->gc_depths >> (8*i)) & 0x7f) == 0) + break; } - return status; + if (i == ARRAY_LENGTH (info->gc)) { + cairo_status_t status; + + /* perform random substitution to ensure fair caching over depths */ + i = rand () % ARRAY_LENGTH (info->gc); + status = + _cairo_xlib_display_queue_work (info->display, + (cairo_xlib_notify_func) XFreeGC, + info->gc[i], + NULL); + if (unlikely (status)) { + /* leak the server side resource... */ + XFree ((char *) info->gc[i]); + } + } + + info->gc[i] = gc; + info->gc_depths &= ~(0xff << (8*i)); + info->gc_depths |= depth << (8*i); + CAIRO_MUTEX_UNLOCK (info->mutex); } +#endif cairo_status_t _cairo_xlib_screen_get_visual_info (cairo_xlib_screen_info_t *info, diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c index 4fa5d2a75..b68e6ed6d 100644 --- a/src/cairo-xlib-surface.c +++ b/src/cairo-xlib-surface.c @@ -67,6 +67,9 @@ _cairo_xlib_surface_create_internal (Display *dpy, static cairo_status_t _cairo_xlib_surface_ensure_gc (cairo_xlib_surface_t *surface); +static void +_cairo_xlib_surface_maybe_put_gc (cairo_xlib_surface_t *surface); + static void _cairo_xlib_surface_ensure_src_picture (cairo_xlib_surface_t *surface); @@ -305,14 +308,11 @@ _cairo_xlib_surface_finish (void *abstract_surface) } if (surface->gc != NULL) { - cairo_status_t status2; - status2 = _cairo_xlib_screen_put_gc (surface->screen_info, - surface->depth, - surface->gc, - surface->gc_has_clip_rects); + _cairo_xlib_screen_put_gc (surface->screen_info, + surface->depth, + surface->gc, + surface->gc_has_clip_rects); surface->gc = NULL; - if (status == CAIRO_STATUS_SUCCESS) - status = status2; } if (surface->clip_rects != surface->embedded_clip_rects) @@ -662,8 +662,7 @@ _get_image_surface (cairo_xlib_surface_t *surface, ximage = NULL; } - if (!ximage) - { + if (!ximage) { /* XGetImage from a window is dangerous because it can * produce errors if the window is unmapped or partially @@ -695,9 +694,12 @@ _get_image_surface (cairo_xlib_surface_t *surface, XFreePixmap (surface->dpy, pixmap); } + + if (!ximage) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_xlib_surface_maybe_put_gc (surface); } - if (!ximage) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); _swap_ximage_to_native (ximage); @@ -902,19 +904,14 @@ _cairo_xlib_surface_ensure_dst_picture (cairo_xlib_surface_t *surface) static cairo_status_t _cairo_xlib_surface_ensure_gc (cairo_xlib_surface_t *surface) { - XGCValues gcv; if (surface->gc == NULL) { surface->gc = _cairo_xlib_screen_get_gc (surface->screen_info, surface->depth, + surface->drawable, &surface->clip_dirty); - if (surface->gc == NULL) { - gcv.graphics_exposures = False; - surface->gc = XCreateGC (surface->dpy, surface->drawable, - GCGraphicsExposures, &gcv); - if (unlikely (surface->gc == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } + if (unlikely (surface->gc == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); } if (surface->clip_dirty & CAIRO_XLIB_SURFACE_CLIP_DIRTY_GC) @@ -923,6 +920,21 @@ _cairo_xlib_surface_ensure_gc (cairo_xlib_surface_t *surface) return CAIRO_STATUS_SUCCESS; } +static void +_cairo_xlib_surface_maybe_put_gc (cairo_xlib_surface_t *surface) +{ + /* return the GC back to the common pool if clean */ + + if (surface->gc_has_clip_rects) + return; + + _cairo_xlib_screen_put_gc (surface->screen_info, + surface->depth, + surface->gc, + FALSE); + surface->gc = NULL; +} + static cairo_status_t _draw_image_surface (cairo_xlib_surface_t *surface, cairo_image_surface_t *image, @@ -1081,6 +1093,8 @@ _draw_image_surface (cairo_xlib_surface_t *surface, &ximage, src_x, src_y, dst_x, dst_y, width, height); + _cairo_xlib_surface_maybe_put_gc (surface); + BAIL: if (own_data) free (ximage.data); @@ -1870,6 +1884,8 @@ _cairo_xlib_surface_composite (cairo_operator_t op, src_y + src_attr.y_offset + ity, width, height, dst_x, dst_y); + + _cairo_xlib_surface_maybe_put_gc (dst); break; case DO_XTILE: @@ -1896,6 +1912,8 @@ _cairo_xlib_surface_composite (cairo_operator_t op, XFillRectangle (dst->dpy, dst->drawable, dst->gc, dst_x, dst_y, width, height); + + _cairo_xlib_surface_maybe_put_gc (dst); break; case DO_UNSUPPORTED: @@ -1969,6 +1987,8 @@ _cairo_xlib_surface_solid_fill_rectangles (cairo_xlib_surface_t *surface, rects[i].width, rects[i].height); } + _cairo_xlib_surface_maybe_put_gc (surface); + BAIL: _cairo_pattern_release_surface (&solid.base, solid_surface, &attrs);