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