11b7c244ea913178a8f3869b12b3d1181a497b98
[mesa.git] / src / mesa / drivers / x11 / xm_buffer.c
1 /*
2 * Mesa 3-D graphics library
3 * Version: 6.5.2
4 *
5 * Copyright (C) 1999-2006 Brian Paul All Rights Reserved.
6 *
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:
13 *
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
16 *
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.
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
39
40 #define XMESA_RENDERBUFFER 0x1234
41
42
43 #if defined(USE_XSHM)
44 static volatile int mesaXErrorFlag = 0;
45
46 /**
47 * Catches potential Xlib errors.
48 */
49 static int
50 mesaHandleXError(XMesaDisplay *dpy, XErrorEvent *event)
51 {
52 (void) dpy;
53 (void) event;
54 mesaXErrorFlag = 1;
55 return 0;
56 }
57
58 /**
59 * Allocate a shared memory XImage back buffer for the given XMesaBuffer.
60 * Return: GL_TRUE if success, GL_FALSE if error
61 */
62 static GLboolean
63 alloc_back_shm_ximage(XMesaBuffer b, GLuint width, GLuint height)
64 {
65 /*
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...
70 */
71 GC gc;
72 int (*old_handler)(XMesaDisplay *, XErrorEvent *);
73
74 if (width == 0 || height == 0) {
75 /* this will be true the first time we're called on 'b' */
76 return GL_FALSE;
77 }
78
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,
83 width, height);
84 if (b->backxrb->ximage == NULL) {
85 _mesa_warning(NULL, "alloc_back_buffer: Shared memory error (XShmCreateImage), disabling.\n");
86 b->shm = 0;
87 return GL_FALSE;
88 }
89
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");
97 b->shm = 0;
98 return GL_FALSE;
99 }
100
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");
109 b->shm = 0;
110 return GL_FALSE;
111 }
112
113 b->shminfo.readOnly = False;
114 mesaXErrorFlag = 0;
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);
119
120 if (mesaXErrorFlag) {
121 /* we are on a remote display, this error is normal, don't print it */
122 XFlush(b->xm_visual->display);
123 mesaXErrorFlag = 0;
124 XDestroyImage(b->backxrb->ximage);
125 shmdt(b->shminfo.shmaddr);
126 shmctl(b->shminfo.shmid, IPC_RMID, 0);
127 b->backxrb->ximage = NULL;
128 b->shm = 0;
129 (void) XSetErrorHandler(old_handler);
130 return GL_FALSE;
131 }
132
133 shmctl(b->shminfo.shmid, IPC_RMID, 0); /* nobody else needs it */
134
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);
144 mesaXErrorFlag = 0;
145 XDestroyImage(b->backxrb->ximage);
146 shmdt(b->shminfo.shmaddr);
147 shmctl(b->shminfo.shmid, IPC_RMID, 0);
148 b->backxrb->ximage = NULL;
149 b->shm = 0;
150 return GL_FALSE;
151 }
152
153 return GL_TRUE;
154 }
155 #else
156 static GLboolean
157 alloc_back_shm_ximage(XMesaBuffer b, GLuint width, GLuint height)
158 {
159 /* Can't compile XSHM support */
160 return GL_FALSE;
161 }
162 #endif
163
164
165
166 /**
167 * Setup an off-screen pixmap or Ximage to use as the back buffer.
168 * Input: b - the X/Mesa buffer
169 */
170 static void
171 alloc_back_buffer(XMesaBuffer b, GLuint width, GLuint height)
172 {
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)
177 if (b->shm) {
178 XShmDetach(b->xm_visual->display, &b->shminfo);
179 XDestroyImage(b->backxrb->ximage);
180 shmdt(b->shminfo.shmaddr);
181 }
182 else
183 #endif
184 XMesaDestroyImage(b->backxrb->ximage);
185 b->backxrb->ximage = NULL;
186 }
187
188 if (width == 0 || height == 0)
189 return;
190
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 */
198 NULL,
199 width, height,
200 8, 0); /* pad, bytes_per_line */
201 if (!b->backxrb->ximage) {
202 _mesa_warning(NULL, "alloc_back_buffer: XCreateImage failed.\n");
203 return;
204 }
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;
211 }
212 }
213 b->backxrb->pixmap = None;
214 }
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;
220 }
221
222 if (width > 0 && height > 0) {
223 /* Allocate new back pixmap */
224 b->backxrb->pixmap = XMesaCreatePixmap(b->xm_visual->display,
225 b->frontxrb->drawable,
226 width, height,
227 GET_VISUAL_DEPTH(b->xm_visual));
228 }
229
230 b->backxrb->ximage = NULL;
231 b->backxrb->drawable = b->backxrb->pixmap;
232 }
233 }
234
235
236 static void
237 xmesa_delete_renderbuffer(struct gl_renderbuffer *rb)
238 {
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().
242 */
243 free(rb);
244 }
245
246
247 /**
248 * Reallocate renderbuffer storage for front color buffer.
249 * Called via gl_renderbuffer::AllocStorage()
250 */
251 static GLboolean
252 xmesa_alloc_front_storage(struct gl_context *ctx, struct gl_renderbuffer *rb,
253 GLenum internalFormat, GLuint width, GLuint height)
254 {
255 struct xmesa_renderbuffer *xrb = xmesa_renderbuffer(rb);
256
257 /* just clear these to be sure we don't accidentally use them */
258 xrb->origin2 = NULL;
259 xrb->origin3 = NULL;
260 xrb->origin4 = NULL;
261
262 /* for the FLIP macro: */
263 xrb->bottom = height - 1;
264
265 rb->Width = width;
266 rb->Height = height;
267 rb->InternalFormat = internalFormat;
268
269 return GL_TRUE;
270 }
271
272
273 /**
274 * Reallocate renderbuffer storage for back color buffer.
275 * Called via gl_renderbuffer::AllocStorage()
276 */
277 static GLboolean
278 xmesa_alloc_back_storage(struct gl_context *ctx, struct gl_renderbuffer *rb,
279 GLenum internalFormat, GLuint width, GLuint height)
280 {
281 struct xmesa_renderbuffer *xrb = xmesa_renderbuffer(rb);
282
283 /* reallocate the back buffer XImage or Pixmap */
284 assert(xrb->Parent);
285 alloc_back_buffer(xrb->Parent, width, height);
286
287 /* same as front buffer */
288 /* XXX why is this here? */
289 (void) xmesa_alloc_front_storage(ctx, rb, internalFormat, width, height);
290
291 /* plus... */
292 if (xrb->ximage) {
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);
296
297 /* Needed by PIXELADDR3 macro */
298 xrb->width3 = xrb->ximage->bytes_per_line;
299 xrb->origin3 = (GLubyte *) xrb->ximage->data + xrb->width3 * (height - 1);
300
301 /* Needed by PIXELADDR4 macro */
302 xrb->width4 = xrb->ximage->width;
303 xrb->origin4 = (GLuint *) xrb->ximage->data + xrb->width4 * (height - 1);
304 }
305 else {
306 /* out of memory or buffer size is 0 x 0 */
307 xrb->width2 = xrb->width3 = xrb->width4 = 0;
308 xrb->origin2 = NULL;
309 xrb->origin3 = NULL;
310 xrb->origin4 = NULL;
311 }
312
313 return GL_TRUE;
314 }
315
316
317 /**
318 * Used for allocating front/back renderbuffers for an X window.
319 */
320 struct xmesa_renderbuffer *
321 xmesa_new_renderbuffer(struct gl_context *ctx, GLuint name,
322 const struct xmesa_visual *xmvis,
323 GLboolean backBuffer)
324 {
325 struct xmesa_renderbuffer *xrb = CALLOC_STRUCT(xmesa_renderbuffer);
326 if (xrb) {
327 GLuint name = 0;
328 _mesa_init_renderbuffer(&xrb->Base, name);
329
330 xrb->Base.Delete = xmesa_delete_renderbuffer;
331 if (backBuffer)
332 xrb->Base.AllocStorage = xmesa_alloc_back_storage;
333 else
334 xrb->Base.AllocStorage = xmesa_alloc_front_storage;
335
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;
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.Format = MESA_FORMAT_XRGB8888;
347 break;
348 case PF_8A8R8G8B:
349 xrb->Base.Format = MESA_FORMAT_ARGB8888;
350 break;
351 case PF_8A8B8G8R:
352 xrb->Base.Format = MESA_FORMAT_RGBA8888_REV;
353 break;
354 case PF_5R6G5B:
355 xrb->Base.Format = MESA_FORMAT_RGB565;
356 break;
357 default:
358 _mesa_warning(ctx, "Bad pixel format in xmesa_new_renderbuffer");
359 xrb->Base.Format = MESA_FORMAT_ARGB8888;
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 if (b->rowimage) {
413 free( b->rowimage->data );
414 b->rowimage->data = NULL;
415 XMesaDestroyImage( b->rowimage );
416 }
417
418 _mesa_free_framebuffer_data(fb);
419 free(fb);
420 }
421
422
423 /**
424 * Called via ctx->Driver.MapRenderbuffer()
425 */
426 void
427 xmesa_MapRenderbuffer(struct gl_context *ctx,
428 struct gl_renderbuffer *rb,
429 GLuint x, GLuint y, GLuint w, GLuint h,
430 GLbitfield mode,
431 GLubyte **mapOut, GLint *rowStrideOut)
432 {
433 struct xmesa_renderbuffer *xrb = xmesa_renderbuffer(rb);
434
435 if (xrb->Base.ClassID == XMESA_RENDERBUFFER) {
436 XImage *ximage = xrb->ximage;
437
438 assert(!xrb->map_mode); /* only a single mapping allowed */
439
440 xrb->map_mode = mode;
441 xrb->map_x = x;
442 xrb->map_y = y;
443 xrb->map_w = w;
444 xrb->map_h = h;
445
446 if (ximage) {
447 int y2 = rb->Height - y - 1;
448
449 *mapOut = (GLubyte *) ximage->data
450 + y2 * ximage->bytes_per_line
451 + x * ximage->bits_per_pixel / 8;
452 }
453 else {
454 /* this must be a pixmap/window renderbuffer */
455 int y2 = rb->Height - y - h;
456
457 assert(xrb->pixmap);
458
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,
462 AllPlanes, ZPixmap);
463 if (!ximage) {
464 *mapOut = NULL;
465 *rowStrideOut = 0;
466 return;
467 }
468
469 xrb->map_ximage = ximage;
470
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;
474 }
475
476 /* We return a negative stride here since XImage data is upside down
477 * with respect to OpenGL images.
478 */
479 *rowStrideOut = -ximage->bytes_per_line;
480 return;
481 }
482
483 /* otherwise, this is an ordinary malloc-based renderbuffer */
484 _mesa_map_soft_renderbuffer(ctx, rb, x, y, w, h, mode,
485 mapOut, rowStrideOut);
486 }
487
488
489 /**
490 * Called via ctx->Driver.UnmapRenderbuffer()
491 */
492 void
493 xmesa_UnmapRenderbuffer(struct gl_context *ctx, struct gl_renderbuffer *rb)
494 {
495 struct xmesa_renderbuffer *xrb = xmesa_renderbuffer(rb);
496
497 if (xrb->Base.ClassID == XMESA_RENDERBUFFER) {
498 XImage *ximage = xrb->ximage;
499
500 if (!ximage) {
501 /* this must be a pixmap/window renderbuffer */
502 assert(xrb->pixmap);
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);
509
510 XPutImage(xrb->Parent->display,
511 xrb->pixmap, /* dest */
512 gc,
513 xrb->map_ximage, /* source */
514 0, 0, /* src x, y */
515 xrb->map_x, y2, /* dest x, y */
516 xrb->map_w, xrb->map_h); /* size */
517
518 XFreeGC(xrb->Parent->display, gc);
519 }
520 XMesaDestroyImage(xrb->map_ximage);
521 xrb->map_ximage = NULL;
522 }
523 }
524
525 xrb->map_mode = 0x0;
526
527 return;
528 }
529
530 /* otherwise, this is an ordinary malloc-based renderbuffer */
531 _mesa_unmap_soft_renderbuffer(ctx, rb);
532 }
533
534