Fix/improve framebuffer object reference counting.
[mesa.git] / src / mesa / drivers / x11 / xm_buffer.c
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 "GL/xmesa.h"
34 #include "xmesaP.h"
35 #include "imports.h"
36 #include "framebuffer.h"
37 #include "renderbuffer.h"
38
39
40 #ifndef XFree86Server
41 static volatile int mesaXErrorFlag = 0;
42
43 /**
44 * Catches potential Xlib errors.
45 */
46 static int
47 mesaHandleXError(XMesaDisplay *dpy, XErrorEvent *event)
48 {
49 (void) dpy;
50 (void) event;
51 mesaXErrorFlag = 1;
52 return 0;
53 }
54 #endif
55
56
57 /**
58 * Allocate a shared memory XImage back buffer for the given XMesaBuffer.
59 * Return: GL_TRUE if success, GL_FALSE if error
60 */
61 #ifndef XFree86Server
62 static GLboolean
63 alloc_back_shm_ximage(XMesaBuffer b, GLuint width, GLuint height)
64 {
65 #ifdef USE_XSHM
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 #else
156 /* Can't compile XSHM support */
157 return GL_FALSE;
158 #endif
159 }
160 #endif
161
162
163
164 /**
165 * Setup an off-screen pixmap or Ximage to use as the back buffer.
166 * Input: b - the X/Mesa buffer
167 */
168 static void
169 alloc_back_buffer(XMesaBuffer b, GLuint width, GLuint height)
170 {
171 if (width == 0 || height == 0)
172 return;
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) && !defined(XFree86Server)
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 /* Allocate new back buffer */
190 #ifdef XFree86Server
191 /* Allocate a regular XImage for the back buffer. */
192 b->backxrb->ximage = XMesaCreateImage(b->xm_visual->BitsPerPixel,
193 width, height, NULL);
194 {
195 #else
196 if (b->shm == 0 || !alloc_back_shm_ximage(b, width, height)) {
197 /* Allocate a regular XImage for the back buffer. */
198 b->backxrb->ximage = XCreateImage(b->xm_visual->display,
199 b->xm_visual->visinfo->visual,
200 GET_VISUAL_DEPTH(b->xm_visual),
201 ZPixmap, 0, /* format, offset */
202 NULL,
203 width, height,
204 8, 0); /* pad, bytes_per_line */
205 #endif
206 if (!b->backxrb->ximage) {
207 _mesa_warning(NULL, "alloc_back_buffer: XCreateImage failed.\n");
208 return;
209 }
210 b->backxrb->ximage->data = (char *) MALLOC(b->backxrb->ximage->height
211 * b->backxrb->ximage->bytes_per_line);
212 if (!b->backxrb->ximage->data) {
213 _mesa_warning(NULL, "alloc_back_buffer: MALLOC failed.\n");
214 XMesaDestroyImage(b->backxrb->ximage);
215 b->backxrb->ximage = NULL;
216 }
217 }
218 b->backxrb->pixmap = None;
219 }
220 else if (b->db_mode == BACK_PIXMAP) {
221 if (!width)
222 width = 1;
223 if (!height)
224 height = 1;
225
226 /* Free the old back pixmap */
227 if (b->backxrb->pixmap) {
228 XMesaFreePixmap(b->xm_visual->display, b->backxrb->pixmap);
229 }
230 /* Allocate new back pixmap */
231 b->backxrb->pixmap = XMesaCreatePixmap(b->xm_visual->display,
232 b->frontxrb->drawable,
233 width, height,
234 GET_VISUAL_DEPTH(b->xm_visual));
235 b->backxrb->ximage = NULL;
236 }
237 }
238
239
240 static void
241 xmesa_delete_renderbuffer(struct gl_renderbuffer *rb)
242 {
243 /* XXX Note: the ximage or Pixmap attached to this renderbuffer
244 * should probably get freed here, but that's currently done in
245 * XMesaDestroyBuffer().
246 */
247 _mesa_free(rb);
248 }
249
250
251 /**
252 * Reallocate renderbuffer storage for front color buffer.
253 */
254 static GLboolean
255 xmesa_alloc_front_storage(GLcontext *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->origin1 = NULL;
262 xrb->origin2 = 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 */
279 static GLboolean
280 xmesa_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 /* this assertion will fail if we happend to run out of memory */
313 /*assert(xrb->pixmap);*/
314 }
315
316 return GL_TRUE;
317 }
318
319
320 struct xmesa_renderbuffer *
321 xmesa_new_renderbuffer(GLcontext *ctx, GLuint name, const GLvisual *visual,
322 GLboolean backBuffer)
323 {
324 struct xmesa_renderbuffer *xrb = CALLOC_STRUCT(xmesa_renderbuffer);
325 if (xrb) {
326 GLuint name = 0;
327 _mesa_init_renderbuffer(&xrb->Base, name);
328
329 xrb->Base.Delete = xmesa_delete_renderbuffer;
330 if (backBuffer)
331 xrb->Base.AllocStorage = xmesa_alloc_back_storage;
332 else
333 xrb->Base.AllocStorage = xmesa_alloc_front_storage;
334
335 if (visual->rgbMode) {
336 xrb->Base.InternalFormat = GL_RGBA;
337 xrb->Base._BaseFormat = GL_RGBA;
338 xrb->Base.DataType = GL_UNSIGNED_BYTE;
339 xrb->Base.RedBits = visual->redBits;
340 xrb->Base.GreenBits = visual->greenBits;
341 xrb->Base.BlueBits = visual->blueBits;
342 xrb->Base.AlphaBits = visual->alphaBits;
343 }
344 else {
345 xrb->Base.InternalFormat = GL_COLOR_INDEX;
346 xrb->Base._BaseFormat = GL_COLOR_INDEX;
347 xrb->Base.DataType = GL_UNSIGNED_INT;
348 xrb->Base.IndexBits = visual->indexBits;
349 }
350 /* only need to set Red/Green/EtcBits fields for user-created RBs */
351 }
352 return xrb;
353 }
354
355
356 /**
357 * Called via gl_framebuffer::Delete() method when this buffer
358 * is _really_ being deleted.
359 */
360 void
361 xmesa_delete_framebuffer(struct gl_framebuffer *fb)
362 {
363 XMesaBuffer b = XMESA_BUFFER(fb);
364
365 #ifdef XFree86Server
366 int client = 0;
367 if (b->frontxrb->drawable)
368 client = CLIENT_ID(b->frontxrb->drawable->id);
369 #endif
370
371 if (b->num_alloced > 0) {
372 /* If no other buffer uses this X colormap then free the colors. */
373 if (!xmesa_find_buffer(b->display, b->cmap, b)) {
374 #ifdef XFree86Server
375 (void)FreeColors(b->cmap, client,
376 b->num_alloced, b->alloced_colors, 0);
377 #else
378 XFreeColors(b->display, b->cmap,
379 b->alloced_colors, b->num_alloced, 0);
380 #endif
381 }
382 }
383
384 if (b->gc)
385 XMesaFreeGC(b->xm_visual->display, b->gc);
386 if (b->cleargc)
387 XMesaFreeGC(b->xm_visual->display, b->cleargc);
388 if (b->swapgc)
389 XMesaFreeGC(b->xm_visual->display, b->swapgc);
390
391 if (b->xm_visual->mesa_visual.doubleBufferMode) {
392 /* free back ximage/pixmap/shmregion */
393 if (b->backxrb->ximage) {
394 #if defined(USE_XSHM) && !defined(XFree86Server)
395 if (b->shm) {
396 XShmDetach( b->xm_visual->display, &b->shminfo );
397 XDestroyImage( b->backxrb->ximage );
398 shmdt( b->shminfo.shmaddr );
399 }
400 else
401 #endif
402 XMesaDestroyImage( b->backxrb->ximage );
403 b->backxrb->ximage = NULL;
404 }
405 if (b->backxrb->pixmap) {
406 XMesaFreePixmap( b->xm_visual->display, b->backxrb->pixmap );
407 if (b->xm_visual->hpcr_clear_flag) {
408 XMesaFreePixmap( b->xm_visual->display,
409 b->xm_visual->hpcr_clear_pixmap );
410 XMesaDestroyImage( b->xm_visual->hpcr_clear_ximage );
411 }
412 }
413 }
414
415 if (b->rowimage) {
416 _mesa_free( b->rowimage->data );
417 b->rowimage->data = NULL;
418 XMesaDestroyImage( b->rowimage );
419 }
420
421 _mesa_free_framebuffer_data(fb);
422 _mesa_free(fb);
423 }