0da08a6e64d483fbcc2f5251acb60ee5f3cf1437
[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/errors.h"
35 #include "main/imports.h"
36 #include "main/formats.h"
37 #include "main/framebuffer.h"
38 #include "main/renderbuffer.h"
39 #include "swrast/s_renderbuffer.h"
40
41
42 #define XMESA_RENDERBUFFER 0x1234
43
44
45 #if defined(USE_XSHM)
46 static volatile int mesaXErrorFlag = 0;
47
48 /**
49 * Catches potential Xlib errors.
50 */
51 static int
52 mesaHandleXError(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 */
64 static GLboolean
65 alloc_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
159 static GLboolean
160 alloc_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 */
173 static void
174 alloc_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
239 static void
240 xmesa_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 */
254 static GLboolean
255 xmesa_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 */
280 static GLboolean
281 xmesa_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 */
323 struct xmesa_renderbuffer *
324 xmesa_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 */
375 void
376 xmesa_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 */
422 void
423 xmesa_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 */
518 void
519 xmesa_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