Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | RSS feed

  1. /*
  2.  * Mesa 3-D graphics library
  3.  *
  4.  * Copyright (C) 1999-2006  Brian Paul   All Rights Reserved.
  5.  *
  6.  * Permission is hereby granted, free of charge, to any person obtaining a
  7.  * copy of this software and associated documentation files (the "Software"),
  8.  * to deal in the Software without restriction, including without limitation
  9.  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  10.  * and/or sell copies of the Software, and to permit persons to whom the
  11.  * Software is furnished to do so, subject to the following conditions:
  12.  *
  13.  * The above copyright notice and this permission notice shall be included
  14.  * in all copies or substantial portions of the Software.
  15.  *
  16.  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  17.  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18.  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  19.  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
  20.  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  21.  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  22.  * 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 = 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 = 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.