xlib: implement renderbuffer mapping/unmapping
authorBrian Paul <brianp@vmware.com>
Sat, 5 Nov 2011 16:48:18 +0000 (10:48 -0600)
committerBrian Paul <brianp@vmware.com>
Mon, 7 Nov 2011 21:25:47 +0000 (14:25 -0700)
This fixes the glReadPixels() regression for reading from the front/back
color buffers.

Note, we only allow one mapping of an XImage/Pixmap renderbuffer
at any time.  That might need to be revisited in the future.

src/mesa/drivers/x11/xm_api.c
src/mesa/drivers/x11/xm_buffer.c
src/mesa/drivers/x11/xm_dd.c
src/mesa/drivers/x11/xmesaP.h

index 5f04163fac399a258d399257a02e51a2eb34a9b9..13b35c790c3a51af8b93e81c395e00993f5c3c7d 100644 (file)
@@ -361,7 +361,7 @@ create_xmesa_buffer(XMesaDrawable d, BufferType type,
    /*
     * Front renderbuffer
     */
-   b->frontxrb = xmesa_new_renderbuffer(NULL, 0, &vis->mesa_visual, GL_FALSE);
+   b->frontxrb = xmesa_new_renderbuffer(NULL, 0, vis, GL_FALSE);
    if (!b->frontxrb) {
       free(b);
       return NULL;
@@ -376,7 +376,7 @@ create_xmesa_buffer(XMesaDrawable d, BufferType type,
     * Back renderbuffer
     */
    if (vis->mesa_visual.doubleBufferMode) {
-      b->backxrb = xmesa_new_renderbuffer(NULL, 0, &vis->mesa_visual, GL_TRUE);
+      b->backxrb = xmesa_new_renderbuffer(NULL, 0, vis, GL_TRUE);
       if (!b->backxrb) {
          /* XXX free front xrb too */
          free(b);
index 10829b4284fd714f3f4f6d6db00bbc51fdb97a30..6cf9f068f2233c5c9012fefbaad63304fb44b1af 100644 (file)
@@ -37,6 +37,9 @@
 #include "main/renderbuffer.h"
 
 
+#define XMESA_RENDERBUFFER 0x1234
+
+
 #if defined(USE_XSHM) 
 static volatile int mesaXErrorFlag = 0;
 
@@ -317,8 +320,12 @@ xmesa_alloc_back_storage(struct gl_context *ctx, struct gl_renderbuffer *rb,
 }
 
 
+/**
+ * Used for allocating front/back renderbuffers for an X window.
+ */
 struct xmesa_renderbuffer *
-xmesa_new_renderbuffer(struct gl_context *ctx, GLuint name, const struct gl_config *visual,
+xmesa_new_renderbuffer(struct gl_context *ctx, GLuint name,
+                       const struct xmesa_visual *xmvis,
                        GLboolean backBuffer)
 {
    struct xmesa_renderbuffer *xrb = CALLOC_STRUCT(xmesa_renderbuffer);
@@ -333,9 +340,32 @@ xmesa_new_renderbuffer(struct gl_context *ctx, GLuint name, const struct gl_conf
          xrb->Base.AllocStorage = xmesa_alloc_front_storage;
 
       xrb->Base.InternalFormat = GL_RGBA;
-      xrb->Base.Format = MESA_FORMAT_RGBA8888;
       xrb->Base._BaseFormat = GL_RGBA;
       xrb->Base.DataType = GL_UNSIGNED_BYTE;
+      xrb->Base.ClassID = XMESA_RENDERBUFFER;
+
+      switch (xmvis->undithered_pf) {
+      case PF_8R8G8B:
+         /* This will really only happen for pixmaps.  We'll access the
+          * pixmap via a temporary XImage which will be 32bpp.
+          */
+         xrb->Base.Format = MESA_FORMAT_ARGB8888;
+         break;
+      case PF_8A8R8G8B:
+         xrb->Base.Format = MESA_FORMAT_ARGB8888;
+         break;
+      case PF_8A8B8G8R:
+         xrb->Base.Format = MESA_FORMAT_RGBA8888_REV;
+         break;
+      case PF_5R6G5B:
+         xrb->Base.Format = MESA_FORMAT_RGB565;
+         break;
+      default:
+         _mesa_warning(ctx, "Bad pixel format in xmesa_new_renderbuffer");
+         xrb->Base.Format = MESA_FORMAT_ARGB8888;
+         break;
+      }
+
       /* only need to set Red/Green/EtcBits fields for user-created RBs */
    }
    return xrb;
@@ -399,3 +429,117 @@ xmesa_delete_framebuffer(struct gl_framebuffer *fb)
    _mesa_free_framebuffer_data(fb);
    free(fb);
 }
+
+
+/**
+ * Called via ctx->Driver.MapRenderbuffer()
+ */
+void
+xmesa_MapRenderbuffer(struct gl_context *ctx,
+                      struct gl_renderbuffer *rb,
+                      GLuint x, GLuint y, GLuint w, GLuint h,
+                      GLbitfield mode,
+                      GLubyte **mapOut, GLint *rowStrideOut)
+{
+   struct xmesa_renderbuffer *xrb = xmesa_renderbuffer(rb);
+
+   if (xrb->Base.ClassID == XMESA_RENDERBUFFER) {
+      XImage *ximage = xrb->ximage;
+
+      assert(!xrb->map_mode); /* only a single mapping allowed */
+
+      xrb->map_mode = mode;
+      xrb->map_x = x;
+      xrb->map_y = y;
+      xrb->map_w = w;
+      xrb->map_h = h;
+
+      if (ximage) {
+         int y2 = rb->Height - y - 1;
+
+         *mapOut = (GLubyte *) ximage->data
+            + y2 * ximage->bytes_per_line
+            + x * ximage->bits_per_pixel / 8;
+      }
+      else {
+         /* this must be a pixmap/window renderbuffer */
+         int y2 = rb->Height - y - h;
+
+         assert(xrb->pixmap);
+
+         /* read pixel data out of the pixmap/window into an XImage */
+         ximage = XGetImage(xrb->Parent->display,
+                            xrb->pixmap, x, y2, w, h,
+                            AllPlanes, ZPixmap);
+         if (!ximage) {
+            *mapOut = NULL;
+            *rowStrideOut = 0;
+            return;
+         }
+
+         xrb->map_ximage = ximage;
+
+         /* the first row of the OpenGL image is last row of the XImage */
+         *mapOut = (GLubyte *) ximage->data
+            + (h - 1) * ximage->bytes_per_line;
+      }
+
+      /* We return a negative stride here since XImage data is upside down
+       * with respect to OpenGL images.
+       */
+      *rowStrideOut = -ximage->bytes_per_line;
+      return;
+   }
+
+   /* otherwise, this is an ordinary malloc-based renderbuffer */
+   _mesa_map_soft_renderbuffer(ctx, rb, x, y, w, h, mode,
+                               mapOut, rowStrideOut);
+}
+
+
+/**
+ * Called via ctx->Driver.UnmapRenderbuffer()
+ */
+void
+xmesa_UnmapRenderbuffer(struct gl_context *ctx, struct gl_renderbuffer *rb)
+{
+   struct xmesa_renderbuffer *xrb = xmesa_renderbuffer(rb);
+
+   if (xrb->Base.ClassID == XMESA_RENDERBUFFER) {
+      XImage *ximage = xrb->ximage;
+
+      if (!ximage) {
+         /* this must be a pixmap/window renderbuffer */
+         assert(xrb->pixmap);
+         assert(xrb->map_ximage);
+         if (xrb->map_ximage) {
+            if (xrb->map_mode & GL_MAP_WRITE_BIT) {
+               /* put modified ximage data back into the pixmap/window */
+               int y2 = rb->Height - xrb->map_y - xrb->map_h;
+               GC gc = XCreateGC(xrb->Parent->display, xrb->pixmap, 0, NULL);
+
+               XPutImage(xrb->Parent->display,
+                         xrb->pixmap,              /* dest */
+                         gc,
+                         xrb->map_ximage,          /* source */
+                         0, 0,                     /* src x, y */
+                         xrb->map_x, y2,           /* dest x, y */
+                         xrb->map_w, xrb->map_h);  /* size */
+
+               XFreeGC(xrb->Parent->display, gc);
+            }
+            XMesaDestroyImage(xrb->map_ximage);
+            xrb->map_ximage = NULL;
+         }
+      }
+
+      xrb->map_mode = 0x0;
+
+      return;
+   }
+
+   /* otherwise, this is an ordinary malloc-based renderbuffer */
+   _mesa_unmap_soft_renderbuffer(ctx, rb);
+}
+
+
index a59ffd3755a827add1b82a423e00b5b6ce54e9a8..c896716c0bce71b49042d67b0a4548fea70908a5 100644 (file)
@@ -1077,6 +1077,9 @@ xmesa_init_driver_functions( XMesaVisual xmvisual,
       }
    }
 
+   driver->MapRenderbuffer = xmesa_MapRenderbuffer;
+   driver->UnmapRenderbuffer = xmesa_UnmapRenderbuffer;
+
 #if ENABLE_EXT_texure_compression_s3tc
    driver->ChooseTextureFormat = choose_tex_format;
 #else
index f2488695e145a69eab56dcd3e08347ce9e3305bb..29f070aa040f5f0eeed3986f8a603b0285961e5a 100644 (file)
@@ -186,6 +186,10 @@ struct xmesa_renderbuffer
    GLint bottom;       /* used for FLIP macro, equals height - 1 */
 
    ClearFunc clearFunc;
+
+   GLuint map_x, map_y, map_w, map_h;
+   GLbitfield map_mode;
+   XMesaImage *map_ximage;
 };
 
 
@@ -473,7 +477,8 @@ extern const int xmesa_kernel1[16];
  */
 
 extern struct xmesa_renderbuffer *
-xmesa_new_renderbuffer(struct gl_context *ctx, GLuint name, const struct gl_config *visual,
+xmesa_new_renderbuffer(struct gl_context *ctx, GLuint name,
+                       const struct xmesa_visual *xmvis,
                        GLboolean backBuffer);
 
 extern void
@@ -505,6 +510,16 @@ extern void
 xmesa_set_renderbuffer_funcs(struct xmesa_renderbuffer *xrb,
                              enum pixel_format pixelformat, GLint depth);
 
+extern void
+xmesa_MapRenderbuffer(struct gl_context *ctx,
+                      struct gl_renderbuffer *rb,
+                      GLuint x, GLuint y, GLuint w, GLuint h,
+                      GLbitfield mode,
+                      GLubyte **mapOut, GLint *rowStrideOut);
+
+extern void
+xmesa_UnmapRenderbuffer(struct gl_context *ctx, struct gl_renderbuffer *rb);
+
 extern void
 xmesa_destroy_buffers_on_display(XMesaDisplay *dpy);