xm_buffer.c revision 9d4ab9a663d4088ec553edaae0eeafb746d2490d
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#include "swrast/s_renderbuffer.h"
39
40
41#define XMESA_RENDERBUFFER 0x1234
42
43
44#if defined(USE_XSHM)
45static volatile int mesaXErrorFlag = 0;
46
47/**
48 * Catches potential Xlib errors.
49 */
50static int
51mesaHandleXError(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 */
63static GLboolean
64alloc_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
157static GLboolean
158alloc_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 */
171static void
172alloc_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 = (char *) 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
237static void
238xmesa_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 */
252static GLboolean
253xmesa_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 */
278static GLboolean
279xmesa_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 */
321struct xmesa_renderbuffer *
322xmesa_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_XRGB8888;
347         break;
348      case PF_8A8R8G8B:
349         xrb->Base.Base.Format = MESA_FORMAT_ARGB8888;
350         break;
351      case PF_8A8B8G8R:
352         xrb->Base.Base.Format = MESA_FORMAT_RGBA8888_REV;
353         break;
354      case PF_5R6G5B:
355         xrb->Base.Base.Format = MESA_FORMAT_RGB565;
356         break;
357      default:
358         _mesa_warning(ctx, "Bad pixel format in xmesa_new_renderbuffer");
359         xrb->Base.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 */
373void
374xmesa_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 */
420void
421xmesa_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 = (char *) 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 */
515void
516xmesa_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
558