xm_buffer.c revision c1f859d4
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/framebuffer.h"
36#include "main/renderbuffer.h"
37
38
39#if defined(USE_XSHM) && !defined(XFree86Server)
40static volatile int mesaXErrorFlag = 0;
41
42/**
43 * Catches potential Xlib errors.
44 */
45static int
46mesaHandleXError(XMesaDisplay *dpy, XErrorEvent *event)
47{
48   (void) dpy;
49   (void) event;
50   mesaXErrorFlag = 1;
51   return 0;
52}
53
54/**
55 * Allocate a shared memory XImage back buffer for the given XMesaBuffer.
56 * Return:  GL_TRUE if success, GL_FALSE if error
57 */
58static GLboolean
59alloc_back_shm_ximage(XMesaBuffer b, GLuint width, GLuint height)
60{
61   /*
62    * We have to do a _lot_ of error checking here to be sure we can
63    * really use the XSHM extension.  It seems different servers trigger
64    * errors at different points if the extension won't work.  Therefore
65    * we have to be very careful...
66    */
67   GC gc;
68   int (*old_handler)(XMesaDisplay *, XErrorEvent *);
69
70   if (width == 0 || height == 0) {
71      /* this will be true the first time we're called on 'b' */
72      return GL_FALSE;
73   }
74
75   b->backxrb->ximage = XShmCreateImage(b->xm_visual->display,
76                                        b->xm_visual->visinfo->visual,
77                                        b->xm_visual->visinfo->depth,
78                                        ZPixmap, NULL, &b->shminfo,
79                                        width, height);
80   if (b->backxrb->ximage == NULL) {
81      _mesa_warning(NULL, "alloc_back_buffer: Shared memory error (XShmCreateImage), disabling.\n");
82      b->shm = 0;
83      return GL_FALSE;
84   }
85
86   b->shminfo.shmid = shmget(IPC_PRIVATE, b->backxrb->ximage->bytes_per_line
87			     * b->backxrb->ximage->height, IPC_CREAT|0777);
88   if (b->shminfo.shmid < 0) {
89      _mesa_warning(NULL, "shmget failed while allocating back buffer.\n");
90      XDestroyImage(b->backxrb->ximage);
91      b->backxrb->ximage = NULL;
92      _mesa_warning(NULL, "alloc_back_buffer: Shared memory error (shmget), disabling.\n");
93      b->shm = 0;
94      return GL_FALSE;
95   }
96
97   b->shminfo.shmaddr = b->backxrb->ximage->data
98                      = (char*)shmat(b->shminfo.shmid, 0, 0);
99   if (b->shminfo.shmaddr == (char *) -1) {
100      _mesa_warning(NULL, "shmat() failed while allocating back buffer.\n");
101      XDestroyImage(b->backxrb->ximage);
102      shmctl(b->shminfo.shmid, IPC_RMID, 0);
103      b->backxrb->ximage = NULL;
104      _mesa_warning(NULL, "alloc_back_buffer: Shared memory error (shmat), disabling.\n");
105      b->shm = 0;
106      return GL_FALSE;
107   }
108
109   b->shminfo.readOnly = False;
110   mesaXErrorFlag = 0;
111   old_handler = XSetErrorHandler(mesaHandleXError);
112   /* This may trigger the X protocol error we're ready to catch: */
113   XShmAttach(b->xm_visual->display, &b->shminfo);
114   XSync(b->xm_visual->display, False);
115
116   if (mesaXErrorFlag) {
117      /* we are on a remote display, this error is normal, don't print it */
118      XFlush(b->xm_visual->display);
119      mesaXErrorFlag = 0;
120      XDestroyImage(b->backxrb->ximage);
121      shmdt(b->shminfo.shmaddr);
122      shmctl(b->shminfo.shmid, IPC_RMID, 0);
123      b->backxrb->ximage = NULL;
124      b->shm = 0;
125      (void) XSetErrorHandler(old_handler);
126      return GL_FALSE;
127   }
128
129   shmctl(b->shminfo.shmid, IPC_RMID, 0); /* nobody else needs it */
130
131   /* Finally, try an XShmPutImage to be really sure the extension works */
132   gc = XCreateGC(b->xm_visual->display, b->frontxrb->drawable, 0, NULL);
133   XShmPutImage(b->xm_visual->display, b->frontxrb->drawable, gc,
134		 b->backxrb->ximage, 0, 0, 0, 0, 1, 1 /*one pixel*/, False);
135   XSync(b->xm_visual->display, False);
136   XFreeGC(b->xm_visual->display, gc);
137   (void) XSetErrorHandler(old_handler);
138   if (mesaXErrorFlag) {
139      XFlush(b->xm_visual->display);
140      mesaXErrorFlag = 0;
141      XDestroyImage(b->backxrb->ximage);
142      shmdt(b->shminfo.shmaddr);
143      shmctl(b->shminfo.shmid, IPC_RMID, 0);
144      b->backxrb->ximage = NULL;
145      b->shm = 0;
146      return GL_FALSE;
147   }
148
149   return GL_TRUE;
150}
151#else
152static GLboolean
153alloc_back_shm_ximage(XMesaBuffer b, GLuint width, GLuint height)
154{
155   /* Can't compile XSHM support */
156   return GL_FALSE;
157}
158#endif
159
160
161
162/**
163 * Setup an off-screen pixmap or Ximage to use as the back buffer.
164 * Input:  b - the X/Mesa buffer
165 */
166static void
167alloc_back_buffer(XMesaBuffer b, GLuint width, GLuint height)
168{
169   if (b->db_mode == BACK_XIMAGE) {
170      /* Deallocate the old backxrb->ximage, if any */
171      if (b->backxrb->ximage) {
172#if defined(USE_XSHM) && !defined(XFree86Server)
173	 if (b->shm) {
174	    XShmDetach(b->xm_visual->display, &b->shminfo);
175	    XDestroyImage(b->backxrb->ximage);
176	    shmdt(b->shminfo.shmaddr);
177	 }
178	 else
179#endif
180	   XMesaDestroyImage(b->backxrb->ximage);
181	 b->backxrb->ximage = NULL;
182      }
183
184      if (width == 0 || height == 0)
185         return;
186
187      /* Allocate new back buffer */
188      if (b->shm == 0 || !alloc_back_shm_ximage(b, width, height)) {
189	 /* Allocate a regular XImage for the back buffer. */
190#ifdef XFree86Server
191	 b->backxrb->ximage = XMesaCreateImage(b->xm_visual->BitsPerPixel,
192                                               width, height, NULL);
193#else
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#endif
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_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   _mesa_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(GLcontext *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->origin1 = NULL;
260   xrb->origin2 = NULL;
261   xrb->origin3 = NULL;
262   xrb->origin4 = NULL;
263
264   /* for the FLIP macro: */
265   xrb->bottom = height - 1;
266
267   rb->Width = width;
268   rb->Height = height;
269   rb->InternalFormat = internalFormat;
270
271   return GL_TRUE;
272}
273
274
275/**
276 * Reallocate renderbuffer storage for back color buffer.
277 * Called via gl_renderbuffer::AllocStorage()
278 */
279static GLboolean
280xmesa_alloc_back_storage(GLcontext *ctx, struct gl_renderbuffer *rb,
281                         GLenum internalFormat, GLuint width, GLuint height)
282{
283   struct xmesa_renderbuffer *xrb = xmesa_renderbuffer(rb);
284
285   /* reallocate the back buffer XImage or Pixmap */
286   assert(xrb->Parent);
287   alloc_back_buffer(xrb->Parent, width, height);
288
289   /* same as front buffer */
290   /* XXX why is this here? */
291   (void) xmesa_alloc_front_storage(ctx, rb, internalFormat, width, height);
292
293   /* plus... */
294   if (xrb->ximage) {
295      /* Needed by PIXELADDR1 macro */
296      xrb->width1 = xrb->ximage->bytes_per_line;
297      xrb->origin1 = (GLubyte *) xrb->ximage->data + xrb->width1 * (height - 1);
298
299      /* Needed by PIXELADDR2 macro */
300      xrb->width2 = xrb->ximage->bytes_per_line / 2;
301      xrb->origin2 = (GLushort *) xrb->ximage->data + xrb->width2 * (height - 1);
302
303      /* Needed by PIXELADDR3 macro */
304      xrb->width3 = xrb->ximage->bytes_per_line;
305      xrb->origin3 = (GLubyte *) xrb->ximage->data + xrb->width3 * (height - 1);
306
307      /* Needed by PIXELADDR4 macro */
308      xrb->width4 = xrb->ximage->width;
309      xrb->origin4 = (GLuint *) xrb->ximage->data + xrb->width4 * (height - 1);
310   }
311   else {
312      /* out of memory or buffer size is 0 x 0 */
313      xrb->width1 = xrb->width2 = xrb->width3 = xrb->width4 = 0;
314      xrb->origin1 = NULL;
315      xrb->origin2 = NULL;
316      xrb->origin3 = NULL;
317      xrb->origin4 = NULL;
318   }
319
320   return GL_TRUE;
321}
322
323
324struct xmesa_renderbuffer *
325xmesa_new_renderbuffer(GLcontext *ctx, GLuint name, const GLvisual *visual,
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, name);
332
333      xrb->Base.Delete = xmesa_delete_renderbuffer;
334      if (backBuffer)
335         xrb->Base.AllocStorage = xmesa_alloc_back_storage;
336      else
337         xrb->Base.AllocStorage = xmesa_alloc_front_storage;
338
339      if (visual->rgbMode) {
340         xrb->Base.InternalFormat = GL_RGBA;
341         xrb->Base._BaseFormat = GL_RGBA;
342         xrb->Base.DataType = GL_UNSIGNED_BYTE;
343         xrb->Base.RedBits = visual->redBits;
344         xrb->Base.GreenBits = visual->greenBits;
345         xrb->Base.BlueBits = visual->blueBits;
346         xrb->Base.AlphaBits = visual->alphaBits;
347      }
348      else {
349         xrb->Base.InternalFormat = GL_COLOR_INDEX;
350         xrb->Base._BaseFormat = GL_COLOR_INDEX;
351         xrb->Base.DataType = GL_UNSIGNED_INT;
352         xrb->Base.IndexBits = visual->indexBits;
353      }
354      /* only need to set Red/Green/EtcBits fields for user-created RBs */
355   }
356   return xrb;
357}
358
359
360/**
361 * Called via gl_framebuffer::Delete() method when this buffer
362 * is _really_ being deleted.
363 */
364void
365xmesa_delete_framebuffer(struct gl_framebuffer *fb)
366{
367   XMesaBuffer b = XMESA_BUFFER(fb);
368
369   if (b->num_alloced > 0) {
370      /* If no other buffer uses this X colormap then free the colors. */
371      if (!xmesa_find_buffer(b->display, b->cmap, b)) {
372#ifdef XFree86Server
373         int client = 0;
374         if (b->frontxrb->drawable)
375            client = CLIENT_ID(b->frontxrb->drawable->id);
376         (void)FreeColors(b->cmap, client,
377                          b->num_alloced, b->alloced_colors, 0);
378#else
379         XFreeColors(b->display, b->cmap,
380                     b->alloced_colors, b->num_alloced, 0);
381#endif
382      }
383   }
384
385   if (b->gc)
386      XMesaFreeGC(b->display, b->gc);
387   if (b->cleargc)
388      XMesaFreeGC(b->display, b->cleargc);
389   if (b->swapgc)
390      XMesaFreeGC(b->display, b->swapgc);
391
392   if (fb->Visual.doubleBufferMode) {
393      /* free back ximage/pixmap/shmregion */
394      if (b->backxrb->ximage) {
395#if defined(USE_XSHM) && !defined(XFree86Server)
396         if (b->shm) {
397            XShmDetach( b->display, &b->shminfo );
398            XDestroyImage( b->backxrb->ximage );
399            shmdt( b->shminfo.shmaddr );
400         }
401         else
402#endif
403            XMesaDestroyImage( b->backxrb->ximage );
404         b->backxrb->ximage = NULL;
405      }
406      if (b->backxrb->pixmap) {
407         XMesaFreePixmap( b->display, b->backxrb->pixmap );
408         if (b->xm_visual->hpcr_clear_flag) {
409            XMesaFreePixmap( b->display,
410                             b->xm_visual->hpcr_clear_pixmap );
411            XMesaDestroyImage( b->xm_visual->hpcr_clear_ximage );
412         }
413      }
414   }
415
416   if (b->rowimage) {
417      _mesa_free( b->rowimage->data );
418      b->rowimage->data = NULL;
419      XMesaDestroyImage( b->rowimage );
420   }
421
422   _mesa_free_framebuffer_data(fb);
423   _mesa_free(fb);
424}
425