f6e14270a47312c3185395a5ef024ef0f076a105
[mesa.git] / src / mesa / drivers / x11 / xm_buffer.c
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/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 = 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_B8G8R8X8_UNORM;
347 break;
348 case PF_8A8R8G8B:
349 xrb->Base.Base.Format = MESA_FORMAT_B8G8R8A8_UNORM;
350 break;
351 case PF_8A8B8G8R:
352 xrb->Base.Base.Format = MESA_FORMAT_R8G8B8A8_UNORM;
353 break;
354 case PF_5R6G5B:
355 xrb->Base.Base.Format = MESA_FORMAT_B5G6R5_UNORM;
356 break;
357 default:
358 _mesa_warning(ctx, "Bad pixel format in xmesa_new_renderbuffer");
359 xrb->Base.Base.Format = MESA_FORMAT_B8G8R8A8_UNORM;
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 = 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