xm_buffer.c revision 4a49301e
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
39
40#if defined(USE_XSHM) && !defined(XFree86Server)
41static volatile int mesaXErrorFlag = 0;
42
43/**
44 * Catches potential Xlib errors.
45 */
46static int
47mesaHandleXError(XMesaDisplay *dpy, XErrorEvent *event)
48{
49   (void) dpy;
50   (void) event;
51   mesaXErrorFlag = 1;
52   return 0;
53}
54
55/**
56 * Allocate a shared memory XImage back buffer for the given XMesaBuffer.
57 * Return:  GL_TRUE if success, GL_FALSE if error
58 */
59static GLboolean
60alloc_back_shm_ximage(XMesaBuffer b, GLuint width, GLuint height)
61{
62   /*
63    * We have to do a _lot_ of error checking here to be sure we can
64    * really use the XSHM extension.  It seems different servers trigger
65    * errors at different points if the extension won't work.  Therefore
66    * we have to be very careful...
67    */
68   GC gc;
69   int (*old_handler)(XMesaDisplay *, XErrorEvent *);
70
71   if (width == 0 || height == 0) {
72      /* this will be true the first time we're called on 'b' */
73      return GL_FALSE;
74   }
75
76   b->backxrb->ximage = XShmCreateImage(b->xm_visual->display,
77                                        b->xm_visual->visinfo->visual,
78                                        b->xm_visual->visinfo->depth,
79                                        ZPixmap, NULL, &b->shminfo,
80                                        width, height);
81   if (b->backxrb->ximage == NULL) {
82      _mesa_warning(NULL, "alloc_back_buffer: Shared memory error (XShmCreateImage), disabling.\n");
83      b->shm = 0;
84      return GL_FALSE;
85   }
86
87   b->shminfo.shmid = shmget(IPC_PRIVATE, b->backxrb->ximage->bytes_per_line
88			     * b->backxrb->ximage->height, IPC_CREAT|0777);
89   if (b->shminfo.shmid < 0) {
90      _mesa_warning(NULL, "shmget failed while allocating back buffer.\n");
91      XDestroyImage(b->backxrb->ximage);
92      b->backxrb->ximage = NULL;
93      _mesa_warning(NULL, "alloc_back_buffer: Shared memory error (shmget), disabling.\n");
94      b->shm = 0;
95      return GL_FALSE;
96   }
97
98   b->shminfo.shmaddr = b->backxrb->ximage->data
99                      = (char*)shmat(b->shminfo.shmid, 0, 0);
100   if (b->shminfo.shmaddr == (char *) -1) {
101      _mesa_warning(NULL, "shmat() failed while allocating back buffer.\n");
102      XDestroyImage(b->backxrb->ximage);
103      shmctl(b->shminfo.shmid, IPC_RMID, 0);
104      b->backxrb->ximage = NULL;
105      _mesa_warning(NULL, "alloc_back_buffer: Shared memory error (shmat), disabling.\n");
106      b->shm = 0;
107      return GL_FALSE;
108   }
109
110   b->shminfo.readOnly = False;
111   mesaXErrorFlag = 0;
112   old_handler = XSetErrorHandler(mesaHandleXError);
113   /* This may trigger the X protocol error we're ready to catch: */
114   XShmAttach(b->xm_visual->display, &b->shminfo);
115   XSync(b->xm_visual->display, False);
116
117   if (mesaXErrorFlag) {
118      /* we are on a remote display, this error is normal, don't print it */
119      XFlush(b->xm_visual->display);
120      mesaXErrorFlag = 0;
121      XDestroyImage(b->backxrb->ximage);
122      shmdt(b->shminfo.shmaddr);
123      shmctl(b->shminfo.shmid, IPC_RMID, 0);
124      b->backxrb->ximage = NULL;
125      b->shm = 0;
126      (void) XSetErrorHandler(old_handler);
127      return GL_FALSE;
128   }
129
130   shmctl(b->shminfo.shmid, IPC_RMID, 0); /* nobody else needs it */
131
132   /* Finally, try an XShmPutImage to be really sure the extension works */
133   gc = XCreateGC(b->xm_visual->display, b->frontxrb->drawable, 0, NULL);
134   XShmPutImage(b->xm_visual->display, b->frontxrb->drawable, gc,
135		 b->backxrb->ximage, 0, 0, 0, 0, 1, 1 /*one pixel*/, False);
136   XSync(b->xm_visual->display, False);
137   XFreeGC(b->xm_visual->display, gc);
138   (void) XSetErrorHandler(old_handler);
139   if (mesaXErrorFlag) {
140      XFlush(b->xm_visual->display);
141      mesaXErrorFlag = 0;
142      XDestroyImage(b->backxrb->ximage);
143      shmdt(b->shminfo.shmaddr);
144      shmctl(b->shminfo.shmid, IPC_RMID, 0);
145      b->backxrb->ximage = NULL;
146      b->shm = 0;
147      return GL_FALSE;
148   }
149
150   return GL_TRUE;
151}
152#else
153static GLboolean
154alloc_back_shm_ximage(XMesaBuffer b, GLuint width, GLuint height)
155{
156   /* Can't compile XSHM support */
157   return GL_FALSE;
158}
159#endif
160
161
162
163/**
164 * Setup an off-screen pixmap or Ximage to use as the back buffer.
165 * Input:  b - the X/Mesa buffer
166 */
167static void
168alloc_back_buffer(XMesaBuffer b, GLuint width, GLuint height)
169{
170   if (b->db_mode == BACK_XIMAGE) {
171      /* Deallocate the old backxrb->ximage, if any */
172      if (b->backxrb->ximage) {
173#if defined(USE_XSHM) && !defined(XFree86Server)
174	 if (b->shm) {
175	    XShmDetach(b->xm_visual->display, &b->shminfo);
176	    XDestroyImage(b->backxrb->ximage);
177	    shmdt(b->shminfo.shmaddr);
178	 }
179	 else
180#endif
181	   XMesaDestroyImage(b->backxrb->ximage);
182	 b->backxrb->ximage = NULL;
183      }
184
185      if (width == 0 || height == 0)
186         return;
187
188      /* Allocate new back buffer */
189      if (b->shm == 0 || !alloc_back_shm_ximage(b, width, height)) {
190	 /* Allocate a regular XImage for the back buffer. */
191#ifdef XFree86Server
192	 b->backxrb->ximage = XMesaCreateImage(b->xm_visual->BitsPerPixel,
193                                               width, height, NULL);
194#else
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#endif
203	 if (!b->backxrb->ximage) {
204	    _mesa_warning(NULL, "alloc_back_buffer: XCreateImage failed.\n");
205            return;
206	 }
207         b->backxrb->ximage->data = (char *) MALLOC(b->backxrb->ximage->height
208                                        * b->backxrb->ximage->bytes_per_line);
209         if (!b->backxrb->ximage->data) {
210            _mesa_warning(NULL, "alloc_back_buffer: MALLOC failed.\n");
211            XMesaDestroyImage(b->backxrb->ximage);
212            b->backxrb->ximage = NULL;
213         }
214      }
215      b->backxrb->pixmap = None;
216   }
217   else if (b->db_mode == BACK_PIXMAP) {
218      /* Free the old back pixmap */
219      if (b->backxrb->pixmap) {
220         XMesaFreePixmap(b->xm_visual->display, b->backxrb->pixmap);
221         b->backxrb->pixmap = 0;
222      }
223
224      if (width > 0 && height > 0) {
225         /* Allocate new back pixmap */
226         b->backxrb->pixmap = XMesaCreatePixmap(b->xm_visual->display,
227                                                b->frontxrb->drawable,
228                                                width, height,
229                                                GET_VISUAL_DEPTH(b->xm_visual));
230      }
231
232      b->backxrb->ximage = NULL;
233      b->backxrb->drawable = b->backxrb->pixmap;
234   }
235}
236
237
238static void
239xmesa_delete_renderbuffer(struct gl_renderbuffer *rb)
240{
241   /* XXX Note: the ximage or Pixmap attached to this renderbuffer
242    * should probably get freed here, but that's currently done in
243    * XMesaDestroyBuffer().
244    */
245   _mesa_free(rb);
246}
247
248
249/**
250 * Reallocate renderbuffer storage for front color buffer.
251 * Called via gl_renderbuffer::AllocStorage()
252 */
253static GLboolean
254xmesa_alloc_front_storage(GLcontext *ctx, struct gl_renderbuffer *rb,
255                          GLenum internalFormat, GLuint width, GLuint height)
256{
257   struct xmesa_renderbuffer *xrb = xmesa_renderbuffer(rb);
258
259   /* just clear these to be sure we don't accidentally use them */
260   xrb->origin1 = NULL;
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 */
280static GLboolean
281xmesa_alloc_back_storage(GLcontext *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 PIXELADDR1 macro */
297      xrb->width1 = xrb->ximage->bytes_per_line;
298      xrb->origin1 = (GLubyte *) xrb->ximage->data + xrb->width1 * (height - 1);
299
300      /* Needed by PIXELADDR2 macro */
301      xrb->width2 = xrb->ximage->bytes_per_line / 2;
302      xrb->origin2 = (GLushort *) xrb->ximage->data + xrb->width2 * (height - 1);
303
304      /* Needed by PIXELADDR3 macro */
305      xrb->width3 = xrb->ximage->bytes_per_line;
306      xrb->origin3 = (GLubyte *) xrb->ximage->data + xrb->width3 * (height - 1);
307
308      /* Needed by PIXELADDR4 macro */
309      xrb->width4 = xrb->ximage->width;
310      xrb->origin4 = (GLuint *) xrb->ximage->data + xrb->width4 * (height - 1);
311   }
312   else {
313      /* out of memory or buffer size is 0 x 0 */
314      xrb->width1 = xrb->width2 = xrb->width3 = xrb->width4 = 0;
315      xrb->origin1 = NULL;
316      xrb->origin2 = NULL;
317      xrb->origin3 = NULL;
318      xrb->origin4 = NULL;
319   }
320
321   return GL_TRUE;
322}
323
324
325struct xmesa_renderbuffer *
326xmesa_new_renderbuffer(GLcontext *ctx, GLuint name, const GLvisual *visual,
327                       GLboolean backBuffer)
328{
329   struct xmesa_renderbuffer *xrb = CALLOC_STRUCT(xmesa_renderbuffer);
330   if (xrb) {
331      GLuint name = 0;
332      _mesa_init_renderbuffer(&xrb->Base, name);
333
334      xrb->Base.Delete = xmesa_delete_renderbuffer;
335      if (backBuffer)
336         xrb->Base.AllocStorage = xmesa_alloc_back_storage;
337      else
338         xrb->Base.AllocStorage = xmesa_alloc_front_storage;
339
340      if (visual->rgbMode) {
341         xrb->Base.InternalFormat = GL_RGBA;
342         xrb->Base.Format = MESA_FORMAT_RGBA8888;
343         xrb->Base._BaseFormat = GL_RGBA;
344         xrb->Base.DataType = GL_UNSIGNED_BYTE;
345      }
346      else {
347         xrb->Base.InternalFormat = GL_COLOR_INDEX;
348         xrb->Base.Format = MESA_FORMAT_CI8;
349         xrb->Base._BaseFormat = GL_COLOR_INDEX;
350         xrb->Base.DataType = GL_UNSIGNED_INT;
351      }
352      /* only need to set Red/Green/EtcBits fields for user-created RBs */
353   }
354   return xrb;
355}
356
357
358/**
359 * Called via gl_framebuffer::Delete() method when this buffer
360 * is _really_ being deleted.
361 */
362void
363xmesa_delete_framebuffer(struct gl_framebuffer *fb)
364{
365   XMesaBuffer b = XMESA_BUFFER(fb);
366
367   if (b->num_alloced > 0) {
368      /* If no other buffer uses this X colormap then free the colors. */
369      if (!xmesa_find_buffer(b->display, b->cmap, b)) {
370#ifdef XFree86Server
371         int client = 0;
372         if (b->frontxrb->drawable)
373            client = CLIENT_ID(b->frontxrb->drawable->id);
374         (void)FreeColors(b->cmap, client,
375                          b->num_alloced, b->alloced_colors, 0);
376#else
377         XFreeColors(b->display, b->cmap,
378                     b->alloced_colors, b->num_alloced, 0);
379#endif
380      }
381   }
382
383   if (b->gc)
384      XMesaFreeGC(b->display, b->gc);
385   if (b->cleargc)
386      XMesaFreeGC(b->display, b->cleargc);
387   if (b->swapgc)
388      XMesaFreeGC(b->display, b->swapgc);
389
390   if (fb->Visual.doubleBufferMode) {
391      /* free back ximage/pixmap/shmregion */
392      if (b->backxrb->ximage) {
393#if defined(USE_XSHM) && !defined(XFree86Server)
394         if (b->shm) {
395            XShmDetach( b->display, &b->shminfo );
396            XDestroyImage( b->backxrb->ximage );
397            shmdt( b->shminfo.shmaddr );
398         }
399         else
400#endif
401            XMesaDestroyImage( b->backxrb->ximage );
402         b->backxrb->ximage = NULL;
403      }
404      if (b->backxrb->pixmap) {
405         XMesaFreePixmap( b->display, b->backxrb->pixmap );
406         if (b->xm_visual->hpcr_clear_flag) {
407            XMesaFreePixmap( b->display,
408                             b->xm_visual->hpcr_clear_pixmap );
409            XMesaDestroyImage( b->xm_visual->hpcr_clear_ximage );
410         }
411      }
412   }
413
414   if (b->rowimage) {
415      _mesa_free( b->rowimage->data );
416      b->rowimage->data = NULL;
417      XMesaDestroyImage( b->rowimage );
418   }
419
420   _mesa_free_framebuffer_data(fb);
421   _mesa_free(fb);
422}
423