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 #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 = (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 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 = (char *) 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