2 * Mesa 3-D graphics library
5 * Copyright (C) 1999-2006 Brian Paul All Rights Reserved.
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:
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
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.
28 * Framebuffer and renderbuffer-related functions.
32 #include "glxheader.h"
34 #include "main/imports.h"
35 #include "main/formats.h"
36 #include "main/framebuffer.h"
37 #include "main/renderbuffer.h"
40 #define XMESA_RENDERBUFFER 0x1234
44 static volatile int mesaXErrorFlag
= 0;
47 * Catches potential Xlib errors.
50 mesaHandleXError(XMesaDisplay
*dpy
, XErrorEvent
*event
)
59 * Allocate a shared memory XImage back buffer for the given XMesaBuffer.
60 * Return: GL_TRUE if success, GL_FALSE if error
63 alloc_back_shm_ximage(XMesaBuffer b
, GLuint width
, GLuint height
)
66 * We have to do a _lot_ of error checking here to be sure we can
67 * really use the XSHM extension. It seems different servers trigger
68 * errors at different points if the extension won't work. Therefore
69 * we have to be very careful...
72 int (*old_handler
)(XMesaDisplay
*, XErrorEvent
*);
74 if (width
== 0 || height
== 0) {
75 /* this will be true the first time we're called on 'b' */
79 b
->backxrb
->ximage
= XShmCreateImage(b
->xm_visual
->display
,
80 b
->xm_visual
->visinfo
->visual
,
81 b
->xm_visual
->visinfo
->depth
,
82 ZPixmap
, NULL
, &b
->shminfo
,
84 if (b
->backxrb
->ximage
== NULL
) {
85 _mesa_warning(NULL
, "alloc_back_buffer: Shared memory error (XShmCreateImage), disabling.\n");
90 b
->shminfo
.shmid
= shmget(IPC_PRIVATE
, b
->backxrb
->ximage
->bytes_per_line
91 * b
->backxrb
->ximage
->height
, IPC_CREAT
|0777);
92 if (b
->shminfo
.shmid
< 0) {
93 _mesa_warning(NULL
, "shmget failed while allocating back buffer.\n");
94 XDestroyImage(b
->backxrb
->ximage
);
95 b
->backxrb
->ximage
= NULL
;
96 _mesa_warning(NULL
, "alloc_back_buffer: Shared memory error (shmget), disabling.\n");
101 b
->shminfo
.shmaddr
= b
->backxrb
->ximage
->data
102 = (char*)shmat(b
->shminfo
.shmid
, 0, 0);
103 if (b
->shminfo
.shmaddr
== (char *) -1) {
104 _mesa_warning(NULL
, "shmat() failed while allocating back buffer.\n");
105 XDestroyImage(b
->backxrb
->ximage
);
106 shmctl(b
->shminfo
.shmid
, IPC_RMID
, 0);
107 b
->backxrb
->ximage
= NULL
;
108 _mesa_warning(NULL
, "alloc_back_buffer: Shared memory error (shmat), disabling.\n");
113 b
->shminfo
.readOnly
= False
;
115 old_handler
= XSetErrorHandler(mesaHandleXError
);
116 /* This may trigger the X protocol error we're ready to catch: */
117 XShmAttach(b
->xm_visual
->display
, &b
->shminfo
);
118 XSync(b
->xm_visual
->display
, False
);
120 if (mesaXErrorFlag
) {
121 /* we are on a remote display, this error is normal, don't print it */
122 XFlush(b
->xm_visual
->display
);
124 XDestroyImage(b
->backxrb
->ximage
);
125 shmdt(b
->shminfo
.shmaddr
);
126 shmctl(b
->shminfo
.shmid
, IPC_RMID
, 0);
127 b
->backxrb
->ximage
= NULL
;
129 (void) XSetErrorHandler(old_handler
);
133 shmctl(b
->shminfo
.shmid
, IPC_RMID
, 0); /* nobody else needs it */
135 /* Finally, try an XShmPutImage to be really sure the extension works */
136 gc
= XCreateGC(b
->xm_visual
->display
, b
->frontxrb
->drawable
, 0, NULL
);
137 XShmPutImage(b
->xm_visual
->display
, b
->frontxrb
->drawable
, gc
,
138 b
->backxrb
->ximage
, 0, 0, 0, 0, 1, 1 /*one pixel*/, False
);
139 XSync(b
->xm_visual
->display
, False
);
140 XFreeGC(b
->xm_visual
->display
, gc
);
141 (void) XSetErrorHandler(old_handler
);
142 if (mesaXErrorFlag
) {
143 XFlush(b
->xm_visual
->display
);
145 XDestroyImage(b
->backxrb
->ximage
);
146 shmdt(b
->shminfo
.shmaddr
);
147 shmctl(b
->shminfo
.shmid
, IPC_RMID
, 0);
148 b
->backxrb
->ximage
= NULL
;
157 alloc_back_shm_ximage(XMesaBuffer b
, GLuint width
, GLuint height
)
159 /* Can't compile XSHM support */
167 * Setup an off-screen pixmap or Ximage to use as the back buffer.
168 * Input: b - the X/Mesa buffer
171 alloc_back_buffer(XMesaBuffer b
, GLuint width
, GLuint height
)
173 if (b
->db_mode
== BACK_XIMAGE
) {
174 /* Deallocate the old backxrb->ximage, if any */
175 if (b
->backxrb
->ximage
) {
176 #if defined(USE_XSHM)
178 XShmDetach(b
->xm_visual
->display
, &b
->shminfo
);
179 XDestroyImage(b
->backxrb
->ximage
);
180 shmdt(b
->shminfo
.shmaddr
);
184 XMesaDestroyImage(b
->backxrb
->ximage
);
185 b
->backxrb
->ximage
= NULL
;
188 if (width
== 0 || height
== 0)
191 /* Allocate new back buffer */
192 if (b
->shm
== 0 || !alloc_back_shm_ximage(b
, width
, height
)) {
193 /* Allocate a regular XImage for the back buffer. */
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 */
200 8, 0); /* pad, bytes_per_line */
201 if (!b
->backxrb
->ximage
) {
202 _mesa_warning(NULL
, "alloc_back_buffer: XCreateImage failed.\n");
205 b
->backxrb
->ximage
->data
= (char *) MALLOC(b
->backxrb
->ximage
->height
206 * b
->backxrb
->ximage
->bytes_per_line
);
207 if (!b
->backxrb
->ximage
->data
) {
208 _mesa_warning(NULL
, "alloc_back_buffer: MALLOC failed.\n");
209 XMesaDestroyImage(b
->backxrb
->ximage
);
210 b
->backxrb
->ximage
= NULL
;
213 b
->backxrb
->pixmap
= None
;
215 else if (b
->db_mode
== BACK_PIXMAP
) {
216 /* Free the old back pixmap */
217 if (b
->backxrb
->pixmap
) {
218 XMesaFreePixmap(b
->xm_visual
->display
, b
->backxrb
->pixmap
);
219 b
->backxrb
->pixmap
= 0;
222 if (width
> 0 && height
> 0) {
223 /* Allocate new back pixmap */
224 b
->backxrb
->pixmap
= XMesaCreatePixmap(b
->xm_visual
->display
,
225 b
->frontxrb
->drawable
,
227 GET_VISUAL_DEPTH(b
->xm_visual
));
230 b
->backxrb
->ximage
= NULL
;
231 b
->backxrb
->drawable
= b
->backxrb
->pixmap
;
237 xmesa_delete_renderbuffer(struct gl_renderbuffer
*rb
)
239 /* XXX Note: the ximage or Pixmap attached to this renderbuffer
240 * should probably get freed here, but that's currently done in
241 * XMesaDestroyBuffer().
248 * Reallocate renderbuffer storage for front color buffer.
249 * Called via gl_renderbuffer::AllocStorage()
252 xmesa_alloc_front_storage(struct gl_context
*ctx
, struct gl_renderbuffer
*rb
,
253 GLenum internalFormat
, GLuint width
, GLuint height
)
255 struct xmesa_renderbuffer
*xrb
= xmesa_renderbuffer(rb
);
257 /* just clear these to be sure we don't accidentally use them */
262 /* for the FLIP macro: */
263 xrb
->bottom
= height
- 1;
267 rb
->InternalFormat
= internalFormat
;
274 * Reallocate renderbuffer storage for back color buffer.
275 * Called via gl_renderbuffer::AllocStorage()
278 xmesa_alloc_back_storage(struct gl_context
*ctx
, struct gl_renderbuffer
*rb
,
279 GLenum internalFormat
, GLuint width
, GLuint height
)
281 struct xmesa_renderbuffer
*xrb
= xmesa_renderbuffer(rb
);
283 /* reallocate the back buffer XImage or Pixmap */
285 alloc_back_buffer(xrb
->Parent
, width
, height
);
287 /* same as front buffer */
288 /* XXX why is this here? */
289 (void) xmesa_alloc_front_storage(ctx
, rb
, internalFormat
, width
, height
);
293 /* Needed by PIXELADDR2 macro */
294 xrb
->width2
= xrb
->ximage
->bytes_per_line
/ 2;
295 xrb
->origin2
= (GLushort
*) xrb
->ximage
->data
+ xrb
->width2
* (height
- 1);
297 /* Needed by PIXELADDR3 macro */
298 xrb
->width3
= xrb
->ximage
->bytes_per_line
;
299 xrb
->origin3
= (GLubyte
*) xrb
->ximage
->data
+ xrb
->width3
* (height
- 1);
301 /* Needed by PIXELADDR4 macro */
302 xrb
->width4
= xrb
->ximage
->width
;
303 xrb
->origin4
= (GLuint
*) xrb
->ximage
->data
+ xrb
->width4
* (height
- 1);
306 /* out of memory or buffer size is 0 x 0 */
307 xrb
->width2
= xrb
->width3
= xrb
->width4
= 0;
318 * Used for allocating front/back renderbuffers for an X window.
320 struct xmesa_renderbuffer
*
321 xmesa_new_renderbuffer(struct gl_context
*ctx
, GLuint name
,
322 const struct xmesa_visual
*xmvis
,
323 GLboolean backBuffer
)
325 struct xmesa_renderbuffer
*xrb
= CALLOC_STRUCT(xmesa_renderbuffer
);
328 _mesa_init_renderbuffer(&xrb
->Base
, name
);
330 xrb
->Base
.Delete
= xmesa_delete_renderbuffer
;
332 xrb
->Base
.AllocStorage
= xmesa_alloc_back_storage
;
334 xrb
->Base
.AllocStorage
= xmesa_alloc_front_storage
;
336 xrb
->Base
.InternalFormat
= GL_RGBA
;
337 xrb
->Base
._BaseFormat
= GL_RGBA
;
338 xrb
->Base
.DataType
= GL_UNSIGNED_BYTE
;
339 xrb
->Base
.ClassID
= XMESA_RENDERBUFFER
;
341 switch (xmvis
->undithered_pf
) {
343 /* This will really only happen for pixmaps. We'll access the
344 * pixmap via a temporary XImage which will be 32bpp.
346 xrb
->Base
.Format
= MESA_FORMAT_XRGB8888
;
349 xrb
->Base
.Format
= MESA_FORMAT_ARGB8888
;
352 xrb
->Base
.Format
= MESA_FORMAT_RGBA8888_REV
;
355 xrb
->Base
.Format
= MESA_FORMAT_RGB565
;
358 _mesa_warning(ctx
, "Bad pixel format in xmesa_new_renderbuffer");
359 xrb
->Base
.Format
= MESA_FORMAT_ARGB8888
;
363 /* only need to set Red/Green/EtcBits fields for user-created RBs */
370 * Called via gl_framebuffer::Delete() method when this buffer
371 * is _really_ being deleted.
374 xmesa_delete_framebuffer(struct gl_framebuffer
*fb
)
376 XMesaBuffer b
= XMESA_BUFFER(fb
);
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);
387 XMesaFreeGC(b
->display
, b
->gc
);
389 XMesaFreeGC(b
->display
, b
->cleargc
);
391 XMesaFreeGC(b
->display
, b
->swapgc
);
393 if (fb
->Visual
.doubleBufferMode
) {
394 /* free back ximage/pixmap/shmregion */
395 if (b
->backxrb
->ximage
) {
396 #if defined(USE_XSHM)
398 XShmDetach( b
->display
, &b
->shminfo
);
399 XDestroyImage( b
->backxrb
->ximage
);
400 shmdt( b
->shminfo
.shmaddr
);
404 XMesaDestroyImage( b
->backxrb
->ximage
);
405 b
->backxrb
->ximage
= NULL
;
407 if (b
->backxrb
->pixmap
) {
408 XMesaFreePixmap( b
->display
, b
->backxrb
->pixmap
);
413 free( b
->rowimage
->data
);
414 b
->rowimage
->data
= NULL
;
415 XMesaDestroyImage( b
->rowimage
);
418 _mesa_free_framebuffer_data(fb
);
424 * Called via ctx->Driver.MapRenderbuffer()
427 xmesa_MapRenderbuffer(struct gl_context
*ctx
,
428 struct gl_renderbuffer
*rb
,
429 GLuint x
, GLuint y
, GLuint w
, GLuint h
,
431 GLubyte
**mapOut
, GLint
*rowStrideOut
)
433 struct xmesa_renderbuffer
*xrb
= xmesa_renderbuffer(rb
);
435 if (xrb
->Base
.ClassID
== XMESA_RENDERBUFFER
) {
436 XImage
*ximage
= xrb
->ximage
;
438 assert(!xrb
->map_mode
); /* only a single mapping allowed */
440 xrb
->map_mode
= mode
;
447 int y2
= rb
->Height
- y
- 1;
449 *mapOut
= (GLubyte
*) ximage
->data
450 + y2
* ximage
->bytes_per_line
451 + x
* ximage
->bits_per_pixel
/ 8;
454 /* this must be a pixmap/window renderbuffer */
455 int y2
= rb
->Height
- y
- h
;
459 /* read pixel data out of the pixmap/window into an XImage */
460 ximage
= XGetImage(xrb
->Parent
->display
,
461 xrb
->pixmap
, x
, y2
, w
, h
,
469 xrb
->map_ximage
= ximage
;
471 /* the first row of the OpenGL image is last row of the XImage */
472 *mapOut
= (GLubyte
*) ximage
->data
473 + (h
- 1) * ximage
->bytes_per_line
;
476 /* We return a negative stride here since XImage data is upside down
477 * with respect to OpenGL images.
479 *rowStrideOut
= -ximage
->bytes_per_line
;
483 /* otherwise, this is an ordinary malloc-based renderbuffer */
484 _mesa_map_soft_renderbuffer(ctx
, rb
, x
, y
, w
, h
, mode
,
485 mapOut
, rowStrideOut
);
490 * Called via ctx->Driver.UnmapRenderbuffer()
493 xmesa_UnmapRenderbuffer(struct gl_context
*ctx
, struct gl_renderbuffer
*rb
)
495 struct xmesa_renderbuffer
*xrb
= xmesa_renderbuffer(rb
);
497 if (xrb
->Base
.ClassID
== XMESA_RENDERBUFFER
) {
498 XImage
*ximage
= xrb
->ximage
;
501 /* this must be a pixmap/window renderbuffer */
503 assert(xrb
->map_ximage
);
504 if (xrb
->map_ximage
) {
505 if (xrb
->map_mode
& GL_MAP_WRITE_BIT
) {
506 /* put modified ximage data back into the pixmap/window */
507 int y2
= rb
->Height
- xrb
->map_y
- xrb
->map_h
;
508 GC gc
= XCreateGC(xrb
->Parent
->display
, xrb
->pixmap
, 0, NULL
);
510 XPutImage(xrb
->Parent
->display
,
511 xrb
->pixmap
, /* dest */
513 xrb
->map_ximage
, /* source */
515 xrb
->map_x
, y2
, /* dest x, y */
516 xrb
->map_w
, xrb
->map_h
); /* size */
518 XFreeGC(xrb
->Parent
->display
, gc
);
520 XMesaDestroyImage(xrb
->map_ximage
);
521 xrb
->map_ximage
= NULL
;
530 /* otherwise, this is an ordinary malloc-based renderbuffer */
531 _mesa_unmap_soft_renderbuffer(ctx
, rb
);