2 * Mesa 3-D graphics library
4 * Copyright (C) 1999-2006 Brian Paul All Rights Reserved.
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:
13 * The above copyright notice and this permission notice shall be included
14 * in all copies or substantial portions of the Software.
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.
28 * Framebuffer and renderbuffer-related functions.
32 #include "glxheader.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"
42 #define XMESA_RENDERBUFFER 0x1234
46 static volatile int mesaXErrorFlag
= 0;
49 * Catches potential Xlib errors.
52 mesaHandleXError(XMesaDisplay
*dpy
, XErrorEvent
*event
)
61 * Allocate a shared memory XImage back buffer for the given XMesaBuffer.
62 * Return: GL_TRUE if success, GL_FALSE if error
65 alloc_back_shm_ximage(XMesaBuffer b
, GLuint width
, GLuint height
)
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...
74 int (*old_handler
)(XMesaDisplay
*, XErrorEvent
*);
76 if (width
== 0 || height
== 0) {
77 /* this will be true the first time we're called on 'b' */
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
,
86 if (b
->backxrb
->ximage
== NULL
) {
87 _mesa_warning(NULL
, "alloc_back_buffer: Shared memory error (XShmCreateImage), disabling.\n");
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");
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");
116 b
->shminfo
.readOnly
= False
;
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
);
123 if (mesaXErrorFlag
) {
124 /* we are on a remote display, this error is normal, don't print it */
125 XFlush(b
->xm_visual
->display
);
127 XDestroyImage(b
->backxrb
->ximage
);
128 shmdt(b
->shminfo
.shmaddr
);
129 shmctl(b
->shminfo
.shmid
, IPC_RMID
, 0);
130 b
->backxrb
->ximage
= NULL
;
132 (void) XSetErrorHandler(old_handler
);
136 shmctl(b
->shminfo
.shmid
, IPC_RMID
, 0); /* nobody else needs it */
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
);
148 XDestroyImage(b
->backxrb
->ximage
);
149 shmdt(b
->shminfo
.shmaddr
);
150 shmctl(b
->shminfo
.shmid
, IPC_RMID
, 0);
151 b
->backxrb
->ximage
= NULL
;
160 alloc_back_shm_ximage(XMesaBuffer b
, GLuint width
, GLuint height
)
162 /* Can't compile XSHM support */
170 * Setup an off-screen pixmap or Ximage to use as the back buffer.
171 * Input: b - the X/Mesa buffer
174 alloc_back_buffer(XMesaBuffer b
, GLuint width
, GLuint height
)
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)
181 XShmDetach(b
->xm_visual
->display
, &b
->shminfo
);
182 XDestroyImage(b
->backxrb
->ximage
);
183 shmdt(b
->shminfo
.shmaddr
);
187 XMesaDestroyImage(b
->backxrb
->ximage
);
188 b
->backxrb
->ximage
= NULL
;
191 if (width
== 0 || height
== 0)
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 */
203 8, 0); /* pad, bytes_per_line */
204 if (!b
->backxrb
->ximage
) {
205 _mesa_warning(NULL
, "alloc_back_buffer: XCreateImage failed.\n");
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
;
216 b
->backxrb
->pixmap
= None
;
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;
225 if (width
> 0 && height
> 0) {
226 /* Allocate new back pixmap */
227 b
->backxrb
->pixmap
= XMesaCreatePixmap(b
->xm_visual
->display
,
228 b
->frontxrb
->drawable
,
230 GET_VISUAL_DEPTH(b
->xm_visual
));
233 b
->backxrb
->ximage
= NULL
;
234 b
->backxrb
->drawable
= b
->backxrb
->pixmap
;
240 xmesa_delete_renderbuffer(struct gl_context
*ctx
, struct gl_renderbuffer
*rb
)
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().
251 * Reallocate renderbuffer storage for front color buffer.
252 * Called via gl_renderbuffer::AllocStorage()
255 xmesa_alloc_front_storage(struct gl_context
*ctx
, struct gl_renderbuffer
*rb
,
256 GLenum internalFormat
, GLuint width
, GLuint height
)
258 struct xmesa_renderbuffer
*xrb
= xmesa_renderbuffer(rb
);
260 /* just clear these to be sure we don't accidentally use them */
265 /* for the FLIP macro: */
266 xrb
->bottom
= height
- 1;
270 rb
->InternalFormat
= internalFormat
;
277 * Reallocate renderbuffer storage for back color buffer.
278 * Called via gl_renderbuffer::AllocStorage()
281 xmesa_alloc_back_storage(struct gl_context
*ctx
, struct gl_renderbuffer
*rb
,
282 GLenum internalFormat
, GLuint width
, GLuint height
)
284 struct xmesa_renderbuffer
*xrb
= xmesa_renderbuffer(rb
);
286 /* reallocate the back buffer XImage or Pixmap */
288 alloc_back_buffer(xrb
->Parent
, width
, height
);
290 /* same as front buffer */
291 /* XXX why is this here? */
292 (void) xmesa_alloc_front_storage(ctx
, rb
, internalFormat
, width
, height
);
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);
300 /* Needed by PIXELADDR3 macro */
301 xrb
->width3
= xrb
->ximage
->bytes_per_line
;
302 xrb
->origin3
= (GLubyte
*) xrb
->ximage
->data
+ xrb
->width3
* (height
- 1);
304 /* Needed by PIXELADDR4 macro */
305 xrb
->width4
= xrb
->ximage
->width
;
306 xrb
->origin4
= (GLuint
*) xrb
->ximage
->data
+ xrb
->width4
* (height
- 1);
309 /* out of memory or buffer size is 0 x 0 */
310 xrb
->width2
= xrb
->width3
= xrb
->width4
= 0;
321 * Used for allocating front/back renderbuffers for an X window.
323 struct xmesa_renderbuffer
*
324 xmesa_new_renderbuffer(struct gl_context
*ctx
, GLuint name
,
325 const struct xmesa_visual
*xmvis
,
326 GLboolean backBuffer
)
328 struct xmesa_renderbuffer
*xrb
= CALLOC_STRUCT(xmesa_renderbuffer
);
331 _mesa_init_renderbuffer(&xrb
->Base
.Base
, name
);
333 xrb
->Base
.Base
.Delete
= xmesa_delete_renderbuffer
;
335 xrb
->Base
.Base
.AllocStorage
= xmesa_alloc_back_storage
;
337 xrb
->Base
.Base
.AllocStorage
= xmesa_alloc_front_storage
;
339 xrb
->Base
.Base
.InternalFormat
= GL_RGBA
;
340 xrb
->Base
.Base
._BaseFormat
= GL_RGBA
;
341 xrb
->Base
.Base
.ClassID
= XMESA_RENDERBUFFER
;
343 switch (xmvis
->undithered_pf
) {
345 /* This will really only happen for pixmaps. We'll access the
346 * pixmap via a temporary XImage which will be 32bpp.
348 xrb
->Base
.Base
.Format
= MESA_FORMAT_B8G8R8X8_UNORM
;
351 xrb
->Base
.Base
.Format
= MESA_FORMAT_B8G8R8A8_UNORM
;
354 xrb
->Base
.Base
.Format
= MESA_FORMAT_R8G8B8A8_UNORM
;
357 xrb
->Base
.Base
.Format
= MESA_FORMAT_B5G6R5_UNORM
;
360 _mesa_warning(ctx
, "Bad pixel format in xmesa_new_renderbuffer");
361 xrb
->Base
.Base
.Format
= MESA_FORMAT_B8G8R8A8_UNORM
;
365 /* only need to set Red/Green/EtcBits fields for user-created RBs */
372 * Called via gl_framebuffer::Delete() method when this buffer
373 * is _really_ being deleted.
376 xmesa_delete_framebuffer(struct gl_framebuffer
*fb
)
378 XMesaBuffer b
= XMESA_BUFFER(fb
);
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);
389 XMesaFreeGC(b
->display
, b
->gc
);
391 XMesaFreeGC(b
->display
, b
->cleargc
);
393 XMesaFreeGC(b
->display
, b
->swapgc
);
395 if (fb
->Visual
.doubleBufferMode
) {
396 /* free back ximage/pixmap/shmregion */
397 if (b
->backxrb
->ximage
) {
398 #if defined(USE_XSHM)
400 XShmDetach( b
->display
, &b
->shminfo
);
401 XDestroyImage( b
->backxrb
->ximage
);
402 shmdt( b
->shminfo
.shmaddr
);
406 XMesaDestroyImage( b
->backxrb
->ximage
);
407 b
->backxrb
->ximage
= NULL
;
409 if (b
->backxrb
->pixmap
) {
410 XMesaFreePixmap( b
->display
, b
->backxrb
->pixmap
);
414 _mesa_free_framebuffer_data(fb
);
420 * Called via ctx->Driver.MapRenderbuffer()
423 xmesa_MapRenderbuffer(struct gl_context
*ctx
,
424 struct gl_renderbuffer
*rb
,
425 GLuint x
, GLuint y
, GLuint w
, GLuint h
,
427 GLubyte
**mapOut
, GLint
*rowStrideOut
,
430 struct xmesa_renderbuffer
*xrb
= xmesa_renderbuffer(rb
);
432 if (xrb
->Base
.Base
.ClassID
== XMESA_RENDERBUFFER
) {
433 XImage
*ximage
= xrb
->ximage
;
435 assert(!xrb
->map_mode
); /* only a single mapping allowed */
437 xrb
->map_mode
= mode
;
444 int y2
= rb
->Height
- y
- 1;
446 *mapOut
= (GLubyte
*) ximage
->data
447 + y2
* ximage
->bytes_per_line
448 + x
* ximage
->bits_per_pixel
/ 8;
451 /* this must be a pixmap/window renderbuffer */
452 int (*old_handler
)(XMesaDisplay
*, XErrorEvent
*);
453 int y2
= rb
->Height
- y
- h
;
457 /* Install error handler for XGetImage() in case the window
458 * isn't mapped. If we fail we'll create a temporary XImage.
461 old_handler
= XSetErrorHandler(mesaHandleXError
);
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
,
468 XSetErrorHandler(old_handler
);
470 if (mesaXErrorFlag
) {
471 /* create new, temporary XImage */
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 */
483 xrb
->Base
.Base
.Width
,
484 xrb
->Base
.Base
.Height
,
495 xrb
->map_ximage
= ximage
;
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
;
502 /* We return a negative stride here since XImage data is upside down
503 * with respect to OpenGL images.
505 *rowStrideOut
= -ximage
->bytes_per_line
;
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);
516 * Called via ctx->Driver.UnmapRenderbuffer()
519 xmesa_UnmapRenderbuffer(struct gl_context
*ctx
, struct gl_renderbuffer
*rb
)
521 struct xmesa_renderbuffer
*xrb
= xmesa_renderbuffer(rb
);
523 if (xrb
->Base
.Base
.ClassID
== XMESA_RENDERBUFFER
) {
524 XImage
*ximage
= xrb
->ximage
;
527 /* this must be a pixmap/window renderbuffer */
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
);
536 XPutImage(xrb
->Parent
->display
,
537 xrb
->pixmap
, /* dest */
539 xrb
->map_ximage
, /* source */
541 xrb
->map_x
, y2
, /* dest x, y */
542 xrb
->map_w
, xrb
->map_h
); /* size */
544 XFreeGC(xrb
->Parent
->display
, gc
);
546 XMesaDestroyImage(xrb
->map_ximage
);
547 xrb
->map_ximage
= NULL
;
556 /* otherwise, this is an ordinary malloc-based renderbuffer */
557 _swrast_unmap_soft_renderbuffer(ctx
, rb
);