xm_buffer.c revision 4a49301e
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 39 40#if defined(USE_XSHM) && !defined(XFree86Server) 41static volatile int mesaXErrorFlag = 0; 42 43/** 44 * Catches potential Xlib errors. 45 */ 46static int 47mesaHandleXError(XMesaDisplay *dpy, XErrorEvent *event) 48{ 49 (void) dpy; 50 (void) event; 51 mesaXErrorFlag = 1; 52 return 0; 53} 54 55/** 56 * Allocate a shared memory XImage back buffer for the given XMesaBuffer. 57 * Return: GL_TRUE if success, GL_FALSE if error 58 */ 59static GLboolean 60alloc_back_shm_ximage(XMesaBuffer b, GLuint width, GLuint height) 61{ 62 /* 63 * We have to do a _lot_ of error checking here to be sure we can 64 * really use the XSHM extension. It seems different servers trigger 65 * errors at different points if the extension won't work. Therefore 66 * we have to be very careful... 67 */ 68 GC gc; 69 int (*old_handler)(XMesaDisplay *, XErrorEvent *); 70 71 if (width == 0 || height == 0) { 72 /* this will be true the first time we're called on 'b' */ 73 return GL_FALSE; 74 } 75 76 b->backxrb->ximage = XShmCreateImage(b->xm_visual->display, 77 b->xm_visual->visinfo->visual, 78 b->xm_visual->visinfo->depth, 79 ZPixmap, NULL, &b->shminfo, 80 width, height); 81 if (b->backxrb->ximage == NULL) { 82 _mesa_warning(NULL, "alloc_back_buffer: Shared memory error (XShmCreateImage), disabling.\n"); 83 b->shm = 0; 84 return GL_FALSE; 85 } 86 87 b->shminfo.shmid = shmget(IPC_PRIVATE, b->backxrb->ximage->bytes_per_line 88 * b->backxrb->ximage->height, IPC_CREAT|0777); 89 if (b->shminfo.shmid < 0) { 90 _mesa_warning(NULL, "shmget failed while allocating back buffer.\n"); 91 XDestroyImage(b->backxrb->ximage); 92 b->backxrb->ximage = NULL; 93 _mesa_warning(NULL, "alloc_back_buffer: Shared memory error (shmget), disabling.\n"); 94 b->shm = 0; 95 return GL_FALSE; 96 } 97 98 b->shminfo.shmaddr = b->backxrb->ximage->data 99 = (char*)shmat(b->shminfo.shmid, 0, 0); 100 if (b->shminfo.shmaddr == (char *) -1) { 101 _mesa_warning(NULL, "shmat() failed while allocating back buffer.\n"); 102 XDestroyImage(b->backxrb->ximage); 103 shmctl(b->shminfo.shmid, IPC_RMID, 0); 104 b->backxrb->ximage = NULL; 105 _mesa_warning(NULL, "alloc_back_buffer: Shared memory error (shmat), disabling.\n"); 106 b->shm = 0; 107 return GL_FALSE; 108 } 109 110 b->shminfo.readOnly = False; 111 mesaXErrorFlag = 0; 112 old_handler = XSetErrorHandler(mesaHandleXError); 113 /* This may trigger the X protocol error we're ready to catch: */ 114 XShmAttach(b->xm_visual->display, &b->shminfo); 115 XSync(b->xm_visual->display, False); 116 117 if (mesaXErrorFlag) { 118 /* we are on a remote display, this error is normal, don't print it */ 119 XFlush(b->xm_visual->display); 120 mesaXErrorFlag = 0; 121 XDestroyImage(b->backxrb->ximage); 122 shmdt(b->shminfo.shmaddr); 123 shmctl(b->shminfo.shmid, IPC_RMID, 0); 124 b->backxrb->ximage = NULL; 125 b->shm = 0; 126 (void) XSetErrorHandler(old_handler); 127 return GL_FALSE; 128 } 129 130 shmctl(b->shminfo.shmid, IPC_RMID, 0); /* nobody else needs it */ 131 132 /* Finally, try an XShmPutImage to be really sure the extension works */ 133 gc = XCreateGC(b->xm_visual->display, b->frontxrb->drawable, 0, NULL); 134 XShmPutImage(b->xm_visual->display, b->frontxrb->drawable, gc, 135 b->backxrb->ximage, 0, 0, 0, 0, 1, 1 /*one pixel*/, False); 136 XSync(b->xm_visual->display, False); 137 XFreeGC(b->xm_visual->display, gc); 138 (void) XSetErrorHandler(old_handler); 139 if (mesaXErrorFlag) { 140 XFlush(b->xm_visual->display); 141 mesaXErrorFlag = 0; 142 XDestroyImage(b->backxrb->ximage); 143 shmdt(b->shminfo.shmaddr); 144 shmctl(b->shminfo.shmid, IPC_RMID, 0); 145 b->backxrb->ximage = NULL; 146 b->shm = 0; 147 return GL_FALSE; 148 } 149 150 return GL_TRUE; 151} 152#else 153static GLboolean 154alloc_back_shm_ximage(XMesaBuffer b, GLuint width, GLuint height) 155{ 156 /* Can't compile XSHM support */ 157 return GL_FALSE; 158} 159#endif 160 161 162 163/** 164 * Setup an off-screen pixmap or Ximage to use as the back buffer. 165 * Input: b - the X/Mesa buffer 166 */ 167static void 168alloc_back_buffer(XMesaBuffer b, GLuint width, GLuint height) 169{ 170 if (b->db_mode == BACK_XIMAGE) { 171 /* Deallocate the old backxrb->ximage, if any */ 172 if (b->backxrb->ximage) { 173#if defined(USE_XSHM) && !defined(XFree86Server) 174 if (b->shm) { 175 XShmDetach(b->xm_visual->display, &b->shminfo); 176 XDestroyImage(b->backxrb->ximage); 177 shmdt(b->shminfo.shmaddr); 178 } 179 else 180#endif 181 XMesaDestroyImage(b->backxrb->ximage); 182 b->backxrb->ximage = NULL; 183 } 184 185 if (width == 0 || height == 0) 186 return; 187 188 /* Allocate new back buffer */ 189 if (b->shm == 0 || !alloc_back_shm_ximage(b, width, height)) { 190 /* Allocate a regular XImage for the back buffer. */ 191#ifdef XFree86Server 192 b->backxrb->ximage = XMesaCreateImage(b->xm_visual->BitsPerPixel, 193 width, height, NULL); 194#else 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#endif 203 if (!b->backxrb->ximage) { 204 _mesa_warning(NULL, "alloc_back_buffer: XCreateImage failed.\n"); 205 return; 206 } 207 b->backxrb->ximage->data = (char *) MALLOC(b->backxrb->ximage->height 208 * b->backxrb->ximage->bytes_per_line); 209 if (!b->backxrb->ximage->data) { 210 _mesa_warning(NULL, "alloc_back_buffer: MALLOC failed.\n"); 211 XMesaDestroyImage(b->backxrb->ximage); 212 b->backxrb->ximage = NULL; 213 } 214 } 215 b->backxrb->pixmap = None; 216 } 217 else if (b->db_mode == BACK_PIXMAP) { 218 /* Free the old back pixmap */ 219 if (b->backxrb->pixmap) { 220 XMesaFreePixmap(b->xm_visual->display, b->backxrb->pixmap); 221 b->backxrb->pixmap = 0; 222 } 223 224 if (width > 0 && height > 0) { 225 /* Allocate new back pixmap */ 226 b->backxrb->pixmap = XMesaCreatePixmap(b->xm_visual->display, 227 b->frontxrb->drawable, 228 width, height, 229 GET_VISUAL_DEPTH(b->xm_visual)); 230 } 231 232 b->backxrb->ximage = NULL; 233 b->backxrb->drawable = b->backxrb->pixmap; 234 } 235} 236 237 238static void 239xmesa_delete_renderbuffer(struct gl_renderbuffer *rb) 240{ 241 /* XXX Note: the ximage or Pixmap attached to this renderbuffer 242 * should probably get freed here, but that's currently done in 243 * XMesaDestroyBuffer(). 244 */ 245 _mesa_free(rb); 246} 247 248 249/** 250 * Reallocate renderbuffer storage for front color buffer. 251 * Called via gl_renderbuffer::AllocStorage() 252 */ 253static GLboolean 254xmesa_alloc_front_storage(GLcontext *ctx, struct gl_renderbuffer *rb, 255 GLenum internalFormat, GLuint width, GLuint height) 256{ 257 struct xmesa_renderbuffer *xrb = xmesa_renderbuffer(rb); 258 259 /* just clear these to be sure we don't accidentally use them */ 260 xrb->origin1 = NULL; 261 xrb->origin2 = NULL; 262 xrb->origin3 = NULL; 263 xrb->origin4 = NULL; 264 265 /* for the FLIP macro: */ 266 xrb->bottom = height - 1; 267 268 rb->Width = width; 269 rb->Height = height; 270 rb->InternalFormat = internalFormat; 271 272 return GL_TRUE; 273} 274 275 276/** 277 * Reallocate renderbuffer storage for back color buffer. 278 * Called via gl_renderbuffer::AllocStorage() 279 */ 280static GLboolean 281xmesa_alloc_back_storage(GLcontext *ctx, struct gl_renderbuffer *rb, 282 GLenum internalFormat, GLuint width, GLuint height) 283{ 284 struct xmesa_renderbuffer *xrb = xmesa_renderbuffer(rb); 285 286 /* reallocate the back buffer XImage or Pixmap */ 287 assert(xrb->Parent); 288 alloc_back_buffer(xrb->Parent, width, height); 289 290 /* same as front buffer */ 291 /* XXX why is this here? */ 292 (void) xmesa_alloc_front_storage(ctx, rb, internalFormat, width, height); 293 294 /* plus... */ 295 if (xrb->ximage) { 296 /* Needed by PIXELADDR1 macro */ 297 xrb->width1 = xrb->ximage->bytes_per_line; 298 xrb->origin1 = (GLubyte *) xrb->ximage->data + xrb->width1 * (height - 1); 299 300 /* Needed by PIXELADDR2 macro */ 301 xrb->width2 = xrb->ximage->bytes_per_line / 2; 302 xrb->origin2 = (GLushort *) xrb->ximage->data + xrb->width2 * (height - 1); 303 304 /* Needed by PIXELADDR3 macro */ 305 xrb->width3 = xrb->ximage->bytes_per_line; 306 xrb->origin3 = (GLubyte *) xrb->ximage->data + xrb->width3 * (height - 1); 307 308 /* Needed by PIXELADDR4 macro */ 309 xrb->width4 = xrb->ximage->width; 310 xrb->origin4 = (GLuint *) xrb->ximage->data + xrb->width4 * (height - 1); 311 } 312 else { 313 /* out of memory or buffer size is 0 x 0 */ 314 xrb->width1 = xrb->width2 = xrb->width3 = xrb->width4 = 0; 315 xrb->origin1 = NULL; 316 xrb->origin2 = NULL; 317 xrb->origin3 = NULL; 318 xrb->origin4 = NULL; 319 } 320 321 return GL_TRUE; 322} 323 324 325struct xmesa_renderbuffer * 326xmesa_new_renderbuffer(GLcontext *ctx, GLuint name, const GLvisual *visual, 327 GLboolean backBuffer) 328{ 329 struct xmesa_renderbuffer *xrb = CALLOC_STRUCT(xmesa_renderbuffer); 330 if (xrb) { 331 GLuint name = 0; 332 _mesa_init_renderbuffer(&xrb->Base, name); 333 334 xrb->Base.Delete = xmesa_delete_renderbuffer; 335 if (backBuffer) 336 xrb->Base.AllocStorage = xmesa_alloc_back_storage; 337 else 338 xrb->Base.AllocStorage = xmesa_alloc_front_storage; 339 340 if (visual->rgbMode) { 341 xrb->Base.InternalFormat = GL_RGBA; 342 xrb->Base.Format = MESA_FORMAT_RGBA8888; 343 xrb->Base._BaseFormat = GL_RGBA; 344 xrb->Base.DataType = GL_UNSIGNED_BYTE; 345 } 346 else { 347 xrb->Base.InternalFormat = GL_COLOR_INDEX; 348 xrb->Base.Format = MESA_FORMAT_CI8; 349 xrb->Base._BaseFormat = GL_COLOR_INDEX; 350 xrb->Base.DataType = GL_UNSIGNED_INT; 351 } 352 /* only need to set Red/Green/EtcBits fields for user-created RBs */ 353 } 354 return xrb; 355} 356 357 358/** 359 * Called via gl_framebuffer::Delete() method when this buffer 360 * is _really_ being deleted. 361 */ 362void 363xmesa_delete_framebuffer(struct gl_framebuffer *fb) 364{ 365 XMesaBuffer b = XMESA_BUFFER(fb); 366 367 if (b->num_alloced > 0) { 368 /* If no other buffer uses this X colormap then free the colors. */ 369 if (!xmesa_find_buffer(b->display, b->cmap, b)) { 370#ifdef XFree86Server 371 int client = 0; 372 if (b->frontxrb->drawable) 373 client = CLIENT_ID(b->frontxrb->drawable->id); 374 (void)FreeColors(b->cmap, client, 375 b->num_alloced, b->alloced_colors, 0); 376#else 377 XFreeColors(b->display, b->cmap, 378 b->alloced_colors, b->num_alloced, 0); 379#endif 380 } 381 } 382 383 if (b->gc) 384 XMesaFreeGC(b->display, b->gc); 385 if (b->cleargc) 386 XMesaFreeGC(b->display, b->cleargc); 387 if (b->swapgc) 388 XMesaFreeGC(b->display, b->swapgc); 389 390 if (fb->Visual.doubleBufferMode) { 391 /* free back ximage/pixmap/shmregion */ 392 if (b->backxrb->ximage) { 393#if defined(USE_XSHM) && !defined(XFree86Server) 394 if (b->shm) { 395 XShmDetach( b->display, &b->shminfo ); 396 XDestroyImage( b->backxrb->ximage ); 397 shmdt( b->shminfo.shmaddr ); 398 } 399 else 400#endif 401 XMesaDestroyImage( b->backxrb->ximage ); 402 b->backxrb->ximage = NULL; 403 } 404 if (b->backxrb->pixmap) { 405 XMesaFreePixmap( b->display, b->backxrb->pixmap ); 406 if (b->xm_visual->hpcr_clear_flag) { 407 XMesaFreePixmap( b->display, 408 b->xm_visual->hpcr_clear_pixmap ); 409 XMesaDestroyImage( b->xm_visual->hpcr_clear_ximage ); 410 } 411 } 412 } 413 414 if (b->rowimage) { 415 _mesa_free( b->rowimage->data ); 416 b->rowimage->data = NULL; 417 XMesaDestroyImage( b->rowimage ); 418 } 419 420 _mesa_free_framebuffer_data(fb); 421 _mesa_free(fb); 422} 423