OSDN Git Service

mesa: remove outdated version lines in comments
[android-x86/external-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/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
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
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
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
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
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
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
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 *
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_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  */
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    _mesa_free_framebuffer_data(fb);
413    free(fb);
414 }
415
416
417 /**
418  * Called via ctx->Driver.MapRenderbuffer()
419  */
420 void
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 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
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