mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2026-01-12 02:10:23 +01:00
[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.
This commit is contained in:
parent
7f238f5424
commit
c0e01d9cd7
3 changed files with 200 additions and 75 deletions
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue