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/formats.h"
36 #include "main/framebuffer.h"
37 #include "main/renderbuffer.h"
38 #include "swrast/s_renderbuffer.h"
39 #include "util/u_memory.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
mesaHandleXError(XMesaDisplay * dpy,XErrorEvent * event)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
alloc_back_shm_ximage(XMesaBuffer b,GLuint width,GLuint height)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
alloc_back_shm_ximage(XMesaBuffer b,GLuint width,GLuint height)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
alloc_back_buffer(XMesaBuffer b,GLuint width,GLuint height)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
xmesa_delete_renderbuffer(struct gl_context * ctx,struct gl_renderbuffer * rb)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
xmesa_alloc_front_storage(struct gl_context * ctx,struct gl_renderbuffer * rb,GLenum internalFormat,GLuint width,GLuint height)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
xmesa_alloc_back_storage(struct gl_context * ctx,struct gl_renderbuffer * rb,GLenum internalFormat,GLuint width,GLuint height)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 *
xmesa_new_renderbuffer(struct gl_context * ctx,GLuint name,const struct xmesa_visual * xmvis,GLboolean backBuffer)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
xmesa_delete_framebuffer(struct gl_framebuffer * fb)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
xmesa_MapRenderbuffer(struct gl_context * ctx,struct gl_renderbuffer * rb,GLuint x,GLuint y,GLuint w,GLuint h,GLbitfield mode,GLubyte ** mapOut,GLint * rowStrideOut,bool flip_y)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
xmesa_UnmapRenderbuffer(struct gl_context * ctx,struct gl_renderbuffer * rb)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 
561