1/* 2 * Mesa 3-D graphics library 3 * 4 * Copyright (C) 1999-2006 Brian Paul All Rights Reserved. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 * and/or sell copies of the Software, and to permit persons to whom the 11 * Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included 14 * in all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 * 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/errors.h" 35#include "main/formats.h" 36#include "main/framebuffer.h" 37#include "main/renderbuffer.h" 38#include "swrast/s_renderbuffer.h" 39#include "util/u_memory.h" 40 41 42#define XMESA_RENDERBUFFER 0x1234 43 44 45#if defined(USE_XSHM) 46static volatile int mesaXErrorFlag = 0; 47 48/** 49 * Catches potential Xlib errors. 50 */ 51static int 52mesaHandleXError(XMesaDisplay *dpy, XErrorEvent *event) 53{ 54 (void) dpy; 55 (void) event; 56 mesaXErrorFlag = 1; 57 return 0; 58} 59 60/** 61 * Allocate a shared memory XImage back buffer for the given XMesaBuffer. 62 * Return: GL_TRUE if success, GL_FALSE if error 63 */ 64static GLboolean 65alloc_back_shm_ximage(XMesaBuffer b, GLuint width, GLuint height) 66{ 67 /* 68 * We have to do a _lot_ of error checking here to be sure we can 69 * really use the XSHM extension. It seems different servers trigger 70 * errors at different points if the extension won't work. Therefore 71 * we have to be very careful... 72 */ 73 GC gc; 74 int (*old_handler)(XMesaDisplay *, XErrorEvent *); 75 76 if (width == 0 || height == 0) { 77 /* this will be true the first time we're called on 'b' */ 78 return GL_FALSE; 79 } 80 81 b->backxrb->ximage = XShmCreateImage(b->xm_visual->display, 82 b->xm_visual->visinfo->visual, 83 b->xm_visual->visinfo->depth, 84 ZPixmap, NULL, &b->shminfo, 85 width, height); 86 if (b->backxrb->ximage == NULL) { 87 _mesa_warning(NULL, "alloc_back_buffer: Shared memory error (XShmCreateImage), disabling.\n"); 88 b->shm = 0; 89 return GL_FALSE; 90 } 91 92 /* 0600 = user read+write */ 93 b->shminfo.shmid = shmget(IPC_PRIVATE, b->backxrb->ximage->bytes_per_line 94 * b->backxrb->ximage->height, IPC_CREAT | 0600); 95 if (b->shminfo.shmid < 0) { 96 _mesa_warning(NULL, "shmget failed while allocating back buffer.\n"); 97 XDestroyImage(b->backxrb->ximage); 98 b->backxrb->ximage = NULL; 99 _mesa_warning(NULL, "alloc_back_buffer: Shared memory error (shmget), disabling.\n"); 100 b->shm = 0; 101 return GL_FALSE; 102 } 103 104 b->shminfo.shmaddr = b->backxrb->ximage->data 105 = (char*)shmat(b->shminfo.shmid, 0, 0); 106 if (b->shminfo.shmaddr == (char *) -1) { 107 _mesa_warning(NULL, "shmat() failed while allocating back buffer.\n"); 108 XDestroyImage(b->backxrb->ximage); 109 shmctl(b->shminfo.shmid, IPC_RMID, 0); 110 b->backxrb->ximage = NULL; 111 _mesa_warning(NULL, "alloc_back_buffer: Shared memory error (shmat), disabling.\n"); 112 b->shm = 0; 113 return GL_FALSE; 114 } 115 116 b->shminfo.readOnly = False; 117 mesaXErrorFlag = 0; 118 old_handler = XSetErrorHandler(mesaHandleXError); 119 /* This may trigger the X protocol error we're ready to catch: */ 120 XShmAttach(b->xm_visual->display, &b->shminfo); 121 XSync(b->xm_visual->display, False); 122 123 if (mesaXErrorFlag) { 124 /* we are on a remote display, this error is normal, don't print it */ 125 XFlush(b->xm_visual->display); 126 mesaXErrorFlag = 0; 127 XDestroyImage(b->backxrb->ximage); 128 shmdt(b->shminfo.shmaddr); 129 shmctl(b->shminfo.shmid, IPC_RMID, 0); 130 b->backxrb->ximage = NULL; 131 b->shm = 0; 132 (void) XSetErrorHandler(old_handler); 133 return GL_FALSE; 134 } 135 136 shmctl(b->shminfo.shmid, IPC_RMID, 0); /* nobody else needs it */ 137 138 /* Finally, try an XShmPutImage to be really sure the extension works */ 139 gc = XCreateGC(b->xm_visual->display, b->frontxrb->drawable, 0, NULL); 140 XShmPutImage(b->xm_visual->display, b->frontxrb->drawable, gc, 141 b->backxrb->ximage, 0, 0, 0, 0, 1, 1 /*one pixel*/, False); 142 XSync(b->xm_visual->display, False); 143 XFreeGC(b->xm_visual->display, gc); 144 (void) XSetErrorHandler(old_handler); 145 if (mesaXErrorFlag) { 146 XFlush(b->xm_visual->display); 147 mesaXErrorFlag = 0; 148 XDestroyImage(b->backxrb->ximage); 149 shmdt(b->shminfo.shmaddr); 150 shmctl(b->shminfo.shmid, IPC_RMID, 0); 151 b->backxrb->ximage = NULL; 152 b->shm = 0; 153 return GL_FALSE; 154 } 155 156 return GL_TRUE; 157} 158#else 159static GLboolean 160alloc_back_shm_ximage(XMesaBuffer b, GLuint width, GLuint height) 161{ 162 /* Can't compile XSHM support */ 163 return GL_FALSE; 164} 165#endif 166 167 168 169/** 170 * Setup an off-screen pixmap or Ximage to use as the back buffer. 171 * Input: b - the X/Mesa buffer 172 */ 173static void 174alloc_back_buffer(XMesaBuffer b, GLuint width, GLuint height) 175{ 176 if (b->db_mode == BACK_XIMAGE) { 177 /* Deallocate the old backxrb->ximage, if any */ 178 if (b->backxrb->ximage) { 179#if defined(USE_XSHM) 180 if (b->shm) { 181 XShmDetach(b->xm_visual->display, &b->shminfo); 182 XDestroyImage(b->backxrb->ximage); 183 shmdt(b->shminfo.shmaddr); 184 } 185 else 186#endif 187 XMesaDestroyImage(b->backxrb->ximage); 188 b->backxrb->ximage = NULL; 189 } 190 191 if (width == 0 || height == 0) 192 return; 193 194 /* Allocate new back buffer */ 195 if (b->shm == 0 || !alloc_back_shm_ximage(b, width, height)) { 196 /* Allocate a regular XImage for the back buffer. */ 197 b->backxrb->ximage = XCreateImage(b->xm_visual->display, 198 b->xm_visual->visinfo->visual, 199 GET_VISUAL_DEPTH(b->xm_visual), 200 ZPixmap, 0, /* format, offset */ 201 NULL, 202 width, height, 203 8, 0); /* pad, bytes_per_line */ 204 if (!b->backxrb->ximage) { 205 _mesa_warning(NULL, "alloc_back_buffer: XCreateImage failed.\n"); 206 return; 207 } 208 b->backxrb->ximage->data = malloc(b->backxrb->ximage->height 209 * b->backxrb->ximage->bytes_per_line); 210 if (!b->backxrb->ximage->data) { 211 _mesa_warning(NULL, "alloc_back_buffer: MALLOC failed.\n"); 212 XMesaDestroyImage(b->backxrb->ximage); 213 b->backxrb->ximage = NULL; 214 } 215 } 216 b->backxrb->pixmap = None; 217 } 218 else if (b->db_mode == BACK_PIXMAP) { 219 /* Free the old back pixmap */ 220 if (b->backxrb->pixmap) { 221 XMesaFreePixmap(b->xm_visual->display, b->backxrb->pixmap); 222 b->backxrb->pixmap = 0; 223 } 224 225 if (width > 0 && height > 0) { 226 /* Allocate new back pixmap */ 227 b->backxrb->pixmap = XMesaCreatePixmap(b->xm_visual->display, 228 b->frontxrb->drawable, 229 width, height, 230 GET_VISUAL_DEPTH(b->xm_visual)); 231 } 232 233 b->backxrb->ximage = NULL; 234 b->backxrb->drawable = b->backxrb->pixmap; 235 } 236} 237 238 239static void 240xmesa_delete_renderbuffer(struct gl_context *ctx, struct gl_renderbuffer *rb) 241{ 242 /* XXX Note: the ximage or Pixmap attached to this renderbuffer 243 * should probably get freed here, but that's currently done in 244 * XMesaDestroyBuffer(). 245 */ 246 free(rb); 247} 248 249 250/** 251 * Reallocate renderbuffer storage for front color buffer. 252 * Called via gl_renderbuffer::AllocStorage() 253 */ 254static GLboolean 255xmesa_alloc_front_storage(struct gl_context *ctx, struct gl_renderbuffer *rb, 256 GLenum internalFormat, GLuint width, GLuint height) 257{ 258 struct xmesa_renderbuffer *xrb = xmesa_renderbuffer(rb); 259 260 /* just clear these to be sure we don't accidentally use them */ 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(struct gl_context *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 PIXELADDR2 macro */ 297 xrb->width2 = xrb->ximage->bytes_per_line / 2; 298 xrb->origin2 = (GLushort *) xrb->ximage->data + xrb->width2 * (height - 1); 299 300 /* Needed by PIXELADDR3 macro */ 301 xrb->width3 = xrb->ximage->bytes_per_line; 302 xrb->origin3 = (GLubyte *) xrb->ximage->data + xrb->width3 * (height - 1); 303 304 /* Needed by PIXELADDR4 macro */ 305 xrb->width4 = xrb->ximage->width; 306 xrb->origin4 = (GLuint *) xrb->ximage->data + xrb->width4 * (height - 1); 307 } 308 else { 309 /* out of memory or buffer size is 0 x 0 */ 310 xrb->width2 = xrb->width3 = xrb->width4 = 0; 311 xrb->origin2 = NULL; 312 xrb->origin3 = NULL; 313 xrb->origin4 = NULL; 314 } 315 316 return GL_TRUE; 317} 318 319 320/** 321 * Used for allocating front/back renderbuffers for an X window. 322 */ 323struct xmesa_renderbuffer * 324xmesa_new_renderbuffer(struct gl_context *ctx, GLuint name, 325 const struct xmesa_visual *xmvis, 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.Base, name); 332 333 xrb->Base.Base.Delete = xmesa_delete_renderbuffer; 334 if (backBuffer) 335 xrb->Base.Base.AllocStorage = xmesa_alloc_back_storage; 336 else 337 xrb->Base.Base.AllocStorage = xmesa_alloc_front_storage; 338 339 xrb->Base.Base.InternalFormat = GL_RGBA; 340 xrb->Base.Base._BaseFormat = GL_RGBA; 341 xrb->Base.Base.ClassID = XMESA_RENDERBUFFER; 342 343 switch (xmvis->undithered_pf) { 344 case PF_8R8G8B: 345 /* This will really only happen for pixmaps. We'll access the 346 * pixmap via a temporary XImage which will be 32bpp. 347 */ 348 xrb->Base.Base.Format = MESA_FORMAT_B8G8R8X8_UNORM; 349 break; 350 case PF_8A8R8G8B: 351 xrb->Base.Base.Format = MESA_FORMAT_B8G8R8A8_UNORM; 352 break; 353 case PF_8A8B8G8R: 354 xrb->Base.Base.Format = MESA_FORMAT_R8G8B8A8_UNORM; 355 break; 356 case PF_5R6G5B: 357 xrb->Base.Base.Format = MESA_FORMAT_B5G6R5_UNORM; 358 break; 359 default: 360 _mesa_warning(ctx, "Bad pixel format in xmesa_new_renderbuffer"); 361 xrb->Base.Base.Format = MESA_FORMAT_B8G8R8A8_UNORM; 362 break; 363 } 364 365 /* only need to set Red/Green/EtcBits fields for user-created RBs */ 366 } 367 return xrb; 368} 369 370 371/** 372 * Called via gl_framebuffer::Delete() method when this buffer 373 * is _really_ being deleted. 374 */ 375void 376xmesa_delete_framebuffer(struct gl_framebuffer *fb) 377{ 378 XMesaBuffer b = XMESA_BUFFER(fb); 379 380 if (b->num_alloced > 0) { 381 /* If no other buffer uses this X colormap then free the colors. */ 382 if (!xmesa_find_buffer(b->display, b->cmap, b)) { 383 XFreeColors(b->display, b->cmap, 384 b->alloced_colors, b->num_alloced, 0); 385 } 386 } 387 388 if (b->gc) 389 XMesaFreeGC(b->display, b->gc); 390 if (b->cleargc) 391 XMesaFreeGC(b->display, b->cleargc); 392 if (b->swapgc) 393 XMesaFreeGC(b->display, b->swapgc); 394 395 if (fb->Visual.doubleBufferMode) { 396 /* free back ximage/pixmap/shmregion */ 397 if (b->backxrb->ximage) { 398#if defined(USE_XSHM) 399 if (b->shm) { 400 XShmDetach( b->display, &b->shminfo ); 401 XDestroyImage( b->backxrb->ximage ); 402 shmdt( b->shminfo.shmaddr ); 403 } 404 else 405#endif 406 XMesaDestroyImage( b->backxrb->ximage ); 407 b->backxrb->ximage = NULL; 408 } 409 if (b->backxrb->pixmap) { 410 XMesaFreePixmap( b->display, b->backxrb->pixmap ); 411 } 412 } 413 414 _mesa_free_framebuffer_data(fb); 415 free(fb); 416} 417 418 419/** 420 * Called via ctx->Driver.MapRenderbuffer() 421 */ 422void 423xmesa_MapRenderbuffer(struct gl_context *ctx, 424 struct gl_renderbuffer *rb, 425 GLuint x, GLuint y, GLuint w, GLuint h, 426 GLbitfield mode, 427 GLubyte **mapOut, GLint *rowStrideOut, 428 bool flip_y) 429{ 430 struct xmesa_renderbuffer *xrb = xmesa_renderbuffer(rb); 431 432 if (xrb->Base.Base.ClassID == XMESA_RENDERBUFFER) { 433 XImage *ximage = xrb->ximage; 434 435 assert(!xrb->map_mode); /* only a single mapping allowed */ 436 437 xrb->map_mode = mode; 438 xrb->map_x = x; 439 xrb->map_y = y; 440 xrb->map_w = w; 441 xrb->map_h = h; 442 443 if (ximage) { 444 int y2 = rb->Height - y - 1; 445 446 *mapOut = (GLubyte *) ximage->data 447 + y2 * ximage->bytes_per_line 448 + x * ximage->bits_per_pixel / 8; 449 } 450 else { 451 /* this must be a pixmap/window renderbuffer */ 452 int (*old_handler)(XMesaDisplay *, XErrorEvent *); 453 int y2 = rb->Height - y - h; 454 455 assert(xrb->pixmap); 456 457 /* Install error handler for XGetImage() in case the window 458 * isn't mapped. If we fail we'll create a temporary XImage. 459 */ 460 mesaXErrorFlag = 0; 461 old_handler = XSetErrorHandler(mesaHandleXError); 462 463 /* read pixel data out of the pixmap/window into an XImage */ 464 ximage = XGetImage(xrb->Parent->display, 465 xrb->pixmap, x, y2, w, h, 466 AllPlanes, ZPixmap); 467 468 XSetErrorHandler(old_handler); 469 470 if (mesaXErrorFlag) { 471 /* create new, temporary XImage */ 472 int bytes_per_line = 473 _mesa_format_row_stride(xrb->Base.Base.Format, 474 xrb->Base.Base.Width); 475 char *image = malloc(bytes_per_line * 476 xrb->Base.Base.Height); 477 ximage = XCreateImage(xrb->Parent->display, 478 xrb->Parent->xm_visual->visinfo->visual, 479 xrb->Parent->xm_visual->visinfo->depth, 480 ZPixmap, /* format */ 481 0, /* offset */ 482 image, /* data */ 483 xrb->Base.Base.Width, 484 xrb->Base.Base.Height, 485 8, /* pad */ 486 bytes_per_line); 487 } 488 489 if (!ximage) { 490 *mapOut = NULL; 491 *rowStrideOut = 0; 492 return; 493 } 494 495 xrb->map_ximage = ximage; 496 497 /* the first row of the OpenGL image is last row of the XImage */ 498 *mapOut = (GLubyte *) ximage->data 499 + (h - 1) * ximage->bytes_per_line; 500 } 501 502 /* We return a negative stride here since XImage data is upside down 503 * with respect to OpenGL images. 504 */ 505 *rowStrideOut = -ximage->bytes_per_line; 506 return; 507 } 508 509 /* otherwise, this is an ordinary malloc-based renderbuffer */ 510 _swrast_map_soft_renderbuffer(ctx, rb, x, y, w, h, mode, 511 mapOut, rowStrideOut, false); 512} 513 514 515/** 516 * Called via ctx->Driver.UnmapRenderbuffer() 517 */ 518void 519xmesa_UnmapRenderbuffer(struct gl_context *ctx, struct gl_renderbuffer *rb) 520{ 521 struct xmesa_renderbuffer *xrb = xmesa_renderbuffer(rb); 522 523 if (xrb->Base.Base.ClassID == XMESA_RENDERBUFFER) { 524 XImage *ximage = xrb->ximage; 525 526 if (!ximage) { 527 /* this must be a pixmap/window renderbuffer */ 528 assert(xrb->pixmap); 529 assert(xrb->map_ximage); 530 if (xrb->map_ximage) { 531 if (xrb->map_mode & GL_MAP_WRITE_BIT) { 532 /* put modified ximage data back into the pixmap/window */ 533 int y2 = rb->Height - xrb->map_y - xrb->map_h; 534 GC gc = XCreateGC(xrb->Parent->display, xrb->pixmap, 0, NULL); 535 536 XPutImage(xrb->Parent->display, 537 xrb->pixmap, /* dest */ 538 gc, 539 xrb->map_ximage, /* source */ 540 0, 0, /* src x, y */ 541 xrb->map_x, y2, /* dest x, y */ 542 xrb->map_w, xrb->map_h); /* size */ 543 544 XFreeGC(xrb->Parent->display, gc); 545 } 546 XMesaDestroyImage(xrb->map_ximage); 547 xrb->map_ximage = NULL; 548 } 549 } 550 551 xrb->map_mode = 0x0; 552 553 return; 554 } 555 556 /* otherwise, this is an ordinary malloc-based renderbuffer */ 557 _swrast_unmap_soft_renderbuffer(ctx, rb); 558} 559 560 561