[PATCH] [cairo-xlib] Use XShm.

Chris Wilson chris at chris-wilson.co.uk
Thu Oct 18 11:59:26 PDT 2007


If available, try to allocate a shared memory segment and create image
surfaces from within that segment.

An XShm image surface can be created by the user (or higher levels) via
the cairo_image_surface_create_for_target() and passing an xlib surface
as the target. Within the xlib backend, the XShm images are created when
retrieving pixels from the drawable (i.e in acquire_dest_image()) which
should improve the performance of backend on local servers.

Although not apparent whilst running cairo-test or cairo-perf, there is
a race condition where we may reuse the image before the XServer has
finished copying its contents. (For similar reasons, we require an XSync
after the XShmAttach.)
---
 configure.in                     |    8 +
 src/cairo-xlib-display.c         |   19 ++
 src/cairo-xlib-private.h         |    9 +
 src/cairo-xlib-surface-private.h |    1 +
 src/cairo-xlib-surface.c         |  340 +++++++++++++++++++++++++++++++++++++-
 5 files changed, 371 insertions(+), 6 deletions(-)

diff --git a/configure.in b/configure.in
index 59a7375..9cfb221 100644
--- a/configure.in
+++ b/configure.in
@@ -278,6 +278,14 @@ CAIRO_BACKEND_ENABLE(xlib, Xlib, xlib, XLIB_SURFACE, auto, [
 		       xlib_NONPKGCONFIG_LIBS="$X_PRE_LIBS $X_LIBS -lX11 $X_EXTRA_LIBS"
 		       xlib_NONPKGCONFIG_CFLAGS=$X_CFLAGS
 		     fi])
