561 lines
17 KiB
C
561 lines
17 KiB
C
/*
|
|
* Mesa 3-D graphics library
|
|
*
|
|
* 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
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS 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 "xmesaP.h"
|
|
#include "main/errors.h"
|
|
#include "main/formats.h"
|
|
#include "main/framebuffer.h"
|
|
#include "main/renderbuffer.h"
|
|
#include "swrast/s_renderbuffer.h"
|
|
#include "util/u_memory.h"
|
|
|
|
|
|
#define XMESA_RENDERBUFFER 0x1234
|
|
|
|
|
|
#if defined(USE_XSHM)
|
|
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;
|
|
}
|
|
|
|
/* 0600 = user read+write */
|
|
b->shminfo.shmid = shmget(IPC_PRIVATE, b->backxrb->ximage->bytes_per_line
|
|
* b->backxrb->ximage->height, IPC_CREAT | 0600);
|
|
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)
|
|
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. */
|
|
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 */
|
|
if (!b->backxrb->ximage) {
|
|
_mesa_warning(NULL, "alloc_back_buffer: XCreateImage failed.\n");
|
|
return;
|
|
}
|
|
b->backxrb->ximage->data = 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;
|
|
b->backxrb->drawable = b->backxrb->pixmap;
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
xmesa_delete_renderbuffer(struct gl_context *ctx, 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().
|
|
*/
|
|
free(rb);
|
|
}
|
|
|
|
|
|
/**
|
|
* Reallocate renderbuffer storage for front color buffer.
|
|
* Called via gl_renderbuffer::AllocStorage()
|
|
*/
|
|
static GLboolean
|
|
xmesa_alloc_front_storage(struct gl_context *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->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;
|
|
|
|
return GL_TRUE;
|
|
}
|
|
|
|
|
|
/**
|
|
* Reallocate renderbuffer storage for back color buffer.
|
|
* Called via gl_renderbuffer::AllocStorage()
|
|
*/
|
|
static GLboolean
|
|
xmesa_alloc_back_storage(struct gl_context *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 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->width2 = xrb->width3 = xrb->width4 = 0;
|
|
xrb->origin2 = NULL;
|
|
xrb->origin3 = NULL;
|
|
xrb->origin4 = NULL;
|
|
}
|
|
|
|
return GL_TRUE;
|
|
}
|
|
|
|
|
|
/**
|
|
* Used for allocating front/back renderbuffers for an X window.
|
|
*/
|
|
struct xmesa_renderbuffer *
|
|
xmesa_new_renderbuffer(struct gl_context *ctx, GLuint name,
|
|
const struct xmesa_visual *xmvis,
|
|
GLboolean backBuffer)
|
|
{
|
|
struct xmesa_renderbuffer *xrb = CALLOC_STRUCT(xmesa_renderbuffer);
|
|
if (xrb) {
|
|
GLuint name = 0;
|
|
_mesa_init_renderbuffer(&xrb->Base.Base, name);
|
|
|
|
xrb->Base.Base.Delete = xmesa_delete_renderbuffer;
|
|
if (backBuffer)
|
|
xrb->Base.Base.AllocStorage = xmesa_alloc_back_storage;
|
|
else
|
|
xrb->Base.Base.AllocStorage = xmesa_alloc_front_storage;
|
|
|
|
xrb->Base.Base.InternalFormat = GL_RGBA;
|
|
xrb->Base.Base._BaseFormat = GL_RGBA;
|
|
xrb->Base.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.Base.Format = MESA_FORMAT_B8G8R8X8_UNORM;
|
|
break;
|
|
case PF_8A8R8G8B:
|
|
xrb->Base.Base.Format = MESA_FORMAT_B8G8R8A8_UNORM;
|
|
break;
|
|
case PF_8A8B8G8R:
|
|
xrb->Base.Base.Format = MESA_FORMAT_R8G8B8A8_UNORM;
|
|
break;
|
|
case PF_5R6G5B:
|
|
xrb->Base.Base.Format = MESA_FORMAT_B5G6R5_UNORM;
|
|
break;
|
|
default:
|
|
_mesa_warning(ctx, "Bad pixel format in xmesa_new_renderbuffer");
|
|
xrb->Base.Base.Format = MESA_FORMAT_B8G8R8A8_UNORM;
|
|
break;
|
|
}
|
|
|
|
/* only need to set Red/Green/EtcBits fields for user-created RBs */
|
|
}
|
|
return xrb;
|
|
}
|
|
|
|
|
|
/**
|
|
* 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)) {
|
|
XFreeColors(b->display, b->cmap,
|
|
b->alloced_colors, b->num_alloced, 0);
|
|
}
|
|
}
|
|
|
|
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)
|
|
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 );
|
|
}
|
|
}
|
|
|
|
_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,
|
|
bool flip_y)
|
|
{
|
|
struct xmesa_renderbuffer *xrb = xmesa_renderbuffer(rb);
|
|
|
|
if (xrb->Base.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 (*old_handler)(XMesaDisplay *, XErrorEvent *);
|
|
int y2 = rb->Height - y - h;
|
|
|
|
assert(xrb->pixmap);
|
|
|
|
/* Install error handler for XGetImage() in case the window
|
|
* isn't mapped. If we fail we'll create a temporary XImage.
|
|
*/
|
|
mesaXErrorFlag = 0;
|
|
old_handler = XSetErrorHandler(mesaHandleXError);
|
|
|
|
/* read pixel data out of the pixmap/window into an XImage */
|
|
ximage = XGetImage(xrb->Parent->display,
|
|
xrb->pixmap, x, y2, w, h,
|
|
AllPlanes, ZPixmap);
|
|
|
|
XSetErrorHandler(old_handler);
|
|
|
|
if (mesaXErrorFlag) {
|
|
/* create new, temporary XImage */
|
|
int bytes_per_line =
|
|
_mesa_format_row_stride(xrb->Base.Base.Format,
|
|
xrb->Base.Base.Width);
|
|
char *image = malloc(bytes_per_line *
|
|
xrb->Base.Base.Height);
|
|
ximage = XCreateImage(xrb->Parent->display,
|
|
xrb->Parent->xm_visual->visinfo->visual,
|
|
xrb->Parent->xm_visual->visinfo->depth,
|
|
ZPixmap, /* format */
|
|
0, /* offset */
|
|
image, /* data */
|
|
xrb->Base.Base.Width,
|
|
xrb->Base.Base.Height,
|
|
8, /* pad */
|
|
bytes_per_line);
|
|
}
|
|
|
|
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 */
|
|
_swrast_map_soft_renderbuffer(ctx, rb, x, y, w, h, mode,
|
|
mapOut, rowStrideOut, false);
|
|
}
|
|
|
|
|
|
/**
|
|
* 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.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 */
|
|
_swrast_unmap_soft_renderbuffer(ctx, rb);
|
|
}
|
|
|
|
|