diff options
Diffstat (limited to 'src/mesa/pipe/xlib/xm_buffer.c')
-rw-r--r-- | src/mesa/pipe/xlib/xm_buffer.c | 503 |
1 files changed, 503 insertions, 0 deletions
diff --git a/src/mesa/pipe/xlib/xm_buffer.c b/src/mesa/pipe/xlib/xm_buffer.c new file mode 100644 index 0000000000..09356c7329 --- /dev/null +++ b/src/mesa/pipe/xlib/xm_buffer.c @@ -0,0 +1,503 @@ +/* + * Mesa 3-D graphics library + * Version: 6.5.2 + * + * Copyright (C) 1999-2006 Brian Paul All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +/** + * \file xm_buffer.h + * Framebuffer and renderbuffer-related functions. + */ + + +#include "glxheader.h" +#include "GL/xmesa.h" +#include "xmesaP.h" +#include "imports.h" +#include "framebuffer.h" +#include "renderbuffer.h" +#include "pipe/p_state.h" +#include "pipe/p_defines.h" +#include "pipe/p_winsys.h" +#include "state_tracker/st_context.h" + + +#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; +} + +/** + * Allocate a shared memory XImage back buffer for the given XMesaBuffer. + * Return: GL_TRUE if success, GL_FALSE if error + */ +static GLboolean +alloc_back_shm_ximage(XMesaBuffer b, GLuint width, GLuint 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... + */ + GC gc; + int (*old_handler)(XMesaDisplay *, XErrorEvent *); + + if (width == 0 || height == 0) { + /* this will be true the first time we're called on 'b' */ + return GL_FALSE; + } + + b->backxrb->ximage = XShmCreateImage(b->xm_visual->display, + b->xm_visual->visinfo->visual, + b->xm_visual->visinfo->depth, + ZPixmap, NULL, &b->shminfo, + width, height); + if (b->backxrb->ximage == NULL) { + _mesa_warning(NULL, "alloc_back_buffer: Shared memory error (XShmCreateImage), disabling.\n"); + b->shm = 0; + return GL_FALSE; + } + + b->shminfo.shmid = shmget(IPC_PRIVATE, b->backxrb->ximage->bytes_per_line + * b->backxrb->ximage->height, IPC_CREAT|0777); + if (b->shminfo.shmid < 0) { + _mesa_warning(NULL, "shmget failed while allocating back buffer.\n"); + XDestroyImage(b->backxrb->ximage); + b->backxrb->ximage = NULL; + _mesa_warning(NULL, "alloc_back_buffer: Shared memory error (shmget), disabling.\n"); + b->shm = 0; + return GL_FALSE; + } + + b->shminfo.shmaddr = b->backxrb->ximage->data + = (char*)shmat(b->shminfo.shmid, 0, 0); + if (b->shminfo.shmaddr == (char *) -1) { + _mesa_warning(NULL, "shmat() failed while allocating back buffer.\n"); + XDestroyImage(b->backxrb->ximage); + shmctl(b->shminfo.shmid, IPC_RMID, 0); + b->backxrb->ximage = NULL; + _mesa_warning(NULL, "alloc_back_buffer: Shared memory error (shmat), disabling.\n"); + b->shm = 0; + return GL_FALSE; + } + + b->shminfo.readOnly = False; + mesaXErrorFlag = 0; + old_handler = XSetErrorHandler(mesaHandleXError); + /* This may trigger the X protocol error we're ready to catch: */ + XShmAttach(b->xm_visual->display, &b->shminfo); + XSync(b->xm_visual->display, False); + + if (mesaXErrorFlag) { + /* we are on a remote display, this error is normal, don't print it */ + XFlush(b->xm_visual->display); + mesaXErrorFlag = 0; + XDestroyImage(b->backxrb->ximage); + shmdt(b->shminfo.shmaddr); + shmctl(b->shminfo.shmid, IPC_RMID, 0); + b->backxrb->ximage = NULL; + b->shm = 0; + (void) XSetErrorHandler(old_handler); + return GL_FALSE; + } + + shmctl(b->shminfo.shmid, IPC_RMID, 0); /* nobody else needs it */ + + /* Finally, try an XShmPutImage to be really sure the extension works */ + gc = XCreateGC(b->xm_visual->display, b->frontxrb->drawable, 0, NULL); + XShmPutImage(b->xm_visual->display, b->frontxrb->drawable, gc, + b->backxrb->ximage, 0, 0, 0, 0, 1, 1 /*one pixel*/, False); + XSync(b->xm_visual->display, False); + XFreeGC(b->xm_visual->display, gc); + (void) XSetErrorHandler(old_handler); + if (mesaXErrorFlag) { + XFlush(b->xm_visual->display); + mesaXErrorFlag = 0; + XDestroyImage(b->backxrb->ximage); + shmdt(b->shminfo.shmaddr); + shmctl(b->shminfo.shmid, IPC_RMID, 0); + b->backxrb->ximage = NULL; + b->shm = 0; + return GL_FALSE; + } + + return GL_TRUE; +} +#else +static GLboolean +alloc_back_shm_ximage(XMesaBuffer b, GLuint width, GLuint height) +{ + /* Can't compile XSHM support */ + return GL_FALSE; +} +#endif + + + +/** + * Setup an off-screen pixmap or Ximage to use as the back buffer. + * Input: b - the X/Mesa buffer + */ +static void +alloc_back_buffer(XMesaBuffer b, GLuint width, GLuint height) +{ + if (b->db_mode == BACK_XIMAGE) { + /* Deallocate the old backxrb->ximage, if any */ + if (b->backxrb->ximage) { +#if defined(USE_XSHM) && !defined(XFree86Server) + if (b->shm) { + XShmDetach(b->xm_visual->display, &b->shminfo); + XDestroyImage(b->backxrb->ximage); + shmdt(b->shminfo.shmaddr); + } + else +#endif + XMesaDestroyImage(b->backxrb->ximage); + b->backxrb->ximage = NULL; + } + + if (width == 0 || height == 0) + return; + + /* Allocate new back buffer */ + if (b->shm == 0 || !alloc_back_shm_ximage(b, width, height)) { + /* Allocate a regular XImage for the back buffer. */ +#ifdef XFree86Server + b->backxrb->ximage = XMesaCreateImage(b->xm_visual->BitsPerPixel, + width, height, NULL); +#else + b->backxrb->ximage = XCreateImage(b->xm_visual->display, + b->xm_visual->visinfo->visual, + GET_VISUAL_DEPTH(b->xm_visual), + ZPixmap, 0, /* format, offset */ + NULL, + width, height, + 8, 0); /* pad, bytes_per_line */ +#endif + if (!b->backxrb->ximage) { + _mesa_warning(NULL, "alloc_back_buffer: XCreateImage failed.\n"); + return; + } + b->backxrb->ximage->data = (char *) MALLOC(b->backxrb->ximage->height + * b->backxrb->ximage->bytes_per_line); + if (!b->backxrb->ximage->data) { + _mesa_warning(NULL, "alloc_back_buffer: MALLOC failed.\n"); + XMesaDestroyImage(b->backxrb->ximage); + b->backxrb->ximage = NULL; + } + } + b->backxrb->pixmap = None; + } + else if (b->db_mode == BACK_PIXMAP) { + /* Free the old back pixmap */ + if (b->backxrb->pixmap) { + XMesaFreePixmap(b->xm_visual->display, b->backxrb->pixmap); + b->backxrb->pixmap = 0; + } + + if (width > 0 && height > 0) { + /* Allocate new back pixmap */ + b->backxrb->pixmap = XMesaCreatePixmap(b->xm_visual->display, + b->frontxrb->drawable, + width, height, + GET_VISUAL_DEPTH(b->xm_visual)); + } + + b->backxrb->ximage = NULL; + } +} + + +static void +xmesa_delete_renderbuffer(struct gl_renderbuffer *rb) +{ + /* XXX Note: the ximage or Pixmap attached to this renderbuffer + * should probably get freed here, but that's currently done in + * XMesaDestroyBuffer(). + */ + _mesa_free(rb); +} + + +static void +finish_surface_init(GLcontext *ctx, struct xmesa_renderbuffer *xrb) +{ + struct pipe_context *pipe = ctx->st->pipe; + if (!xrb->St.surface->region) { + int w = 1, h = 1; + xrb->St.surface->region = pipe->winsys->region_alloc(pipe->winsys, + 1, w, h, 0x0); + } +} + + +/** + * Reallocate renderbuffer storage for front color buffer. + * Called via gl_renderbuffer::AllocStorage() + */ +static GLboolean +xmesa_alloc_front_storage(GLcontext *ctx, struct gl_renderbuffer *rb, + GLenum internalFormat, GLuint width, GLuint height) +{ + struct xmesa_renderbuffer *xrb = xmesa_renderbuffer(rb); + + /* just clear these to be sure we don't accidentally use them */ + xrb->origin1 = NULL; + xrb->origin2 = NULL; + xrb->origin3 = NULL; + xrb->origin4 = NULL; + + /* for the FLIP macro: */ + xrb->bottom = height - 1; + + rb->Width = width; + rb->Height = height; + rb->InternalFormat = internalFormat; + + if (!xrb->St.surface || !xrb->St.surface->region) + finish_surface_init(ctx, xrb); + + xrb->St.surface->width = width; + xrb->St.surface->height = height; + + return GL_TRUE; +} + + +/** + * Reallocate renderbuffer storage for back color buffer. + * Called via gl_renderbuffer::AllocStorage() + */ +static GLboolean +xmesa_alloc_back_storage(GLcontext *ctx, struct gl_renderbuffer *rb, + GLenum internalFormat, GLuint width, GLuint height) +{ + struct xmesa_renderbuffer *xrb = xmesa_renderbuffer(rb); + + /* reallocate the back buffer XImage or Pixmap */ + assert(xrb->Parent); + alloc_back_buffer(xrb->Parent, width, height); + + /* same as front buffer */ + /* XXX why is this here? */ + (void) xmesa_alloc_front_storage(ctx, rb, internalFormat, width, height); + + /* plus... */ + if (xrb->ximage) { + /* Needed by PIXELADDR1 macro */ + xrb->width1 = xrb->ximage->bytes_per_line; + xrb->origin1 = (GLubyte *) xrb->ximage->data + xrb->width1 * (height - 1); + + /* Needed by PIXELADDR2 macro */ + xrb->width2 = xrb->ximage->bytes_per_line / 2; + xrb->origin2 = (GLushort *) xrb->ximage->data + xrb->width2 * (height - 1); + + /* Needed by PIXELADDR3 macro */ + xrb->width3 = xrb->ximage->bytes_per_line; + xrb->origin3 = (GLubyte *) xrb->ximage->data + xrb->width3 * (height - 1); + + /* Needed by PIXELADDR4 macro */ + xrb->width4 = xrb->ximage->width; + xrb->origin4 = (GLuint *) xrb->ximage->data + xrb->width4 * (height - 1); + } + else { + /* out of memory or buffer size is 0 x 0 */ + xrb->width1 = xrb->width2 = xrb->width3 = xrb->width4 = 0; + xrb->origin1 = NULL; + xrb->origin2 = NULL; + xrb->origin3 = NULL; + xrb->origin4 = NULL; + } + + if (!xrb->St.surface || !xrb->St.surface->region) + finish_surface_init(ctx, xrb); + + xrb->St.surface->width = width; + xrb->St.surface->height = height; + + return GL_TRUE; +} + + +/** + * Called to create the front/back color renderbuffers, not user-created + * renderbuffers. + */ +struct xmesa_renderbuffer * +xmesa_create_renderbuffer(GLcontext *ctx, GLuint name, const GLvisual *visual, + GLboolean backBuffer) +{ + struct xmesa_renderbuffer *xrb = CALLOC_STRUCT(xmesa_renderbuffer); + struct pipe_context *pipe = NULL;/*ctx->st->pipe;*/ + if (xrb) { + GLuint name = 0; + GLuint pipeFormat = 0; + struct xmesa_surface *xms; + + _mesa_init_renderbuffer(&xrb->St.Base, name); + + xrb->St.Base.Delete = xmesa_delete_renderbuffer; + if (backBuffer) + xrb->St.Base.AllocStorage = xmesa_alloc_back_storage; + else + xrb->St.Base.AllocStorage = xmesa_alloc_front_storage; + + if (visual->rgbMode) { + xrb->St.Base.InternalFormat = GL_RGBA; + xrb->St.Base._BaseFormat = GL_RGBA; + xrb->St.Base.DataType = GL_UNSIGNED_BYTE; + xrb->St.Base.RedBits = visual->redBits; + xrb->St.Base.GreenBits = visual->greenBits; + xrb->St.Base.BlueBits = visual->blueBits; + xrb->St.Base.AlphaBits = visual->alphaBits; + pipeFormat = PIPE_FORMAT_U_A8_R8_G8_B8; + } + else { + xrb->St.Base.InternalFormat = GL_COLOR_INDEX; + xrb->St.Base._BaseFormat = GL_COLOR_INDEX; + xrb->St.Base.DataType = GL_UNSIGNED_INT; + xrb->St.Base.IndexBits = visual->indexBits; + } + /* only need to set Red/Green/EtcBits fields for user-created RBs */ + + xrb->St.surface = xmesa_new_color_surface(pipe, pipeFormat); + xms = (struct xmesa_surface *) xrb->St.surface; + xms->xrb = xrb; + } + return xrb; +} + + +#if 0 +struct gl_renderbuffer * +xmesa_new_renderbuffer(GLcontext *ctx, struct gl_renderbuffer *rb, + GLenum internalFormat, GLuint width, GLuint height) +{ + struct xmesa_renderbuffer *xrb = CALLOC_STRUCT(xmesa_renderbuffer); + if (xrb) { + GLuint name = 0; + _mesa_init_renderbuffer(&xrb->St.Base, name); + + xrb->St.Base.Delete = xmesa_delete_renderbuffer; + if (backBuffer) + xrb->St.Base.AllocStorage = xmesa_alloc_back_storage; + else + xrb->St.Base.AllocStorage = xmesa_alloc_front_storage; + + if (visual->rgbMode) { + xrb->St.Base.InternalFormat = GL_RGBA; + xrb->St.Base._BaseFormat = GL_RGBA; + xrb->St.Base.DataType = GL_UNSIGNED_BYTE; + xrb->St.Base.RedBits = visual->redBits; + xrb->St.Base.GreenBits = visual->greenBits; + xrb->St.Base.BlueBits = visual->blueBits; + xrb->St.Base.AlphaBits = visual->alphaBits; + } + else { + xrb->St.Base.InternalFormat = GL_COLOR_INDEX; + xrb->St.Base._BaseFormat = GL_COLOR_INDEX; + xrb->St.Base.DataType = GL_UNSIGNED_INT; + xrb->St.Base.IndexBits = visual->indexBits; + } + /* only need to set Red/Green/EtcBits fields for user-created RBs */ + } + return xrb; +} +#endif + + +/** + * Called via gl_framebuffer::Delete() method when this buffer + * is _really_ being deleted. + */ +void +xmesa_delete_framebuffer(struct gl_framebuffer *fb) +{ + XMesaBuffer b = XMESA_BUFFER(fb); + + if (b->num_alloced > 0) { + /* If no other buffer uses this X colormap then free the colors. */ + if (!xmesa_find_buffer(b->display, b->cmap, b)) { +#ifdef XFree86Server + int client = 0; + if (b->frontxrb->drawable) + client = CLIENT_ID(b->frontxrb->drawable->id); + (void)FreeColors(b->cmap, client, + b->num_alloced, b->alloced_colors, 0); +#else + XFreeColors(b->display, b->cmap, + b->alloced_colors, b->num_alloced, 0); +#endif + } + } + + if (b->gc) + XMesaFreeGC(b->display, b->gc); + if (b->cleargc) + XMesaFreeGC(b->display, b->cleargc); + if (b->swapgc) + XMesaFreeGC(b->display, b->swapgc); + + if (fb->Visual.doubleBufferMode) { + /* free back ximage/pixmap/shmregion */ + if (b->backxrb->ximage) { +#if defined(USE_XSHM) && !defined(XFree86Server) + if (b->shm) { + XShmDetach( b->display, &b->shminfo ); + XDestroyImage( b->backxrb->ximage ); + shmdt( b->shminfo.shmaddr ); + } + else +#endif + XMesaDestroyImage( b->backxrb->ximage ); + b->backxrb->ximage = NULL; + } + if (b->backxrb->pixmap) { + XMesaFreePixmap( b->display, b->backxrb->pixmap ); + if (b->xm_visual->hpcr_clear_flag) { + XMesaFreePixmap( b->display, + b->xm_visual->hpcr_clear_pixmap ); + XMesaDestroyImage( b->xm_visual->hpcr_clear_ximage ); + } + } + } + + if (b->rowimage) { + _mesa_free( b->rowimage->data ); + b->rowimage->data = NULL; + XMesaDestroyImage( b->rowimage ); + } + + _mesa_free_framebuffer_data(fb); + _mesa_free(fb); +} |