• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
mesaHandleXError(XMesaDisplay * dpy,XErrorEvent * event)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
alloc_back_shm_ximage(XMesaBuffer b,GLuint width,GLuint height)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
alloc_back_shm_ximage(XMesaBuffer b,GLuint width,GLuint height)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
alloc_back_buffer(XMesaBuffer b,GLuint width,GLuint height)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
xmesa_delete_renderbuffer(struct gl_context * ctx,struct gl_renderbuffer * rb)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
xmesa_alloc_front_storage(struct gl_context * ctx,struct gl_renderbuffer * rb,GLenum internalFormat,GLuint width,GLuint height)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
xmesa_alloc_back_storage(struct gl_context * ctx,struct gl_renderbuffer * rb,GLenum internalFormat,GLuint width,GLuint height)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 *
xmesa_new_renderbuffer(struct gl_context * ctx,GLuint name,const struct xmesa_visual * xmvis,GLboolean backBuffer)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
xmesa_delete_framebuffer(struct gl_framebuffer * fb)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
xmesa_MapRenderbuffer(struct gl_context * ctx,struct gl_renderbuffer * rb,GLuint x,GLuint y,GLuint w,GLuint h,GLbitfield mode,GLubyte ** mapOut,GLint * rowStrideOut)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 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
xmesa_UnmapRenderbuffer(struct gl_context * ctx,struct gl_renderbuffer * rb)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 
558