From b09de96a17028c3c936f6a196e048c4f224da89f Mon Sep 17 00:00:00 2001 From: Ian Romanick Date: Tue, 25 Mar 2008 18:07:56 -0700 Subject: [PATCH] xlib: Add support for MIT-SHM in xlib winsys driver Gives about a 3% performance increase in gears on x86-64 (non-tiled) and about 10% performance increase in gears on Cell (tiled). I actually expected more of a boost. :( --- src/gallium/winsys/xlib/xm_api.c | 16 +- src/gallium/winsys/xlib/xm_winsys.c | 301 ++++++++++++++++++++++++++------ src/gallium/winsys/xlib/xm_winsys_aub.h | 3 +- src/gallium/winsys/xlib/xmesaP.h | 3 + 4 files changed, 267 insertions(+), 56 deletions(-) diff --git a/src/gallium/winsys/xlib/xm_api.c b/src/gallium/winsys/xlib/xm_api.c index e5fef1d..6b46327 100644 --- a/src/gallium/winsys/xlib/xm_api.c +++ b/src/gallium/winsys/xlib/xm_api.c @@ -104,12 +104,16 @@ static int host_byte_order( void ) * 1 = shared XImage support available * 2 = shared Pixmap support available also */ -static int check_for_xshm( XMesaDisplay *display ) +int xmesa_check_for_xshm( XMesaDisplay *display ) { #if defined(USE_XSHM) && !defined(XFree86Server) int major, minor, ignore; Bool pixmaps; + if (getenv("MESA_NOSHM")) { + return 0; + } + if (XQueryExtension( display, "MIT-SHM", &ignore, &ignore, &ignore )) { if (XShmQueryVersion( display, &major, &minor, &pixmaps )==True) { return (pixmaps==True) ? 2 : 1; @@ -528,7 +532,7 @@ initialize_visual_and_buffer(XMesaVisual v, XMesaBuffer b, /* Setup for single/double buffering */ if (v->mesa_visual.doubleBufferMode) { /* Double buffered */ - b->shm = check_for_xshm( v->display ); + b->shm = xmesa_check_for_xshm( v->display ); } /* X11 graphics context */ @@ -770,13 +774,16 @@ XMesaContext XMesaCreateContext( XMesaVisual v, XMesaContext share_list ) pf = choose_pixel_format(v); assert(pf); + c->xm_visual = v; + c->xm_buffer = NULL; /* set later by XMesaMakeCurrent */ + if (!getenv("XM_AUB")) { xmesa_mode = XMESA_SOFTPIPE; pipe = xmesa_create_pipe_context( c, pf ); } else { xmesa_mode = XMESA_AUB; - pipe = xmesa_create_i965simple( xmesa_get_pipe_winsys_aub() ); + pipe = xmesa_create_i965simple(xmesa_get_pipe_winsys_aub(v)); } c->st = st_create_context(pipe, &v->mesa_visual, @@ -800,9 +807,6 @@ XMesaContext XMesaCreateContext( XMesaVisual v, XMesaContext share_list ) #endif /* finish up xmesa context initializations */ - c->xm_visual = v; - c->xm_buffer = NULL; /* set later by XMesaMakeCurrent */ - c->st->haveFramebufferSurfaces = GL_TRUE; return c; diff --git a/src/gallium/winsys/xlib/xm_winsys.c b/src/gallium/winsys/xlib/xm_winsys.c index 018cf01..7baaae2 100644 --- a/src/gallium/winsys/xlib/xm_winsys.c +++ b/src/gallium/winsys/xlib/xm_winsys.c @@ -65,8 +65,19 @@ struct xm_buffer boolean userBuffer; /** Is this a user-space buffer? */ void *data; void *mapped; + + XImage *tempImage; + int shm; +#if defined(USE_XSHM) && !defined(XFree86Server) + XShmSegmentInfo shminfo; +#endif }; +#if defined(USE_XSHM) && !defined(XFree86Server) +# define XSHM_ENABLED(b) ((b)->shm) +#else +# define XSHM_ENABLED(b) 0 +#endif struct xmesa_surface { @@ -88,6 +99,16 @@ struct xmesa_softpipe_winsys }; +struct xmesa_pipe_winsys +{ + struct pipe_winsys base; + struct xmesa_visual *xm_visual; + int shm; +}; + + +static void alloc_shm_ximage(struct xm_buffer *b, struct xmesa_buffer *xmb, + unsigned width, unsigned height); /** Cast wrapper */ static INLINE struct xmesa_surface * @@ -136,13 +157,27 @@ xm_buffer_unmap(struct pipe_winsys *pws, struct pipe_buffer *buf) static void xm_buffer_destroy(struct pipe_winsys *pws, - struct pipe_buffer *buf) + struct pipe_buffer *buf) { struct xm_buffer *oldBuf = xm_buffer(buf); if (oldBuf->data) { - if (!oldBuf->userBuffer) - align_free(oldBuf->data); +#if defined(USE_XSHM) && !defined(XFree86Server) + if (oldBuf->shminfo.shmid >= 0) { + shmdt(oldBuf->shminfo.shmaddr); + shmctl(oldBuf->shminfo.shmid, IPC_RMID, 0); + + oldBuf->shminfo.shmid = -1; + oldBuf->shminfo.shmaddr = (char *) -1; + } + else +#endif + { + if (!oldBuf->userBuffer) { + align_free(oldBuf->data); + } + } + oldBuf->data = NULL; } @@ -157,7 +192,7 @@ xm_buffer_destroy(struct pipe_winsys *pws, static void xmesa_display_surface_tiled(XMesaBuffer b, const struct pipe_surface *surf) { - XImage *ximage = b->tempImage; + XImage *ximage; struct xm_buffer *xm_buf = xm_buffer(surf->buffer); const uint tilesPerRow = (surf->width + TILE_SIZE - 1) / TILE_SIZE; uint x, y; @@ -166,10 +201,18 @@ xmesa_display_surface_tiled(XMesaBuffer b, const struct pipe_surface *surf) assert(ximage->format); assert(ximage->bitmap_unit); - /* update XImage's fields */ - ximage->width = TILE_SIZE; - ximage->height = TILE_SIZE; - ximage->bytes_per_line = TILE_SIZE * 4; + if (XSHM_ENABLED(xm_buf) && (xm_buf->tempImage == NULL)) { + alloc_shm_ximage(xm_buf, b, TILE_SIZE, TILE_SIZE); + } + + ximage = (XSHM_ENABLED(xm_buf)) ? xm_buf->tempImage : b->tempImage; + + if (!XSHM_ENABLED(xm_buf)) { + /* update XImage's fields */ + ximage->width = TILE_SIZE; + ximage->height = TILE_SIZE; + ximage->bytes_per_line = TILE_SIZE * 4; + } for (y = 0; y < surf->height; y += TILE_SIZE) { for (x = 0; x < surf->width; x += TILE_SIZE) { @@ -183,8 +226,15 @@ xmesa_display_surface_tiled(XMesaBuffer b, const struct pipe_surface *surf) ximage->data = (char *) xm_buf->data + offset; - XPutImage(b->xm_visual->display, b->drawable, b->gc, - ximage, 0, 0, dx, dy, TILE_SIZE, TILE_SIZE); + if (XSHM_ENABLED(xm_buf)) { +#if defined(USE_XSHM) && !defined(XFree86Server) + XShmPutImage(b->xm_visual->display, b->drawable, b->gc, + ximage, 0, 0, x, y, TILE_SIZE, TILE_SIZE, False); +#endif + } else { + XPutImage(b->xm_visual->display, b->drawable, b->gc, + ximage, 0, 0, dx, dy, TILE_SIZE, TILE_SIZE); + } } } } @@ -197,7 +247,7 @@ xmesa_display_surface_tiled(XMesaBuffer b, const struct pipe_surface *surf) void xmesa_display_surface(XMesaBuffer b, const struct pipe_surface *surf) { - XImage *ximage = b->tempImage; + XImage *ximage; struct xm_buffer *xm_buf = xm_buffer(surf->buffer); const struct xmesa_surface *xm_surf = xmesa_surface((struct pipe_surface *) surf); @@ -207,19 +257,33 @@ xmesa_display_surface(XMesaBuffer b, const struct pipe_surface *surf) return; } - /* check that the XImage has been previously initialized */ - assert(ximage->format); - assert(ximage->bitmap_unit); - /* update XImage's fields */ - ximage->width = surf->width; - ximage->height = surf->height; - ximage->bytes_per_line = surf->pitch * (ximage->bits_per_pixel / 8); + if (XSHM_ENABLED(xm_buf) && (xm_buf->tempImage == NULL)) { + alloc_shm_ximage(xm_buf, b, surf->pitch, surf->height); + } + + ximage = (XSHM_ENABLED(xm_buf)) ? xm_buf->tempImage : b->tempImage; ximage->data = xm_buf->data; /* display image in Window */ - XPutImage(b->xm_visual->display, b->drawable, b->gc, - ximage, 0, 0, 0, 0, surf->width, surf->height); + if (XSHM_ENABLED(xm_buf)) { +#if defined(USE_XSHM) && !defined(XFree86Server) + XShmPutImage(b->xm_visual->display, b->drawable, b->gc, + ximage, 0, 0, 0, 0, surf->width, surf->height, False); +#endif + } else { + /* check that the XImage has been previously initialized */ + assert(ximage->format); + assert(ximage->bitmap_unit); + + /* update XImage's fields */ + ximage->width = surf->width; + ximage->height = surf->height; + ximage->bytes_per_line = surf->pitch * surf->cpp; + + XPutImage(b->xm_visual->display, b->drawable, b->gc, + ximage, 0, 0, 0, 0, surf->width, surf->height); + } } @@ -256,6 +320,119 @@ xm_get_name(struct pipe_winsys *pws) } +#if defined(USE_XSHM) && !defined(XFree86Server) +static volatile int mesaXErrorFlag = 0; + +/** + * Catches potential Xlib errors. + */ +static int +mesaHandleXError(XMesaDisplay *dpy, XErrorEvent *event) +{ + (void) dpy; + (void) event; + mesaXErrorFlag = 1; + return 0; +} + + +static GLboolean alloc_shm(struct xm_buffer *buf, unsigned size) +{ + XShmSegmentInfo *const shminfo = & buf->shminfo; + + shminfo->shmid = shmget(IPC_PRIVATE, size, IPC_CREAT|0777); + if (shminfo->shmid < 0) { + return GL_FALSE; + } + + shminfo->shmaddr = (char *) shmat(shminfo->shmid, 0, 0); + if (shminfo->shmaddr == (char *) -1) { + shmctl(shminfo->shmid, IPC_RMID, 0); + return GL_FALSE; + } + + shminfo->readOnly = False; + return GL_TRUE; +} + + +/** + * Allocate a shared memory XImage back buffer for the given XMesaBuffer. + */ +static void +alloc_shm_ximage(struct xm_buffer *b, struct xmesa_buffer *xmb, + unsigned width, unsigned height) +{ + /* + * We have to do a _lot_ of error checking here to be sure we can + * really use the XSHM extension. It seems different servers trigger + * errors at different points if the extension won't work. Therefore + * we have to be very careful... + */ +#if 0 + GC gc; +#endif + int (*old_handler)(XMesaDisplay *, XErrorEvent *); + + b->tempImage = XShmCreateImage(xmb->xm_visual->display, + xmb->xm_visual->visinfo->visual, + xmb->xm_visual->visinfo->depth, + ZPixmap, + NULL, + &b->shminfo, + width, height); + if (b->tempImage == NULL) { + b->shm = 0; + return; + } + + + mesaXErrorFlag = 0; + old_handler = XSetErrorHandler(mesaHandleXError); + /* This may trigger the X protocol error we're ready to catch: */ + XShmAttach(xmb->xm_visual->display, &b->shminfo); + XSync(xmb->xm_visual->display, False); + + if (mesaXErrorFlag) { + /* we are on a remote display, this error is normal, don't print it */ + XFlush(xmb->xm_visual->display); + mesaXErrorFlag = 0; + XDestroyImage(b->tempImage); + b->tempImage = NULL; + b->shm = 0; + (void) XSetErrorHandler(old_handler); + return; + } + + + /* Finally, try an XShmPutImage to be really sure the extension works */ +#if 0 + gc = XCreateGC(xmb->xm_visual->display, xmb->drawable, 0, NULL); + XShmPutImage(xmb->xm_visual->display, xmb->drawable, gc, + b->tempImage, 0, 0, 0, 0, 1, 1 /*one pixel*/, False); + XSync(xmb->xm_visual->display, False); + XFreeGC(xmb->xm_visual->display, gc); + (void) XSetErrorHandler(old_handler); + if (mesaXErrorFlag) { + XFlush(xmb->xm_visual->display); + mesaXErrorFlag = 0; + XDestroyImage(b->tempImage); + b->tempImage = NULL; + b->shm = 0; + return; + } +#endif +} +#else +static void +alloc_shm_ximage(struct xm_buffer *b, struct xmesa_buffer *xmb, + unsigned width, unsigned height) +{ + b->shm = 0; +} +#endif + + static struct pipe_buffer * xm_buffer_create(struct pipe_winsys *pws, unsigned alignment, @@ -263,13 +440,35 @@ xm_buffer_create(struct pipe_winsys *pws, unsigned size) { struct xm_buffer *buffer = CALLOC_STRUCT(xm_buffer); +#if defined(USE_XSHM) && !defined(XFree86Server) + struct xmesa_pipe_winsys *xpws = (struct xmesa_pipe_winsys *) pws; +#endif + buffer->base.refcount = 1; buffer->base.alignment = alignment; buffer->base.usage = usage; buffer->base.size = size; - /* align to 16-byte multiple for Cell */ - buffer->data = align_malloc(size, max(alignment, 16)); + +#if defined(USE_XSHM) && !defined(XFree86Server) + buffer->shminfo.shmid = -1; + buffer->shminfo.shmaddr = (char *) -1; + + if (xpws->shm && (usage & PIPE_BUFFER_USAGE_PIXEL) != 0) { + buffer->shm = xpws->shm; + + if (alloc_shm(buffer, size)) { + buffer->data = buffer->shminfo.shmaddr; + } + } +#endif + + if (buffer->data == NULL) { + buffer->shm = 0; + + /* align to 16-byte multiple for Cell */ + buffer->data = align_malloc(size, max(alignment, 16)); + } return &buffer->base; } @@ -286,6 +485,7 @@ xm_user_buffer_create(struct pipe_winsys *pws, void *ptr, unsigned bytes) buffer->base.size = bytes; buffer->userBuffer = TRUE; buffer->data = ptr; + buffer->shm = 0; return &buffer->base; } @@ -377,35 +577,38 @@ xm_surface_release(struct pipe_winsys *winsys, struct pipe_surface **s) * Nothing special for the Xlib driver so no subclassing or anything. */ struct pipe_winsys * -xmesa_get_pipe_winsys_aub(void) +xmesa_get_pipe_winsys_aub(struct xmesa_visual *xm_vis) { - static struct pipe_winsys *ws = NULL; + static struct xmesa_pipe_winsys *ws = NULL; if (!ws && getenv("XM_AUB")) { - ws = xmesa_create_pipe_winsys_aub(); + ws = (struct xmesa_pipe_winsys *) xmesa_create_pipe_winsys_aub(); } else if (!ws) { - ws = CALLOC_STRUCT(pipe_winsys); - + ws = CALLOC_STRUCT(xmesa_pipe_winsys); + + ws->xm_visual = xm_vis; + ws->shm = xmesa_check_for_xshm(xm_vis->display); + /* Fill in this struct with callbacks that pipe will need to * communicate with the window system, buffer manager, etc. */ - ws->buffer_create = xm_buffer_create; - ws->user_buffer_create = xm_user_buffer_create; - ws->buffer_map = xm_buffer_map; - ws->buffer_unmap = xm_buffer_unmap; - ws->buffer_destroy = xm_buffer_destroy; - - ws->surface_alloc = xm_surface_alloc; - ws->surface_alloc_storage = xm_surface_alloc_storage; - ws->surface_release = xm_surface_release; - - ws->flush_frontbuffer = xm_flush_frontbuffer; - ws->printf = xm_printf; - ws->get_name = xm_get_name; + ws->base.buffer_create = xm_buffer_create; + ws->base.user_buffer_create = xm_user_buffer_create; + ws->base.buffer_map = xm_buffer_map; + ws->base.buffer_unmap = xm_buffer_unmap; + ws->base.buffer_destroy = xm_buffer_destroy; + + ws->base.surface_alloc = xm_surface_alloc; + ws->base.surface_alloc_storage = xm_surface_alloc_storage; + ws->base.surface_release = xm_surface_release; + + ws->base.flush_frontbuffer = xm_flush_frontbuffer; + ws->base.printf = xm_printf; + ws->base.get_name = xm_get_name; } - return ws; + return &ws->base; } @@ -445,27 +648,27 @@ xmesa_get_softpipe_winsys(uint pixelformat) struct pipe_context * xmesa_create_pipe_context(XMesaContext xmesa, uint pixelformat) { - struct pipe_winsys *pws = xmesa_get_pipe_winsys_aub(); + struct pipe_winsys *pws = xmesa_get_pipe_winsys_aub(xmesa->xm_visual); struct pipe_context *pipe; #ifdef GALLIUM_CELL if (!getenv("GALLIUM_NOCELL")) { struct cell_winsys *cws = cell_get_winsys(pixelformat); struct pipe_screen *screen = cell_create_screen(pws); + pipe = cell_create_context(screen, cws); - if (pipe) - pipe->priv = xmesa; - return pipe; } else #endif { struct softpipe_winsys *spws = xmesa_get_softpipe_winsys(pixelformat); struct pipe_screen *screen = softpipe_create_screen(pws); - pipe = softpipe_create( screen, pws, spws ); - if (pipe) - pipe->priv = xmesa; - return pipe; + pipe = softpipe_create(screen, pws, spws); } + + if (pipe) + pipe->priv = xmesa; + + return pipe; } diff --git a/src/gallium/winsys/xlib/xm_winsys_aub.h b/src/gallium/winsys/xlib/xm_winsys_aub.h index 7bee199..cc2a755 100644 --- a/src/gallium/winsys/xlib/xm_winsys_aub.h +++ b/src/gallium/winsys/xlib/xm_winsys_aub.h @@ -62,6 +62,7 @@ void xmesa_commands_aub(struct pipe_winsys *winsys, void xmesa_display_aub( /* struct pipe_winsys *winsys, */ struct pipe_surface *surface ); -struct pipe_winsys *xmesa_get_pipe_winsys_aub(void); +extern struct pipe_winsys * +xmesa_get_pipe_winsys_aub(struct xmesa_visual *xm_vis); #endif diff --git a/src/gallium/winsys/xlib/xmesaP.h b/src/gallium/winsys/xlib/xmesaP.h index fa8d1f1..9b15b2d 100644 --- a/src/gallium/winsys/xlib/xmesaP.h +++ b/src/gallium/winsys/xlib/xmesaP.h @@ -173,4 +173,7 @@ xmesa_buffer_height(XMesaBuffer b) extern void xmesa_display_surface(XMesaBuffer b, const struct pipe_surface *surf); +extern int +xmesa_check_for_xshm(XMesaDisplay *display); + #endif -- 2.7.4