Move region_alloc() and region_release() to pipe_winsys.
[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 #include "pipe/p_defines.h"
40 #include "pipe/p_winsys.h"
41 #include "state_tracker/st_context.h"
42
43
44 #if defined(USE_XSHM) && !defined(XFree86Server)
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) && !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 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 #ifdef XFree86Server
196 b->backxrb->ximage = XMesaCreateImage(b->xm_visual->BitsPerPixel,
197 width, height, NULL);
198 #else
199 b->backxrb->ximage = XCreateImage(b->xm_visual->display,
200 b->xm_visual->visinfo->visual,
201 GET_VISUAL_DEPTH(b->xm_visual),
202 ZPixmap, 0, /* format, offset */
203 NULL,
204 width, height,
205 8, 0); /* pad, bytes_per_line */
206 #endif
207 if (!b->backxrb->ximage) {
208 _mesa_warning(NULL, "alloc_back_buffer: XCreateImage failed.\n");
209 return;
210 }
211 b->backxrb->ximage->data = (char *) MALLOC(b->backxrb->ximage->height
212 * b->backxrb->ximage->bytes_per_line);
213 if (!b->backxrb->ximage->data) {
214 _mesa_warning(NULL, "alloc_back_buffer: MALLOC failed.\n");
215 XMesaDestroyImage(b->backxrb->ximage);
216 b->backxrb->ximage = NULL;
217 }
218 }
219 b->backxrb->pixmap = None;
220 }
221 else if (b->db_mode == BACK_PIXMAP) {
222 /* Free the old back pixmap */
223 if (b->backxrb->pixmap) {
224 XMesaFreePixmap(b->xm_visual->display, b->backxrb->pixmap);
225 b->backxrb->pixmap = 0;
226 }
227
228 if (width > 0 && height > 0) {
229 /* Allocate new back pixmap */
230 b->backxrb->pixmap = XMesaCreatePixmap(b->xm_visual->display,
231 b->frontxrb->drawable,
232 width, height,
233 GET_VISUAL_DEPTH(b->xm_visual));
234 }
235
236 b->backxrb->ximage = NULL;
237 }
238 }
239
240
241 static void
242 xmesa_delete_renderbuffer(struct gl_renderbuffer *rb)
243 {
244 /* XXX Note: the ximage or Pixmap attached to this renderbuffer
245 * should probably get freed here, but that's currently done in
246 * XMesaDestroyBuffer().
247 */
248 _mesa_free(rb);
249 }
250
251
252 static void
253 finish_surface_init(GLcontext *ctx, struct xmesa_renderbuffer *xrb)
254 {
255 struct pipe_context *pipe = ctx->st->pipe;
256 if (!xrb->St.surface->region) {
257 int w = 1, h = 1;
258 xrb->St.surface->region = pipe->winsys->region_alloc(pipe->winsys,
259 1, w, h, 0x0);
260 }
261 }
262
263
264 /**
265 * Reallocate renderbuffer storage for front color buffer.
266 * Called via gl_renderbuffer::AllocStorage()
267 */
268 static GLboolean
269 xmesa_alloc_front_storage(GLcontext *ctx, struct gl_renderbuffer *rb,
270 GLenum internalFormat, GLuint width, GLuint height)
271 {
272 struct xmesa_renderbuffer *xrb = xmesa_renderbuffer(rb);
273
274 /* just clear these to be sure we don't accidentally use them */
275 xrb->origin1 = NULL;
276 xrb->origin2 = NULL;
277 xrb->origin3 = NULL;
278 xrb->origin4 = NULL;
279
280 /* for the FLIP macro: */
281 xrb->bottom = height - 1;
282
283 rb->Width = width;
284 rb->Height = height;
285 rb->InternalFormat = internalFormat;
286
287 if (!xrb->St.surface || !xrb->St.surface->region)
288 finish_surface_init(ctx, xrb);
289
290 xrb->St.surface->width = width;
291 xrb->St.surface->height = height;
292
293 return GL_TRUE;
294 }
295
296
297 /**
298 * Reallocate renderbuffer storage for back color buffer.
299 * Called via gl_renderbuffer::AllocStorage()
300 */
301 static GLboolean
302 xmesa_alloc_back_storage(GLcontext *ctx, struct gl_renderbuffer *rb,
303 GLenum internalFormat, GLuint width, GLuint height)
304 {
305 struct xmesa_renderbuffer *xrb = xmesa_renderbuffer(rb);
306
307 /* reallocate the back buffer XImage or Pixmap */
308 assert(xrb->Parent);
309 alloc_back_buffer(xrb->Parent, width, height);
310
311 /* same as front buffer */
312 /* XXX why is this here? */
313 (void) xmesa_alloc_front_storage(ctx, rb, internalFormat, width, height);
314
315 /* plus... */
316 if (xrb->ximage) {
317 /* Needed by PIXELADDR1 macro */
318 xrb->width1 = xrb->ximage->bytes_per_line;
319 xrb->origin1 = (GLubyte *) xrb->ximage->data + xrb->width1 * (height - 1);
320
321 /* Needed by PIXELADDR2 macro */
322 xrb->width2 = xrb->ximage->bytes_per_line / 2;
323 xrb->origin2 = (GLushort *) xrb->ximage->data + xrb->width2 * (height - 1);
324
325 /* Needed by PIXELADDR3 macro */
326 xrb->width3 = xrb->ximage->bytes_per_line;
327 xrb->origin3 = (GLubyte *) xrb->ximage->data + xrb->width3 * (height - 1);
328
329 /* Needed by PIXELADDR4 macro */
330 xrb->width4 = xrb->ximage->width;
331 xrb->origin4 = (GLuint *) xrb->ximage->data + xrb->width4 * (height - 1);
332 }
333 else {
334 /* out of memory or buffer size is 0 x 0 */
335 xrb->width1 = xrb->width2 = xrb->width3 = xrb->width4 = 0;
336 xrb->origin1 = NULL;
337 xrb->origin2 = NULL;
338 xrb->origin3 = NULL;
339 xrb->origin4 = NULL;
340 }
341
342 if (!xrb->St.surface || !xrb->St.surface->region)
343 finish_surface_init(ctx, xrb);
344
345 xrb->St.surface->width = width;
346 xrb->St.surface->height = height;
347
348 return GL_TRUE;
349 }
350
351
352 /**
353 * Called to create the front/back color renderbuffers, not user-created
354 * renderbuffers.
355 */
356 struct xmesa_renderbuffer *
357 xmesa_create_renderbuffer(GLcontext *ctx, GLuint name, const GLvisual *visual,
358 GLboolean backBuffer)
359 {
360 struct xmesa_renderbuffer *xrb = CALLOC_STRUCT(xmesa_renderbuffer);
361 struct pipe_context *pipe = NULL;/*ctx->st->pipe;*/
362 if (xrb) {
363 GLuint name = 0;
364 GLuint pipeFormat = 0;
365 struct xmesa_surface *xms;
366
367 _mesa_init_renderbuffer(&xrb->St.Base, name);
368
369 xrb->St.Base.Delete = xmesa_delete_renderbuffer;
370 if (backBuffer)
371 xrb->St.Base.AllocStorage = xmesa_alloc_back_storage;
372 else
373 xrb->St.Base.AllocStorage = xmesa_alloc_front_storage;
374
375 if (visual->rgbMode) {
376 xrb->St.Base.InternalFormat = GL_RGBA;
377 xrb->St.Base._BaseFormat = GL_RGBA;
378 xrb->St.Base.DataType = GL_UNSIGNED_BYTE;
379 xrb->St.Base.RedBits = visual->redBits;
380 xrb->St.Base.GreenBits = visual->greenBits;
381 xrb->St.Base.BlueBits = visual->blueBits;
382 xrb->St.Base.AlphaBits = visual->alphaBits;
383 pipeFormat = PIPE_FORMAT_U_A8_R8_G8_B8;
384 }
385 else {
386 xrb->St.Base.InternalFormat = GL_COLOR_INDEX;
387 xrb->St.Base._BaseFormat = GL_COLOR_INDEX;
388 xrb->St.Base.DataType = GL_UNSIGNED_INT;
389 xrb->St.Base.IndexBits = visual->indexBits;
390 }
391 /* only need to set Red/Green/EtcBits fields for user-created RBs */
392
393 xrb->St.surface = xmesa_new_color_surface(pipe, pipeFormat);
394 xms = (struct xmesa_surface *) xrb->St.surface;
395 xms->xrb = xrb;
396 }
397 return xrb;
398 }
399
400
401 #if 0
402 struct gl_renderbuffer *
403 xmesa_new_renderbuffer(GLcontext *ctx, struct gl_renderbuffer *rb,
404 GLenum internalFormat, GLuint width, GLuint height)
405 {
406 struct xmesa_renderbuffer *xrb = CALLOC_STRUCT(xmesa_renderbuffer);
407 if (xrb) {
408 GLuint name = 0;
409 _mesa_init_renderbuffer(&xrb->St.Base, name);
410
411 xrb->St.Base.Delete = xmesa_delete_renderbuffer;
412 if (backBuffer)
413 xrb->St.Base.AllocStorage = xmesa_alloc_back_storage;
414 else
415 xrb->St.Base.AllocStorage = xmesa_alloc_front_storage;
416
417 if (visual->rgbMode) {
418 xrb->St.Base.InternalFormat = GL_RGBA;
419 xrb->St.Base._BaseFormat = GL_RGBA;
420 xrb->St.Base.DataType = GL_UNSIGNED_BYTE;
421 xrb->St.Base.RedBits = visual->redBits;
422 xrb->St.Base.GreenBits = visual->greenBits;
423 xrb->St.Base.BlueBits = visual->blueBits;
424 xrb->St.Base.AlphaBits = visual->alphaBits;
425 }
426 else {
427 xrb->St.Base.InternalFormat = GL_COLOR_INDEX;
428 xrb->St.Base._BaseFormat = GL_COLOR_INDEX;
429 xrb->St.Base.DataType = GL_UNSIGNED_INT;
430 xrb->St.Base.IndexBits = visual->indexBits;
431 }
432 /* only need to set Red/Green/EtcBits fields for user-created RBs */
433 }
434 return xrb;
435 }
436 #endif
437
438
439 /**
440 * Called via gl_framebuffer::Delete() method when this buffer
441 * is _really_ being deleted.
442 */
443 void
444 xmesa_delete_framebuffer(struct gl_framebuffer *fb)
445 {
446 XMesaBuffer b = XMESA_BUFFER(fb);
447
448 if (b->num_alloced > 0) {
449 /* If no other buffer uses this X colormap then free the colors. */
450 if (!xmesa_find_buffer(b->display, b->cmap, b)) {
451 #ifdef XFree86Server
452 int client = 0;
453 if (b->frontxrb->drawable)
454 client = CLIENT_ID(b->frontxrb->drawable->id);
455 (void)FreeColors(b->cmap, client,
456 b->num_alloced, b->alloced_colors, 0);
457 #else
458 XFreeColors(b->display, b->cmap,
459 b->alloced_colors, b->num_alloced, 0);
460 #endif
461 }
462 }
463
464 if (b->gc)
465 XMesaFreeGC(b->display, b->gc);
466 if (b->cleargc)
467 XMesaFreeGC(b->display, b->cleargc);
468 if (b->swapgc)
469 XMesaFreeGC(b->display, b->swapgc);
470
471 if (fb->Visual.doubleBufferMode) {
472 /* free back ximage/pixmap/shmregion */
473 if (b->backxrb->ximage) {
474 #if defined(USE_XSHM) && !defined(XFree86Server)
475 if (b->shm) {
476 XShmDetach( b->display, &b->shminfo );
477 XDestroyImage( b->backxrb->ximage );
478 shmdt( b->shminfo.shmaddr );
479 }
480 else
481 #endif
482 XMesaDestroyImage( b->backxrb->ximage );
483 b->backxrb->ximage = NULL;
484 }
485 if (b->backxrb->pixmap) {
486 XMesaFreePixmap( b->display, b->backxrb->pixmap );
487 if (b->xm_visual->hpcr_clear_flag) {
488 XMesaFreePixmap( b->display,
489 b->xm_visual->hpcr_clear_pixmap );
490 XMesaDestroyImage( b->xm_visual->hpcr_clear_ximage );
491 }
492 }
493 }
494
495 if (b->rowimage) {
496 _mesa_free( b->rowimage->data );
497 b->rowimage->data = NULL;
498 XMesaDestroyImage( b->rowimage );
499 }
500
501 _mesa_free_framebuffer_data(fb);
502 _mesa_free(fb);
503 }