+  if test "x$use_xlib" = "xyes"; then
+    old_CPPFLAGS=$CPPFLAGS
+    CPPFLAGS="$CPPFLAGS $xlib_CFLAGS $xlib_NONPKGCONFIG_CFLAGS"
+    AC_CHECK_HEADER(X11/extensions/XShm.h,
+	AC_DEFINE([HAVE_XSHM], [1],
+	    [Define to 1 if you have the X11 Shared Memory extension]))
+    CPPFLAGS=$old_CPPFLAGS
+  fi
 ])
 
 CAIRO_BACKEND_ENABLE(xlib_xrender, Xlib Xrender, xlib-xrender, XLIB_XRENDER_SURFACE, auto, [
diff --git a/src/cairo-xlib-display.c b/src/cairo-xlib-display.c
index efb53ba..344cd6d 100644
--- a/src/cairo-xlib-display.c
+++ b/src/cairo-xlib-display.c
@@ -38,6 +38,9 @@
 #include <fontconfig/fontconfig.h>
 
 #include <X11/Xlibint.h>	/* For XESetCloseDisplay */
+#ifdef HAVE_XSHM
+#include <X11/extensions/XShm.h>
+#endif
 
 typedef int (*cairo_xlib_error_func_t) (Display     *display,
 					XErrorEvent *event);
@@ -75,6 +78,9 @@ _cairo_xlib_call_close_display_hooks (cairo_xlib_display_t *display)
     for (screen = display->screens; screen != NULL; screen = screen->next)
 	_cairo_xlib_screen_info_close_display (screen);
 
+    if (display->last_shm_info)
+	_cairo_xlib_shm_destroy (NULL, display->last_shm_info);
+
     hooks = display->close_display_hooks;
     while (hooks != NULL) {
 	display->close_display_hooks = NULL;
@@ -290,6 +296,19 @@ _cairo_xlib_display_get (Display *dpy)
     display->close_display_hooks = NULL;
     display->closed = FALSE;
 
+    display->last_shm_info = NULL;
+    display->use_shm = FALSE;
+    display->have_shm_pixmaps = FALSE;
+#ifdef HAVE_XSHM
+    if (XShmQueryExtension (dpy)) {
+	Bool pixmaps;
+	if (XShmQueryVersion (dpy, &major_unused, &minor_unused, &pixmaps)) {
+	    display->use_shm = TRUE;
+	    display->have_shm_pixmaps = pixmaps;
+	}
+    }
+#endif
+
     display->buggy_repeat = FALSE;
     if (strstr (ServerVendor (dpy), "X.Org") != NULL) {
 	if (VendorRelease (dpy) <= 60802000)
diff --git a/src/cairo-xlib-private.h b/src/cairo-xlib-private.h
index 05c7d0e..71f8613 100644
--- a/src/cairo-xlib-private.h
+++ b/src/cairo-xlib-private.h
@@ -57,6 +57,11 @@ struct _cairo_xlib_hook {
     const void *key;
 };
 
+typedef struct _cairo_xlib_shm_info cairo_xlib_shm_info_t;
+
+cairo_private void
+_cairo_xlib_shm_destroy (Display *dpy, cairo_xlib_shm_info_t *shm_info);
+
 struct _cairo_xlib_display {
     cairo_xlib_display_t *next;
     cairo_reference_count_t ref_count;
@@ -68,9 +73,13 @@ struct _cairo_xlib_display {
     cairo_xlib_job_t *workqueue;
     cairo_freelist_t wq_freelist;
 
+    cairo_xlib_shm_info_t *last_shm_info;
+
     cairo_freelist_t hook_freelist;
     cairo_xlib_hook_t *close_display_hooks;
     unsigned int buggy_repeat :1;
+    unsigned int use_shm :1;
+    unsigned int have_shm_pixmaps :1;
     unsigned int closed :1;
 };
 
diff --git a/src/cairo-xlib-surface-private.h b/src/cairo-xlib-surface-private.h
index 3bbf43e..e78f476 100644
--- a/src/cairo-xlib-surface-private.h
+++ b/src/cairo-xlib-surface-private.h
@@ -52,6 +52,7 @@ struct _cairo_xlib_surface {
     Visual *visual;
 
     int use_pixmap;
+    cairo_bool_t use_shm;
 
     int render_major;
     int render_minor;
diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c
index 1cf2ce2..6272929 100644
--- a/src/cairo-xlib-surface.c
+++ b/src/cairo-xlib-surface.c
@@ -34,6 +34,7 @@
  * Contributor(s):
  *	Carl D. Worth <cworth at cworth.org>
  *	Behdad Esfahbod <behdad at behdad.org>
+ *	Chris Wilson <chris at chris-wilson.co.uk>
  */
 
 #include "cairoint.h"
@@ -42,6 +43,28 @@
 #include "cairo-xlib-surface-private.h"
 #include "cairo-clip-private.h"
 
+#ifdef HAVE_XSHM
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <X11/extensions/XShm.h>
+#include <errno.h>
+
+struct _cairo_xlib_shm_info {
+    Display *dpy;
+    XShmSegmentInfo shm;
+    int size;
+};
+
+static cairo_status_t
+_cairo_xlib_surface_create_image_surface_internal (cairo_xlib_surface_t *target,
+	                                           cairo_format_t format,
+						   int width,
+						   int height,
+						   cairo_surface_t **image_out);
+
+static cairo_user_data_key_t _cairo_xlib_surface_shm_info_key;
+#endif
+
 /* Xlib doesn't define a typedef, so define one ourselves */
 typedef int (*cairo_xlib_error_func_t) (Display     *display,
 					XErrorEvent *event);
@@ -338,6 +361,11 @@ static int
 _noop_error_handler (Display     *display,
 		     XErrorEvent *event)
 {
+#if 0
+    char buf[1024];
+    XGetErrorText (display, event->error_code, buf, sizeof (buf));
+    printf ("XError: %s\n", buf);
+#endif
     return False;		/* return value is ignored */
 }
 
@@ -451,6 +479,73 @@ _swap_ximage_to_native (XImage *ximage)
     }
 }
 
+#ifdef HAVE_XSHM
+static cairo_status_t
+get_shm_image_surface (cairo_xlib_surface_t *target,
+	               int x1, int y1, int x2, int y2,
+		       cairo_image_surface_t **image_out)
+{
+    XImage ximage;
+    uint32_t bpp, red, green, blue;
+    int native_byte_order = _native_byte_order_lsb () ? LSBFirst : MSBFirst;
+    cairo_status_t status;
+    cairo_surface_t *surface;
+    cairo_image_surface_t *image;
+    cairo_xlib_shm_info_t *shm_info;
+    cairo_xlib_error_func_t old_handler;
+    Status success;
+
+    status = _cairo_xlib_surface_create_image_surface_internal (target,
+	                     _cairo_format_from_content (target->base.content),
+			     x2 - x1, y2 - y1,
+			     &surface);
+    if (status)
+	return status;
+
+    shm_info = _cairo_user_data_array_get_data (&surface->user_data,
+	                                     &_cairo_xlib_surface_shm_info_key);
+    assert (shm_info != NULL);
+
+    image = (cairo_image_surface_t *) surface;
+    _pixman_format_to_masks (image->pixman_format, &bpp, &red, &green, &blue);
+    ximage.width = image->width;
+    ximage.height = image->height;
+    ximage.format = ZPixmap;
+    ximage.data = (char *)image->data;
+    ximage.obdata = (char *) &shm_info->shm;
+    ximage.byte_order = native_byte_order;
+    ximage.bitmap_unit = 32;	/* always for libpixman */
+    ximage.bitmap_bit_order = native_byte_order;
+    ximage.bitmap_pad = 32;	/* always for libpixman */
+    ximage.depth = image->depth;
+    ximage.bytes_per_line = image->stride;
+    ximage.bits_per_pixel = bpp;
+    ximage.red_mask = red;
+    ximage.green_mask = green;
+    ximage.blue_mask = blue;
+    ximage.xoffset = 0;
+
+    XInitImage (&ximage);
+
+    old_handler = XSetErrorHandler (_noop_error_handler);
+
+    success = XShmGetImage (target->dpy, target->drawable,
+	                    &ximage, x1, y1, AllPlanes);
+
+    XSetErrorHandler (old_handler);
+
+    if (!success) {
+	/* most probably BadMatch... */
+	target->use_shm = FALSE;
+	cairo_surface_destroy (surface);
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    *image_out = image;
+    return CAIRO_STATUS_SUCCESS;
+}
+#endif
+
 static cairo_status_t
 _get_image_surface (cairo_xlib_surface_t    *surface,
 		    cairo_rectangle_int_t   *interest_rect,
@@ -462,6 +557,7 @@ _get_image_surface (cairo_xlib_surface_t    *surface,
     short x1, y1, x2, y2;
     cairo_format_masks_t masks;
     pixman_format_code_t pixman_format;
+    cairo_status_t status;
 
     x1 = 0;
     y1 = 0;
@@ -498,7 +594,13 @@ _get_image_surface (cairo_xlib_surface_t    *surface,
 	image_rect->height = y2 - y1;
     }
 
-    /* XXX: This should try to use the XShm extension if available */
+    *image_out = NULL;
+
+#ifdef HAVE_XSHM
+    status = get_shm_image_surface (surface, x1, y1, x2, y2, image_out);
+    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+	return status;
+#endif
 
     if (surface->use_pixmap == 0)
     {
@@ -718,9 +820,26 @@ _draw_image_surface (cairo_xlib_surface_t   *surface,
     uint32_t bpp, red, green, blue;
     int native_byte_order = _native_byte_order_lsb () ? LSBFirst : MSBFirst;
     cairo_status_t status;
+#ifdef HAVE_XSHM
+    cairo_xlib_shm_info_t *shm_info;
+#endif
+
+    if (src_x < 0) {
+	width += src_x;
+	src_x = 0;
+    }
+    if (src_x + width > image->width)
+	width = image->width - src_x;
+
+    if (src_y < 0) {
+	height += src_y;
+	src_y = 0;
+    }
+    if (src_y + height > image->height)
+	height = image->height - src_y;
 
     _pixman_format_to_masks (image->pixman_format, &bpp, &red, &green, &blue);
-    
+
     ximage.width = image->width;
     ximage.height = image->height;
     ximage.format = ZPixmap;
@@ -742,9 +861,23 @@ _draw_image_surface (cairo_xlib_surface_t   *surface,
     status = _cairo_xlib_surface_ensure_gc (surface);
     if (status)
 	return status;
-    XPutImage(surface->dpy, surface->drawable, surface->gc,
-	      &ximage, src_x, src_y, dst_x, dst_y,
-	      width, height);
+
+#ifdef HAVE_XSHM
+    shm_info = _cairo_user_data_array_get_data (&image->base.user_data,
+	                                     &_cairo_xlib_surface_shm_info_key);
+    if (shm_info != NULL) {
+	ximage.obdata = (char *) &shm_info->shm;
+	XShmPutImage(surface->dpy, surface->drawable, surface->gc,
+		  &ximage, src_x, src_y, dst_x, dst_y,
+		  width, height,
+		  False);
+    } else
+#endif
+    {
+	XPutImage(surface->dpy, surface->drawable, surface->gc,
+		  &ximage, src_x, src_y, dst_x, dst_y,
+		  width, height);
+    }
 
     return CAIRO_STATUS_SUCCESS;
 
@@ -1849,6 +1982,195 @@ _cairo_xlib_surface_reset (void *abstract_surface)
     return CAIRO_STATUS_SUCCESS;
 }
 
+#ifdef HAVE_XSHM
+static void
+_cairo_xlib_shm_remove (Display *dpy,
+	                void    *data)
+{
+    cairo_surface_t *surface = data;
+    cairo_status_t status;
+
+    status = _cairo_user_data_array_set_data (
+			&surface->user_data,
+			&_cairo_xlib_surface_shm_info_key,
+			NULL, NULL);
+    assert (status == CAIRO_STATUS_SUCCESS);
+}
+
+void
+_cairo_xlib_shm_destroy (Display *dpy, cairo_xlib_shm_info_t *shm_info)
+{
+    if (dpy != NULL)
+	XShmDetach (shm_info->dpy, &shm_info->shm);
+    shmdt (shm_info->shm.shmaddr);
+    free (shm_info);
+}
+
+static void
+_cairo_xlib_shm_detach (void *data)
+{
+    cairo_xlib_shm_info_t *shm_info = data;
+    cairo_xlib_display_t *display;
+
+    _cairo_xlib_remove_close_display_hooks (shm_info->dpy, shm_info);
+
+    display = _cairo_xlib_display_get (shm_info->dpy);
+    if (display != NULL) {
+	CAIRO_MUTEX_LOCK (display->mutex);
+	if (display->last_shm_info == NULL ||
+		display->last_shm_info->size < shm_info->size) {
+	    cairo_xlib_shm_info_t *tmp = display->last_shm_info;
+	    display->last_shm_info = shm_info;
+	    shm_info = tmp;
+	}
+	CAIRO_MUTEX_UNLOCK (display->mutex);
+	_cairo_xlib_display_destroy (display);
+    }
+
+    if (shm_info != NULL)
+	_cairo_xlib_shm_destroy (shm_info->dpy, shm_info);
+}
+
+static cairo_status_t
+_cairo_xlib_surface_create_image_surface_internal (cairo_xlib_surface_t *target,
+	                                           cairo_format_t format,
+						   int width,
+						   int height,
+						   cairo_surface_t **image_out)
+{
+    cairo_xlib_display_t *display = target->screen_info->display;
+    cairo_xlib_shm_info_t *shm_info;
+    int stride;
+    pixman_format_code_t pixman_format;
+    cairo_surface_t *surface;
+    cairo_status_t status;
+    Status success;
+
+    if (!target->use_shm)
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    display = target->screen_info->display;
+    if (display == NULL)
+	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    if (!display->use_shm) {
+	target->use_shm = FALSE;
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    pixman_format = _cairo_format_to_pixman_format_code (format);
+    stride = PIXMAN_FORMAT_BPP (pixman_format) / 8 * width;
+    stride = (stride + 3) & -4;
+
+    CAIRO_MUTEX_LOCK (display->mutex);
+    shm_info = display->last_shm_info;
+    if (shm_info != NULL && shm_info->size >= stride * height) {
+	display->last_shm_info = NULL;
+    } else
+	shm_info = NULL;
+    CAIRO_MUTEX_UNLOCK (display->mutex);
+
+    if (shm_info == NULL) {
+	shm_info = malloc (sizeof (cairo_xlib_shm_info_t));
+	if (shm_info == NULL)
+	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+	shm_info->dpy = target->dpy;
+	shm_info->size = stride * height;
+	shm_info->shm.shmid = shmget (IPC_PRIVATE,
+				      stride * height,
+				      IPC_CREAT | 0600);
+	if (shm_info->shm.shmid == -1) {
+	    if (errno != EINVAL)
+		display->use_shm = FALSE;
+	    free (shm_info);
+	    return CAIRO_INT_STATUS_UNSUPPORTED;
+	}
+
+	shm_info->shm.readOnly = FALSE;
+	shm_info->shm.shmaddr = shmat (shm_info->shm.shmid, NULL, 0);
+	if (shm_info->shm.shmaddr == (char *) -1) {
+	    display->use_shm = FALSE;
+	    shmctl (shm_info->shm.shmid, IPC_RMID, NULL);
+	    free (shm_info);
+	    return CAIRO_INT_STATUS_UNSUPPORTED;
+	}
+
+	success = XShmAttach (target->dpy, &shm_info->shm);
+
+	XSync (target->dpy, False);
+	/* XXX check for XErrors */
+
+	shmctl (shm_info->shm.shmid, IPC_RMID, NULL);
+
+	if (!success) {
+	    shmdt (shm_info->shm.shmaddr);
+	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+	}
+    }
+
+    surface = cairo_image_surface_create_for_data (
+	    (unsigned char *) shm_info->shm.shmaddr, format,
+	    width, height, stride);
+    status = cairo_surface_status (surface);
+    if (status) {
+	_cairo_xlib_shm_detach (shm_info);
+	return status;
+    }
+
+    status = _cairo_user_data_array_set_data (&surface->user_data,
+		                              &_cairo_xlib_surface_shm_info_key,
+					      shm_info,
+					      _cairo_xlib_shm_detach);
+    if (status) {
+	_cairo_xlib_shm_detach (shm_info);
+	cairo_surface_destroy (surface);
+	return status;
+    }
+
+    if (! _cairo_xlib_add_close_display_hook (target->dpy,
+	                                      _cairo_xlib_shm_remove,
+					      surface,
+					      shm_info)) {
+	cairo_surface_destroy (surface);
+	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    *image_out = surface;
+    return CAIRO_STATUS_SUCCESS;
+}
+#else
+void
+_cairo_xlib_shm_destroy (Display *dpy, cairo_xlib_shm_info_t *shm_info)
+{
+}
+#endif
+
+static cairo_surface_t *
+_cairo_xlib_surface_create_image_surface (cairo_format_t  format,
+	                                  int             width,
+					  int             height,
+					  void           *abstract_target)
+{
+#ifdef HAVE_XSHM
+    cairo_xlib_surface_t *target = abstract_target;
+    cairo_surface_t *image;
+    cairo_status_t status;
+
+    status = _cairo_xlib_surface_create_image_surface_internal (target,
+							       format,
+							       width,
+							       height,
+							       &image);
+    if (status == CAIRO_STATUS_SUCCESS)
+	return image;
+    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+	return (cairo_surface_t *) &_cairo_surface_nil;
+#endif
+
+    return cairo_image_surface_create (format, width, height);
+}
+
 static const cairo_surface_backend_t cairo_xlib_surface_backend = {
     CAIRO_SURFACE_TYPE_XLIB,
     _cairo_xlib_surface_create_similar,
@@ -1881,7 +2203,11 @@ static const cairo_surface_backend_t cairo_xlib_surface_backend = {
     NULL,  /* snapshot */
     _cairo_xlib_surface_is_similar,
 
-    _cairo_xlib_surface_reset
+    _cairo_xlib_surface_reset,
+
+    NULL, /* fill stroke */
+
+    _cairo_xlib_surface_create_image_surface
 };
 
 /**
@@ -2012,6 +2338,8 @@ _cairo_xlib_surface_create_internal (Display		       *dpy,
     surface->width = width;
     surface->height = height;
 
+    surface->use_shm = screen_info->display->use_shm;
+
     surface->buggy_repeat = screen_info->display->buggy_repeat;
 
     surface->dst_picture = None;
-- 
1.5.2.5


--GvXjxJ+pjyke8COw--


More information about the cairo mailing list