[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