nir: support lowering clipdist to arrays
[mesa.git] / src / mesa / drivers / x11 / xm_buffer.c
1 /*
2 * Mesa 3-D graphics library
3 *
4 * Copyright (C) 1999-2006 Brian Paul All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included
14 * in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 * 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/errors.h"
35 #include "main/imports.h"
36 #include "main/formats.h"
37 #include "main/framebuffer.h"
38 #include "main/renderbuffer.h"
39 #include "swrast/s_renderbuffer.h"
40
41
42 #define XMESA_RENDERBUFFER 0x1234
43
44
45 #if defined(USE_XSHM)
46 static volatile int mesaXErrorFlag = 0;
47
48 /**
49 * Catches potential Xlib errors.
50 */
51 static int
52 mesaHandleXError(XMesaDisplay *dpy, XErrorEvent *event)
53 {
54 (void) dpy;
55 (void) event;
56 mesaXErrorFlag = 1;
57 return 0;
58 }
59
60 /**
61 * Allocate a shared memory XImage back buffer for the given XMesaBuffer.
62 * Return: GL_TRUE if success, GL_FALSE if error
63 */
64 static GLboolean
65 alloc_back_shm_ximage(XMesaBuffer b, GLuint width, GLuint height)
66 {
67 /*
68 * We have to do a _lot_ of error checking here to be sure we can
69 * really use the XSHM extension. It seems different servers trigger
70 * errors at different points if the extension won't work. Therefore
71 * we have to be very careful...
72 */
73 GC gc;
74 int (*old_handler)(XMesaDisplay *, XErrorEvent *);
75
76 if (width == 0 || height == 0) {
77 /* this will be true the first time we're called on 'b' */
78 return GL_FALSE;
79 }
80
81 b->backxrb->ximage = XShmCreateImage(b->xm_visual->display,
82 b->xm_visual->visinfo->visual,
83 b->xm_visual->visinfo->depth,
84 ZPixmap, NULL, &b->shminfo,
85 width, height);
86 if (b->backxrb->ximage == NULL) {
87 _mesa_warning(NULL, "alloc_back_buffer: Shared memory error (XShmCreateImage), disabling.\n");
88 b->shm = 0;
89 return GL_FALSE;
90 }
91
92 b->shminfo.shmid = shmget(IPC_PRIVATE, b->backxrb->ximage->bytes_per_line
93 * b->backxrb->ximage->height, IPC_CREAT|0777);
94 if (b->shminfo.shmid < 0) {
95 _mesa_warning(NULL, "shmget failed while allocating back buffer.\n");
96 XDestroyImage(b->backxrb->ximage);
97 b->backxrb->ximage = NULL;
98 _mesa_warning(NULL, "alloc_back_buffer: Shared memory error (shmget), disabling.\n");
99 b->shm = 0;
100 return GL_FALSE;
101 }
102
103 b->shminfo.shmaddr = b->backxrb->ximage->data
104 = (char*)shmat(b->shminfo.shmid, 0, 0);
105 if (b->shminfo.shmaddr == (char *) -1) {
106 _mesa_warning(NULL, "shmat() failed while allocating back buffer.\n");
107 XDestroyImage(b->backxrb->ximage);
108 shmctl(b->shminfo.shmid, IPC_RMID, 0);
109 b->backxrb->ximage = NULL;
110 _mesa_warning(NULL, "alloc_back_buffer: Shared memory error (shmat), disabling.\n");
111 b->shm = 0;
112 return GL_FALSE;
113 }
114
115 b->shminfo.readOnly = False;
116 mesaXErrorFlag = 0;
117 old_handler = XSetErrorHandler(mesaHandleXError);
118 /* This may trigger the X protocol error we're ready to catch: */
119 XShmAttach(b->xm_visual->display, &b->shminfo);
120 XSync(b->xm_visual->display, False);
121
122 if (mesaXErrorFlag) {
123 /* we are on a remote display, this error is normal, don't print it */
124 XFlush(b->xm_visual->display);
125 mesaXErrorFlag = 0;
126 XDestroyImage(b->backxrb->ximage);
127 shmdt(b->shminfo.shmaddr);
128 shmctl(b->shminfo.shmid, IPC_RMID, 0);
129 b->backxrb->ximage = NULL;
130 b->shm = 0;
131 (void) XSetErrorHandler(old_handler);
132 return GL_FALSE;
133 }
134
135 shmctl(b->shminfo.shmid, IPC_RMID, 0); /* nobody else needs it */
136
137 /* Finally, try an XShmPutImage to be really sure the extension works */
138 gc = XCreateGC(b->xm_visual->display, b->frontxrb->drawable, 0, NULL);
139 XShmPutImage(b->xm_visual->display, b->frontxrb->drawable, gc,
140 b->backxrb->ximage, 0, 0, 0, 0, 1, 1 /*one pixel*/, False);
141 XSync(b->xm_visual->display, False);
142 XFreeGC(b->xm_visual->display, gc);
143 (void) XSetErrorHandler(old_handler);
144 if (mesaXErrorFlag) {
145 XFlush(b->xm_visual->display);
146 mesaXErrorFlag = 0;
147 XDestroyImage(b->backxrb->ximage);
148 shmdt(b->shminfo.shmaddr);
149 shmctl(b->shminfo.shmid, IPC_RMID, 0);
150 b->backxrb->ximage = NULL;
151 b->shm = 0;
152 return GL_FALSE;
153 }
154
155 return GL_TRUE;
156 }
157 #else
158 static GLboolean
159 alloc_back_shm_ximage(XMesaBuffer b, GLuint width, GLuint height)
160 {
161 /* Can't compile XSHM support */
162 return GL_FALSE;
163 }
164 #endif
165
166
167
168 /**
169 * Setup an off-screen pixmap or Ximage to use as the back buffer.
170 * Input: b - the X/Mesa buffer
171 */
172 static void
173 alloc_back_buffer(XMesaBuffer b, GLuint width, GLuint height)
174 {
175 if (b->db_mode == BACK_XIMAGE) {
176 /* Deallocate the old backxrb->ximage, if any */
177 if (b->backxrb->ximage) {
178 #if defined(USE_XSHM)
179 if (b->shm) {
180 XShmDetach(b->xm_visual->display, &b->shminfo);
181 XDestroyImage(b->backxrb->ximage);
182 shmdt(b->shminfo.shmaddr);
183 }
184 else
185 #endif
186 XMesaDestroyImage(b->backxrb->ximage);
187 b->backxrb->ximage = NULL;
188 }
189
190 if (width == 0 || height == 0)
191 return;
192
193 /* Allocate new back buffer */
194 if (b->shm == 0 || !alloc_back_shm_ximage(b, width, height)) {
195 /* Allocate a regular XImage for the back buffer. */
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 if (!b->backxrb->ximage) {
204 _mesa_warning(NULL, "alloc_back_buffer: XCreateImage failed.\n");
205 return;
206 }
207 b->backxrb->ximage->data = 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
238 static void
239 xmesa_delete_renderbuffer(struct gl_context *ctx, 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 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(struct gl_context *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->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 */
279 static GLboolean
280 xmesa_alloc_back_storage(struct gl_context *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 PIXELADDR2 macro */
296 xrb->width2 = xrb->ximage->bytes_per_line / 2;
297 xrb->origin2 = (GLushort *) xrb->ximage->data + xrb->width2 * (height - 1);
298
299 /* Needed by PIXELADDR3 macro */
300 xrb->width3 = xrb->ximage->bytes_per_line;
301 xrb->origin3 = (GLubyte *) xrb->ximage->data + xrb->width3 * (height - 1);
302
303 /* Needed by PIXELADDR4 macro */
304 xrb->width4 = xrb->ximage->width;
305 xrb->origin4 = (GLuint *) xrb->ximage->data + xrb->width4 * (height - 1);
306 }
307 else {
308 /* out of memory or buffer size is 0 x 0 */
309 xrb->width2 = xrb->width3 = xrb->width4 = 0;
310 xrb->origin2 = NULL;
311 xrb->origin3 = NULL;
312 xrb->origin4 = NULL;
313 }
314
315 return GL_TRUE;
316 }
317
318
319 /**
320 * Used for allocating front/back renderbuffers for an X window.
321 */
322 struct xmesa_renderbuffer *
323 xmesa_new_renderbuffer(struct gl_context *ctx, GLuint name,
324 const struct xmesa_visual *xmvis,
325 GLboolean backBuffer)
326 {
327 struct xmesa_renderbuffer *xrb = CALLOC_STRUCT(xmesa_renderbuffer);
328 if (xrb) {
329 GLuint name = 0;
330 _mesa_init_renderbuffer(&xrb->Base.Base, name);
331
332 xrb->Base.Base.Delete = xmesa_delete_renderbuffer;
333 if (backBuffer)
334 xrb->Base.Base.AllocStorage = xmesa_alloc_back_storage;
335 else
336 xrb->Base.Base.AllocStorage = xmesa_alloc_front_storage;
337
338 xrb->Base.Base.InternalFormat = GL_RGBA;
339 xrb->Base.Base._BaseFormat = GL_RGBA;
340 xrb->Base.Base.ClassID = XMESA_RENDERBUFFER;
341
342 switch (xmvis->undithered_pf) {
343 case PF_8R8G8B:
344 /* This will really only happen for pixmaps. We'll access the
345 * pixmap via a temporary XImage which will be 32bpp.
346 */
347 xrb->Base.Base.Format = MESA_FORMAT_B8G8R8X8_UNORM;
348 break;
349 case PF_8A8R8G8B:
350 xrb->Base.Base.Format = MESA_FORMAT_B8G8R8A8_UNORM;
351 break;
352 case PF_8A8B8G8R:
353 xrb->Base.Base.Format = MESA_FORMAT_R8G8B8A8_UNORM;
354 break;
355 case PF_5R6G5B:
356 xrb->Base.Base.Format = MESA_FORMAT_B5G6R5_UNORM;
357 break;
358 default:
359 _mesa_warning(ctx, "Bad pixel format in xmesa_new_renderbuffer");
360 xrb->Base.Base.Format = MESA_FORMAT_B8G8R8A8_UNORM;
361 break;
362 }
363
364 /* only need to set Red/Green/EtcBits fields for user-created RBs */
365 }
366 return xrb;
367 }
368
369
370 /**
371 * Called via gl_framebuffer::Delete() method when this buffer
372 * is _really_ being deleted.
373 */
374 void
375 xmesa_delete_framebuffer(struct gl_framebuffer *fb)
376 {
377 XMesaBuffer b = XMESA_BUFFER(fb);
378
379 if (b->num_alloced > 0) {
380 /* If no other buffer uses this X colormap then free the colors. */
381 if (!xmesa_find_buffer(b->display, b->cmap, b)) {
382 XFreeColors(b->display, b->cmap,
383 b->alloced_colors, b->num_alloced, 0);
384 }
385 }
386
387 if (b->gc)
388 XMesaFreeGC(b->display, b->gc);
389 if (b->cleargc)
390 XMesaFreeGC(b->display, b->cleargc);
391 if (b->swapgc)
392 XMesaFreeGC(b->display, b->swapgc);
393
394 if (fb->Visual.doubleBufferMode) {
395 /* free back ximage/pixmap/shmregion */
396 if (b->backxrb->ximage) {
397 #if defined(USE_XSHM)
398 if (b->shm) {
399 XShmDetach( b->display, &b->shminfo );
400 XDestroyImage( b->backxrb->ximage );
401 shmdt( b->shminfo.shmaddr );
402 }
403 else
404 #endif
405 XMesaDestroyImage( b->backxrb->ximage );
406 b->backxrb->ximage = NULL;
407 }
408 if (b->backxrb->pixmap) {
409 XMesaFreePixmap( b->display, b->backxrb->pixmap );
410 }
411 }
412
413 _mesa_free_framebuffer_data(fb);
414 free(fb);
415 }
416
417
418 /**
419 * Called via ctx->Driver.MapRenderbuffer()
420 */
421 void
422 xmesa_MapRenderbuffer(struct gl_context *ctx,
423 struct gl_renderbuffer *rb,
424 GLuint x, GLuint y, GLuint w, GLuint h,
425 GLbitfield mode,
426 GLubyte **mapOut, GLint *rowStrideOut,
427 bool flip_y)
428 {
429 struct xmesa_renderbuffer *xrb = xmesa_renderbuffer(rb);
430
431 if (xrb->Base.Base.ClassID == XMESA_RENDERBUFFER) {
432 XImage *ximage = xrb->ximage;
433
434 assert(!xrb->map_mode); /* only a single mapping allowed */
435
436 xrb->map_mode = mode;
437 xrb->map_x = x;
438 xrb->map_y = y;
439 xrb->map_w = w;
440 xrb->map_h = h;
441
442 if (ximage) {
443 int y2 = rb->Height - y - 1;
444
445 *mapOut = (GLubyte *) ximage->data
446 + y2 * ximage->bytes_per_line
447 + x * ximage->bits_per_pixel / 8;
448 }
449 else {
450 /* this must be a pixmap/window renderbuffer */
451 int (*old_handler)(XMesaDisplay *, XErrorEvent *);
452 int y2 = rb->Height - y - h;
453
454 assert(xrb->pixmap);
455
456 /* Install error handler for XGetImage() in case the window
457 * isn't mapped. If we fail we'll create a temporary XImage.
458 */
459 mesaXErrorFlag = 0;
460 old_handler = XSetErrorHandler(mesaHandleXError);
461
462 /* read pixel data out of the pixmap/window into an XImage */
463 ximage = XGetImage(xrb->Parent->display,
464 xrb->pixmap, x, y2, w, h,
465 AllPlanes, ZPixmap);
466
467 XSetErrorHandler(old_handler);
468
469 if (mesaXErrorFlag) {
470 /* create new, temporary XImage */
471 int bytes_per_line =
472 _mesa_format_row_stride(xrb->Base.Base.Format,
473 xrb->Base.Base.Width);
474 char *image = malloc(bytes_per_line *
475 xrb->Base.Base.Height);
476 ximage = XCreateImage(xrb->Parent->display,
477 xrb->Parent->xm_visual->visinfo->visual,
478 xrb->Parent->xm_visual->visinfo->depth,
479 ZPixmap, /* format */
480 0, /* offset */
481 image, /* data */
482 xrb->Base.Base.Width,
483 xrb->Base.Base.Height,
484 8, /* pad */
485 bytes_per_line);
486 }
487
488 if (!ximage) {
489 *mapOut = NULL;
490 *rowStrideOut = 0;
491 return;
492 }
493
494 xrb->map_ximage = ximage;
495
496 /* the first row of the OpenGL image is last row of the XImage */
497 *mapOut = (GLubyte *) ximage->data
498 + (h - 1) * ximage->bytes_per_line;
499 }
500
501 /* We return a negative stride here since XImage data is upside down
502 * with respect to OpenGL images.
503 */
504 *rowStrideOut = -ximage->bytes_per_line;
505 return;
506 }
507
508 /* otherwise, this is an ordinary malloc-based renderbuffer */
509 _swrast_map_soft_renderbuffer(ctx, rb, x, y, w, h, mode,
510 mapOut, rowStrideOut, false);
511 }
512
513
514 /**
515 * Called via ctx->Driver.UnmapRenderbuffer()
516 */
517 void
518 xmesa_UnmapRenderbuffer(struct gl_context *ctx, struct gl_renderbuffer *rb)
519 {
520 struct xmesa_renderbuffer *xrb = xmesa_renderbuffer(rb);
521
522 if (xrb->Base.Base.ClassID == XMESA_RENDERBUFFER) {
523 XImage *ximage = xrb->ximage;
524
525 if (!ximage) {
526 /* this must be a pixmap/window renderbuffer */
527 assert(xrb->pixmap);
528 assert(xrb->map_ximage);
529 if (xrb->map_ximage) {
530 if (xrb->map_mode & GL_MAP_WRITE_BIT) {
531 /* put modified ximage data back into the pixmap/window */
532 int y2 = rb->Height - xrb->map_y - xrb->map_h;
533 GC gc = XCreateGC(xrb->Parent->display, xrb->pixmap, 0, NULL);
534
535 XPutImage(xrb->Parent->display,
536 xrb->pixmap, /* dest */
537 gc,
538 xrb->map_ximage, /* source */
539 0, 0, /* src x, y */
540 xrb->map_x, y2, /* dest x, y */
541 xrb->map_w, xrb->map_h); /* size */
542
543 XFreeGC(xrb->Parent->display, gc);
544 }
545 XMesaDestroyImage(xrb->map_ximage);
546 xrb->map_ximage = NULL;
547 }
548 }
549
550 xrb->map_mode = 0x0;
551
552 return;
553 }
554
555 /* otherwise, this is an ordinary malloc-based renderbuffer */
556 _swrast_unmap_soft_renderbuffer(ctx, rb);
557 }
558
559