Home | History | Annotate | Download | only in x11
      1 /*
      2  * Mesa 3-D graphics library
      3  * Version:  6.5.2
      4  *
      5  * Copyright (C) 1999-2006  Brian Paul   All Rights Reserved.
      6  *
      7  * Permission is hereby granted, free of charge, to any person obtaining a
      8  * copy of this software and associated documentation files (the "Software"),
      9  * to deal in the Software without restriction, including without limitation
     10  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     11  * and/or sell copies of the Software, and to permit persons to whom the
     12  * Software is furnished to do so, subject to the following conditions:
     13  *
     14  * The above copyright notice and this permission notice shall be included
     15  * in all copies or substantial portions of the Software.
     16  *
     17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     18  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     20  * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
     21  * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
     22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     23  */
     24 
     25 
     26 /**
     27  * \file xm_buffer.h
     28  * Framebuffer and renderbuffer-related functions.
     29  */
     30 
     31 
     32 #include "glxheader.h"
     33 #include "xmesaP.h"
     34 #include "main/imports.h"
     35 #include "main/formats.h"
     36 #include "main/framebuffer.h"
     37 #include "main/renderbuffer.h"
     38 #include "swrast/s_renderbuffer.h"
     39 
     40 
     41 #define XMESA_RENDERBUFFER 0x1234
     42 
     43 
     44 #if defined(USE_XSHM)
     45 static volatile int mesaXErrorFlag = 0;
     46 
     47 /**
     48  * Catches potential Xlib errors.
     49  */
     50 static int
     51 mesaHandleXError(XMesaDisplay *dpy, XErrorEvent *event)
     52 {
     53    (void) dpy;
     54    (void) event;
     55    mesaXErrorFlag = 1;
     56    return 0;
     57 }
     58 
     59 /**
     60  * Allocate a shared memory XImage back buffer for the given XMesaBuffer.
     61  * Return:  GL_TRUE if success, GL_FALSE if error
     62  */
     63 static GLboolean
     64 alloc_back_shm_ximage(XMesaBuffer b, GLuint width, GLuint height)
     65 {
     66    /*
     67     * We have to do a _lot_ of error checking here to be sure we can
     68     * really use the XSHM extension.  It seems different servers trigger
     69     * errors at different points if the extension won't work.  Therefore
     70     * we have to be very careful...
     71     */
     72    GC gc;
     73    int (*old_handler)(XMesaDisplay *, XErrorEvent *);
     74 
     75    if (width == 0 || height == 0) {
     76       /* this will be true the first time we're called on 'b' */
     77       return GL_FALSE;
     78    }
     79 
     80    b->backxrb->ximage = XShmCreateImage(b->xm_visual->display,
     81                                         b->xm_visual->visinfo->visual,
     82                                         b->xm_visual->visinfo->depth,
     83                                         ZPixmap, NULL, &b->shminfo,
     84                                         width, height);
     85    if (b->backxrb->ximage == NULL) {
     86       _mesa_warning(NULL, "alloc_back_buffer: Shared memory error (XShmCreateImage), disabling.\n");
     87       b->shm = 0;
     88       return GL_FALSE;
     89    }
     90 
     91    b->shminfo.shmid = shmget(IPC_PRIVATE, b->backxrb->ximage->bytes_per_line
     92 			     * b->backxrb->ximage->height, IPC_CREAT|0777);
     93    if (b->shminfo.shmid < 0) {
     94       _mesa_warning(NULL, "shmget failed while allocating back buffer.\n");
     95       XDestroyImage(b->backxrb->ximage);
     96       b->backxrb->ximage = NULL;
     97       _mesa_warning(NULL, "alloc_back_buffer: Shared memory error (shmget), disabling.\n");
     98       b->shm = 0;
     99       return GL_FALSE;
    100    }
    101 
    102    b->shminfo.shmaddr = b->backxrb->ximage->data
    103                       = (char*)shmat(b->shminfo.shmid, 0, 0);
    104    if (b->shminfo.shmaddr == (char *) -1) {
    105       _mesa_warning(NULL, "shmat() failed while allocating back buffer.\n");
    106       XDestroyImage(b->backxrb->ximage);
    107       shmctl(b->shminfo.shmid, IPC_RMID, 0);
    108       b->backxrb->ximage = NULL;
    109       _mesa_warning(NULL, "alloc_back_buffer: Shared memory error (shmat), disabling.\n");
    110       b->shm = 0;
    111       return GL_FALSE;
    112    }
    113 
    114    b->shminfo.readOnly = False;
    115    mesaXErrorFlag = 0;
    116    old_handler = XSetErrorHandler(mesaHandleXError);
    117    /* This may trigger the X protocol error we're ready to catch: */
    118    XShmAttach(b->xm_visual->display, &b->shminfo);
    119    XSync(b->xm_visual->display, False);
    120 
    121    if (mesaXErrorFlag) {
    122       /* we are on a remote display, this error is normal, don't print it */
    123       XFlush(b->xm_visual->display);
    124       mesaXErrorFlag = 0;
    125       XDestroyImage(b->backxrb->ximage);
    126       shmdt(b->shminfo.shmaddr);
    127       shmctl(b->shminfo.shmid, IPC_RMID, 0);
    128       b->backxrb->ximage = NULL;
    129       b->shm = 0;
    130       (void) XSetErrorHandler(old_handler);
    131       return GL_FALSE;
    132    }
    133 
    134    shmctl(b->shminfo.shmid, IPC_RMID, 0); /* nobody else needs it */
    135 
    136    /* Finally, try an XShmPutImage to be really sure the extension works */
    137    gc = XCreateGC(b->xm_visual->display, b->frontxrb->drawable, 0, NULL);
    138    XShmPutImage(b->xm_visual->display, b->frontxrb->drawable, gc,
    139 		 b->backxrb->ximage, 0, 0, 0, 0, 1, 1 /*one pixel*/, False);
    140    XSync(b->xm_visual->display, False);
    141    XFreeGC(b->xm_visual->display, gc);
    142    (void) XSetErrorHandler(old_handler);
    143    if (mesaXErrorFlag) {
    144       XFlush(b->xm_visual->display);
    145       mesaXErrorFlag = 0;
    146       XDestroyImage(b->backxrb->ximage);
    147       shmdt(b->shminfo.shmaddr);
    148       shmctl(b->shminfo.shmid, IPC_RMID, 0);
    149       b->backxrb->ximage = NULL;
    150       b->shm = 0;
    151       return GL_FALSE;
    152    }
    153 
    154    return GL_TRUE;
    155 }
    156 #else
    157 static GLboolean
    158 alloc_back_shm_ximage(XMesaBuffer b, GLuint width, GLuint height)
    159 {
    160    /* Can't compile XSHM support */
    161    return GL_FALSE;
    162 }
    163 #endif
    164 
    165 
    166 
    167 /**
    168  * Setup an off-screen pixmap or Ximage to use as the back buffer.
    169  * Input:  b - the X/Mesa buffer
    170  */
    171 static void
    172 alloc_back_buffer(XMesaBuffer b, GLuint width, GLuint height)
    173 {
    174    if (b->db_mode == BACK_XIMAGE) {
    175       /* Deallocate the old backxrb->ximage, if any */
    176       if (b->backxrb->ximage) {
    177 #if defined(USE_XSHM)
    178 	 if (b->shm) {
    179 	    XShmDetach(b->xm_visual->display, &b->shminfo);
    180 	    XDestroyImage(b->backxrb->ximage);
    181 	    shmdt(b->shminfo.shmaddr);
    182 	 }
    183 	 else
    184 #endif
    185 	   XMesaDestroyImage(b->backxrb->ximage);
    186 	 b->backxrb->ximage = NULL;
    187       }
    188 
    189       if (width == 0 || height == 0)
    190          return;
    191 
    192       /* Allocate new back buffer */
    193       if (b->shm == 0 || !alloc_back_shm_ximage(b, width, height)) {
    194 	 /* Allocate a regular XImage for the back buffer. */
    195 	 b->backxrb->ximage = XCreateImage(b->xm_visual->display,
    196                                       b->xm_visual->visinfo->visual,
    197                                       GET_VISUAL_DEPTH(b->xm_visual),
    198 				      ZPixmap, 0,   /* format, offset */
    199 				      NULL,
    200                                       width, height,
    201 				      8, 0);  /* pad, bytes_per_line */
    202 	 if (!b->backxrb->ximage) {
    203 	    _mesa_warning(NULL, "alloc_back_buffer: XCreateImage failed.\n");
    204             return;
    205 	 }
    206          b->backxrb->ximage->data = (char *) MALLOC(b->backxrb->ximage->height
    207                                         * b->backxrb->ximage->bytes_per_line);
    208          if (!b->backxrb->ximage->data) {
    209             _mesa_warning(NULL, "alloc_back_buffer: MALLOC failed.\n");
    210             XMesaDestroyImage(b->backxrb->ximage);
    211             b->backxrb->ximage = NULL;
    212          }
    213       }
    214       b->backxrb->pixmap = None;
    215    }
    216    else if (b->db_mode == BACK_PIXMAP) {
    217       /* Free the old back pixmap */
    218       if (b->backxrb->pixmap) {
    219          XMesaFreePixmap(b->xm_visual->display, b->backxrb->pixmap);
    220          b->backxrb->pixmap = 0;
    221       }
    222 
    223       if (width > 0 && height > 0) {
    224          /* Allocate new back pixmap */
    225          b->backxrb->pixmap = XMesaCreatePixmap(b->xm_visual->display,
    226                                                 b->frontxrb->drawable,
    227                                                 width, height,
    228                                                 GET_VISUAL_DEPTH(b->xm_visual));
    229       }
    230 
    231       b->backxrb->ximage = NULL;
    232       b->backxrb->drawable = b->backxrb->pixmap;
    233    }
    234 }
    235 
    236 
    237 static void
    238 xmesa_delete_renderbuffer(struct gl_context *ctx, struct gl_renderbuffer *rb)
    239 {
    240    /* XXX Note: the ximage or Pixmap attached to this renderbuffer
    241     * should probably get freed here, but that's currently done in
    242     * XMesaDestroyBuffer().
    243     */
    244    free(rb);
    245 }
    246 
    247 
    248 /**
    249  * Reallocate renderbuffer storage for front color buffer.
    250  * Called via gl_renderbuffer::AllocStorage()
    251  */
    252 static GLboolean
    253 xmesa_alloc_front_storage(struct gl_context *ctx, struct gl_renderbuffer *rb,
    254                           GLenum internalFormat, GLuint width, GLuint height)
    255 {
    256    struct xmesa_renderbuffer *xrb = xmesa_renderbuffer(rb);
    257 
    258    /* just clear these to be sure we don't accidentally use them */
    259    xrb->origin2 = NULL;
    260    xrb->origin3 = NULL;
    261    xrb->origin4 = NULL;
    262 
    263    /* for the FLIP macro: */
    264    xrb->bottom = height - 1;
    265 
    266    rb->Width = width;
    267    rb->Height = height;
    268    rb->InternalFormat = internalFormat;
    269 
    270    return GL_TRUE;
    271 }
    272 
    273 
    274 /**
    275  * Reallocate renderbuffer storage for back color buffer.
    276  * Called via gl_renderbuffer::AllocStorage()
    277  */
    278 static GLboolean
    279 xmesa_alloc_back_storage(struct gl_context *ctx, struct gl_renderbuffer *rb,
    280                          GLenum internalFormat, GLuint width, GLuint height)
    281 {
    282    struct xmesa_renderbuffer *xrb = xmesa_renderbuffer(rb);
    283 
    284    /* reallocate the back buffer XImage or Pixmap */
    285    assert(xrb->Parent);
    286    alloc_back_buffer(xrb->Parent, width, height);
    287 
    288    /* same as front buffer */
    289    /* XXX why is this here? */
    290    (void) xmesa_alloc_front_storage(ctx, rb, internalFormat, width, height);
    291 
    292    /* plus... */
    293    if (xrb->ximage) {
    294       /* Needed by PIXELADDR2 macro */
    295       xrb->width2 = xrb->ximage->bytes_per_line / 2;
    296       xrb->origin2 = (GLushort *) xrb->ximage->data + xrb->width2 * (height - 1);
    297 
    298       /* Needed by PIXELADDR3 macro */
    299       xrb->width3 = xrb->ximage->bytes_per_line;
    300       xrb->origin3 = (GLubyte *) xrb->ximage->data + xrb->width3 * (height - 1);
    301 
    302       /* Needed by PIXELADDR4 macro */
    303       xrb->width4 = xrb->ximage->width;
    304       xrb->origin4 = (GLuint *) xrb->ximage->data + xrb->width4 * (height - 1);
    305    }
    306    else {
    307       /* out of memory or buffer size is 0 x 0 */
    308       xrb->width2 = xrb->width3 = xrb->width4 = 0;
    309       xrb->origin2 = NULL;
    310       xrb->origin3 = NULL;
    311       xrb->origin4 = NULL;
    312    }
    313 
    314    return GL_TRUE;
    315 }
    316 
    317 
    318 /**
    319  * Used for allocating front/back renderbuffers for an X window.
    320  */
    321 struct xmesa_renderbuffer *
    322 xmesa_new_renderbuffer(struct gl_context *ctx, GLuint name,
    323                        const struct xmesa_visual *xmvis,
    324                        GLboolean backBuffer)
    325 {
    326    struct xmesa_renderbuffer *xrb = CALLOC_STRUCT(xmesa_renderbuffer);
    327    if (xrb) {
    328       GLuint name = 0;
    329       _mesa_init_renderbuffer(&xrb->Base.Base, name);
    330 
    331       xrb->Base.Base.Delete = xmesa_delete_renderbuffer;
    332       if (backBuffer)
    333          xrb->Base.Base.AllocStorage = xmesa_alloc_back_storage;
    334       else
    335          xrb->Base.Base.AllocStorage = xmesa_alloc_front_storage;
    336 
    337       xrb->Base.Base.InternalFormat = GL_RGBA;
    338       xrb->Base.Base._BaseFormat = GL_RGBA;
    339       xrb->Base.Base.ClassID = XMESA_RENDERBUFFER;
    340 
    341       switch (xmvis->undithered_pf) {
    342       case PF_8R8G8B:
    343          /* This will really only happen for pixmaps.  We'll access the
    344           * pixmap via a temporary XImage which will be 32bpp.
    345           */
    346          xrb->Base.Base.Format = MESA_FORMAT_XRGB8888;
    347          break;
    348       case PF_8A8R8G8B:
    349          xrb->Base.Base.Format = MESA_FORMAT_ARGB8888;
    350          break;
    351       case PF_8A8B8G8R:
    352          xrb->Base.Base.Format = MESA_FORMAT_RGBA8888_REV;
    353          break;
    354       case PF_5R6G5B:
    355          xrb->Base.Base.Format = MESA_FORMAT_RGB565;
    356          break;
    357       default:
    358          _mesa_warning(ctx, "Bad pixel format in xmesa_new_renderbuffer");
    359          xrb->Base.Base.Format = MESA_FORMAT_ARGB8888;
    360          break;
    361       }
    362 
    363       /* only need to set Red/Green/EtcBits fields for user-created RBs */
    364    }
    365    return xrb;
    366 }
    367 
    368 
    369 /**
    370  * Called via gl_framebuffer::Delete() method when this buffer
    371  * is _really_ being deleted.
    372  */
    373 void
    374 xmesa_delete_framebuffer(struct gl_framebuffer *fb)
    375 {
    376    XMesaBuffer b = XMESA_BUFFER(fb);
    377 
    378    if (b->num_alloced > 0) {
    379       /* If no other buffer uses this X colormap then free the colors. */
    380       if (!xmesa_find_buffer(b->display, b->cmap, b)) {
    381          XFreeColors(b->display, b->cmap,
    382                      b->alloced_colors, b->num_alloced, 0);
    383       }
    384    }
    385 
    386    if (b->gc)
    387       XMesaFreeGC(b->display, b->gc);
    388    if (b->cleargc)
    389       XMesaFreeGC(b->display, b->cleargc);
    390    if (b->swapgc)
    391       XMesaFreeGC(b->display, b->swapgc);
    392 
    393    if (fb->Visual.doubleBufferMode) {
    394       /* free back ximage/pixmap/shmregion */
    395       if (b->backxrb->ximage) {
    396 #if defined(USE_XSHM)
    397          if (b->shm) {
    398             XShmDetach( b->display, &b->shminfo );
    399             XDestroyImage( b->backxrb->ximage );
    400             shmdt( b->shminfo.shmaddr );
    401          }
    402          else
    403 #endif
    404             XMesaDestroyImage( b->backxrb->ximage );
    405          b->backxrb->ximage = NULL;
    406       }
    407       if (b->backxrb->pixmap) {
    408          XMesaFreePixmap( b->display, b->backxrb->pixmap );
    409       }
    410    }
    411 
    412    _mesa_free_framebuffer_data(fb);
    413    free(fb);
    414 }
    415 
    416 
    417 /**
    418  * Called via ctx->Driver.MapRenderbuffer()
    419  */
    420 void
    421 xmesa_MapRenderbuffer(struct gl_context *ctx,
    422                       struct gl_renderbuffer *rb,
    423                       GLuint x, GLuint y, GLuint w, GLuint h,
    424                       GLbitfield mode,
    425                       GLubyte **mapOut, GLint *rowStrideOut)
    426 {
    427    struct xmesa_renderbuffer *xrb = xmesa_renderbuffer(rb);
    428 
    429    if (xrb->Base.Base.ClassID == XMESA_RENDERBUFFER) {
    430       XImage *ximage = xrb->ximage;
    431 
    432       assert(!xrb->map_mode); /* only a single mapping allowed */
    433 
    434       xrb->map_mode = mode;
    435       xrb->map_x = x;
    436       xrb->map_y = y;
    437       xrb->map_w = w;
    438       xrb->map_h = h;
    439 
    440       if (ximage) {
    441          int y2 = rb->Height - y - 1;
    442 
    443          *mapOut = (GLubyte *) ximage->data
    444             + y2 * ximage->bytes_per_line
    445             + x * ximage->bits_per_pixel / 8;
    446       }
    447       else {
    448          /* this must be a pixmap/window renderbuffer */
    449          int (*old_handler)(XMesaDisplay *, XErrorEvent *);
    450          int y2 = rb->Height - y - h;
    451 
    452          assert(xrb->pixmap);
    453 
    454          /* Install error handler for XGetImage() in case the the window
    455           * isn't mapped.  If we fail we'll create a temporary XImage.
    456           */
    457          mesaXErrorFlag = 0;
    458          old_handler = XSetErrorHandler(mesaHandleXError);
    459 
    460          /* read pixel data out of the pixmap/window into an XImage */
    461          ximage = XGetImage(xrb->Parent->display,
    462                             xrb->pixmap, x, y2, w, h,
    463                             AllPlanes, ZPixmap);
    464 
    465          XSetErrorHandler(old_handler);
    466 
    467          if (mesaXErrorFlag) {
    468             /* create new, temporary XImage */
    469             int bytes_per_line =
    470                _mesa_format_row_stride(xrb->Base.Base.Format,
    471                                        xrb->Base.Base.Width);
    472             char *image = (char *) malloc(bytes_per_line *
    473                                           xrb->Base.Base.Height);
    474             ximage = XCreateImage(xrb->Parent->display,
    475                                   xrb->Parent->xm_visual->visinfo->visual,
    476                                   xrb->Parent->xm_visual->visinfo->depth,
    477                                   ZPixmap, /* format */
    478                                   0, /* offset */
    479                                   image, /* data */
    480                                   xrb->Base.Base.Width,
    481                                   xrb->Base.Base.Height,
    482                                   8, /* pad */
    483                                   bytes_per_line);
    484          }
    485 
    486          if (!ximage) {
    487             *mapOut = NULL;
    488             *rowStrideOut = 0;
    489             return;
    490          }
    491 
    492          xrb->map_ximage = ximage;
    493 
    494          /* the first row of the OpenGL image is last row of the XImage */
    495          *mapOut = (GLubyte *) ximage->data
    496             + (h - 1) * ximage->bytes_per_line;
    497       }
    498 
    499       /* We return a negative stride here since XImage data is upside down
    500        * with respect to OpenGL images.
    501        */
    502       *rowStrideOut = -ximage->bytes_per_line;
    503       return;
    504    }
    505 
    506    /* otherwise, this is an ordinary malloc-based renderbuffer */
    507    _swrast_map_soft_renderbuffer(ctx, rb, x, y, w, h, mode,
    508                                  mapOut, rowStrideOut);
    509 }
    510 
    511 
    512 /**
    513  * Called via ctx->Driver.UnmapRenderbuffer()
    514  */
    515 void
    516 xmesa_UnmapRenderbuffer(struct gl_context *ctx, struct gl_renderbuffer *rb)
    517 {
    518    struct xmesa_renderbuffer *xrb = xmesa_renderbuffer(rb);
    519 
    520    if (xrb->Base.Base.ClassID == XMESA_RENDERBUFFER) {
    521       XImage *ximage = xrb->ximage;
    522 
    523       if (!ximage) {
    524          /* this must be a pixmap/window renderbuffer */
    525          assert(xrb->pixmap);
    526          assert(xrb->map_ximage);
    527          if (xrb->map_ximage) {
    528             if (xrb->map_mode & GL_MAP_WRITE_BIT) {
    529                /* put modified ximage data back into the pixmap/window */
    530                int y2 = rb->Height - xrb->map_y - xrb->map_h;
    531                GC gc = XCreateGC(xrb->Parent->display, xrb->pixmap, 0, NULL);
    532 
    533                XPutImage(xrb->Parent->display,
    534                          xrb->pixmap,              /* dest */
    535                          gc,
    536                          xrb->map_ximage,          /* source */
    537                          0, 0,                     /* src x, y */
    538                          xrb->map_x, y2,           /* dest x, y */
    539                          xrb->map_w, xrb->map_h);  /* size */
    540 
    541                XFreeGC(xrb->Parent->display, gc);
    542             }
    543             XMesaDestroyImage(xrb->map_ximage);
    544             xrb->map_ximage = NULL;
    545          }
    546       }
    547 
    548       xrb->map_mode = 0x0;
    549 
    550       return;
    551    }
    552 
    553    /* otherwise, this is an ordinary malloc-based renderbuffer */
    554    _swrast_unmap_soft_renderbuffer(ctx, rb);
    555 }
    556 
    557 
    558