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