Merge commit 'origin/gallium-0.1' into gallium-0.2
[mesa.git] / src / gallium / winsys / xlib / xm_winsys.c
1 /**************************************************************************
2 *
3 * Copyright 2007 Tungsten Graphics, Inc., Bismarck, ND., USA
4 * 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
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
17 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
18 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20 * USE OR OTHER DEALINGS IN THE SOFTWARE.
21 *
22 * The above copyright notice and this permission notice (including the
23 * next paragraph) shall be included in all copies or substantial portions
24 * of the Software.
25 *
26 *
27 **************************************************************************/
28
29 /*
30 * Authors:
31 * Keith Whitwell
32 * Brian Paul
33 */
34
35
36 #include "glxheader.h"
37 #include "xmesaP.h"
38
39 #undef ASSERT
40 #undef Elements
41
42 #include "pipe/p_winsys.h"
43 #include "pipe/p_format.h"
44 #include "pipe/p_context.h"
45 #include "pipe/p_inlines.h"
46 #include "util/u_math.h"
47 #include "util/u_memory.h"
48 #include "softpipe/sp_winsys.h"
49
50 #ifdef GALLIUM_CELL
51 #include "cell/ppu/cell_context.h"
52 #include "cell/ppu/cell_screen.h"
53 #include "cell/ppu/cell_winsys.h"
54 #else
55 #define TILE_SIZE 32 /* avoid compilation errors */
56 #endif
57
58 #ifdef GALLIUM_TRACE
59 #include "trace/tr_screen.h"
60 #include "trace/tr_context.h"
61 #endif
62
63 #include "xm_winsys_aub.h"
64
65
66 /**
67 * Subclass of pipe_buffer for Xlib winsys.
68 * Low-level OS/window system memory buffer
69 */
70 struct xm_buffer
71 {
72 struct pipe_buffer base;
73 boolean userBuffer; /** Is this a user-space buffer? */
74 void *data;
75 void *mapped;
76
77 XImage *tempImage;
78 int shm;
79 #if defined(USE_XSHM) && !defined(XFree86Server)
80 XShmSegmentInfo shminfo;
81 #endif
82 };
83
84
85 /**
86 * Subclass of pipe_winsys for Xlib winsys
87 */
88 struct xmesa_pipe_winsys
89 {
90 struct pipe_winsys base;
91 struct xmesa_visual *xm_visual;
92 int shm;
93 };
94
95
96
97 /** Cast wrapper */
98 static INLINE struct xm_buffer *
99 xm_buffer( struct pipe_buffer *buf )
100 {
101 return (struct xm_buffer *)buf;
102 }
103
104
105 /**
106 * X Shared Memory Image extension code
107 */
108 #if defined(USE_XSHM) && !defined(XFree86Server)
109
110 #define XSHM_ENABLED(b) ((b)->shm)
111
112 static volatile int mesaXErrorFlag = 0;
113
114 /**
115 * Catches potential Xlib errors.
116 */
117 static int
118 mesaHandleXError(XMesaDisplay *dpy, XErrorEvent *event)
119 {
120 (void) dpy;
121 (void) event;
122 mesaXErrorFlag = 1;
123 return 0;
124 }
125
126
127 static GLboolean alloc_shm(struct xm_buffer *buf, unsigned size)
128 {
129 XShmSegmentInfo *const shminfo = & buf->shminfo;
130
131 shminfo->shmid = shmget(IPC_PRIVATE, size, IPC_CREAT|0777);
132 if (shminfo->shmid < 0) {
133 return GL_FALSE;
134 }
135
136 shminfo->shmaddr = (char *) shmat(shminfo->shmid, 0, 0);
137 if (shminfo->shmaddr == (char *) -1) {
138 shmctl(shminfo->shmid, IPC_RMID, 0);
139 return GL_FALSE;
140 }
141
142 shminfo->readOnly = False;
143 return GL_TRUE;
144 }
145
146
147 /**
148 * Allocate a shared memory XImage back buffer for the given XMesaBuffer.
149 */
150 static void
151 alloc_shm_ximage(struct xm_buffer *b, struct xmesa_buffer *xmb,
152 unsigned width, unsigned height)
153 {
154 /*
155 * We have to do a _lot_ of error checking here to be sure we can
156 * really use the XSHM extension. It seems different servers trigger
157 * errors at different points if the extension won't work. Therefore
158 * we have to be very careful...
159 */
160 #if 0
161 GC gc;
162 #endif
163 int (*old_handler)(XMesaDisplay *, XErrorEvent *);
164
165 b->tempImage = XShmCreateImage(xmb->xm_visual->display,
166 xmb->xm_visual->visinfo->visual,
167 xmb->xm_visual->visinfo->depth,
168 ZPixmap,
169 NULL,
170 &b->shminfo,
171 width, height);
172 if (b->tempImage == NULL) {
173 b->shm = 0;
174 return;
175 }
176
177
178 mesaXErrorFlag = 0;
179 old_handler = XSetErrorHandler(mesaHandleXError);
180 /* This may trigger the X protocol error we're ready to catch: */
181 XShmAttach(xmb->xm_visual->display, &b->shminfo);
182 XSync(xmb->xm_visual->display, False);
183
184 if (mesaXErrorFlag) {
185 /* we are on a remote display, this error is normal, don't print it */
186 XFlush(xmb->xm_visual->display);
187 mesaXErrorFlag = 0;
188 XDestroyImage(b->tempImage);
189 b->tempImage = NULL;
190 b->shm = 0;
191 (void) XSetErrorHandler(old_handler);
192 return;
193 }
194
195
196 /* Finally, try an XShmPutImage to be really sure the extension works */
197 #if 0
198 gc = XCreateGC(xmb->xm_visual->display, xmb->drawable, 0, NULL);
199 XShmPutImage(xmb->xm_visual->display, xmb->drawable, gc,
200 b->tempImage, 0, 0, 0, 0, 1, 1 /*one pixel*/, False);
201 XSync(xmb->xm_visual->display, False);
202 XFreeGC(xmb->xm_visual->display, gc);
203 (void) XSetErrorHandler(old_handler);
204 if (mesaXErrorFlag) {
205 XFlush(xmb->xm_visual->display);
206 mesaXErrorFlag = 0;
207 XDestroyImage(b->tempImage);
208 b->tempImage = NULL;
209 b->shm = 0;
210 return;
211 }
212 #endif
213 }
214
215 #else
216
217 #define XSHM_ENABLED(b) 0
218
219 static void
220 alloc_shm_ximage(struct xm_buffer *b, struct xmesa_buffer *xmb,
221 unsigned width, unsigned height)
222 {
223 b->shm = 0;
224 }
225 #endif /* USE_XSHM */
226
227
228
229
230 /* Most callbacks map direcly onto dri_bufmgr operations:
231 */
232 static void *
233 xm_buffer_map(struct pipe_winsys *pws, struct pipe_buffer *buf,
234 unsigned flags)
235 {
236 struct xm_buffer *xm_buf = xm_buffer(buf);
237 xm_buf->mapped = xm_buf->data;
238 return xm_buf->mapped;
239 }
240
241 static void
242 xm_buffer_unmap(struct pipe_winsys *pws, struct pipe_buffer *buf)
243 {
244 struct xm_buffer *xm_buf = xm_buffer(buf);
245 xm_buf->mapped = NULL;
246 }
247
248 static void
249 xm_buffer_destroy(struct pipe_winsys *pws,
250 struct pipe_buffer *buf)
251 {
252 struct xm_buffer *oldBuf = xm_buffer(buf);
253
254 if (oldBuf->data) {
255 #if defined(USE_XSHM) && !defined(XFree86Server)
256 if (oldBuf->shminfo.shmid >= 0) {
257 shmdt(oldBuf->shminfo.shmaddr);
258 shmctl(oldBuf->shminfo.shmid, IPC_RMID, 0);
259
260 oldBuf->shminfo.shmid = -1;
261 oldBuf->shminfo.shmaddr = (char *) -1;
262 }
263 else
264 #endif
265 {
266 if (!oldBuf->userBuffer) {
267 align_free(oldBuf->data);
268 }
269 }
270
271 oldBuf->data = NULL;
272 }
273
274 free(oldBuf);
275 }
276
277
278 /**
279 * For Cell. Basically, rearrange the pixels/quads from this layout:
280 * +--+--+--+--+
281 * |p0|p1|p2|p3|....
282 * +--+--+--+--+
283 *
284 * to this layout:
285 * +--+--+
286 * |p0|p1|....
287 * +--+--+
288 * |p2|p3|
289 * +--+--+
290 */
291 static void
292 twiddle_tile(const uint *tileIn, uint *tileOut)
293 {
294 int y, x;
295
296 for (y = 0; y < TILE_SIZE; y+=2) {
297 for (x = 0; x < TILE_SIZE; x+=2) {
298 int k = 4 * (y/2 * TILE_SIZE/2 + x/2);
299 tileOut[y * TILE_SIZE + (x + 0)] = tileIn[k];
300 tileOut[y * TILE_SIZE + (x + 1)] = tileIn[k+1];
301 tileOut[(y + 1) * TILE_SIZE + (x + 0)] = tileIn[k+2];
302 tileOut[(y + 1) * TILE_SIZE + (x + 1)] = tileIn[k+3];
303 }
304 }
305 }
306
307
308
309 /**
310 * Display a surface that's in a tiled configuration. That is, all the
311 * pixels for a TILE_SIZExTILE_SIZE block are contiguous in memory.
312 */
313 static void
314 xmesa_display_surface_tiled(XMesaBuffer b, const struct pipe_surface *surf)
315 {
316 XImage *ximage;
317 struct xm_buffer *xm_buf = xm_buffer(surf->buffer);
318 const uint tilesPerRow = (surf->width + TILE_SIZE - 1) / TILE_SIZE;
319 uint x, y;
320
321 if (XSHM_ENABLED(xm_buf) && (xm_buf->tempImage == NULL)) {
322 alloc_shm_ximage(xm_buf, b, TILE_SIZE, TILE_SIZE);
323 }
324
325 ximage = (XSHM_ENABLED(xm_buf)) ? xm_buf->tempImage : b->tempImage;
326
327 /* check that the XImage has been previously initialized */
328 assert(ximage->format);
329 assert(ximage->bitmap_unit);
330
331 if (!XSHM_ENABLED(xm_buf)) {
332 /* update XImage's fields */
333 ximage->width = TILE_SIZE;
334 ximage->height = TILE_SIZE;
335 ximage->bytes_per_line = TILE_SIZE * 4;
336 }
337
338 for (y = 0; y < surf->height; y += TILE_SIZE) {
339 for (x = 0; x < surf->width; x += TILE_SIZE) {
340 uint tmpTile[TILE_SIZE * TILE_SIZE];
341 int tx = x / TILE_SIZE;
342 int ty = y / TILE_SIZE;
343 int offset = ty * tilesPerRow + tx;
344 int w = TILE_SIZE;
345 int h = TILE_SIZE;
346
347 if (y + h > surf->height)
348 h = surf->height - y;
349 if (x + w > surf->width)
350 w = surf->width - x;
351
352 /* offset in pixels */
353 offset *= TILE_SIZE * TILE_SIZE;
354
355 if (0 && XSHM_ENABLED(xm_buf)) {
356 ximage->data = (char *) xm_buf->data + 4 * offset;
357 /* make copy of tile data */
358 memcpy(tmpTile, (uint *) ximage->data, sizeof(tmpTile));
359 /* twiddle from temp to ximage in shared memory */
360 twiddle_tile(tmpTile, (uint *) ximage->data);
361 /* display image in shared memory */
362 #if defined(USE_XSHM) && !defined(XFree86Server)
363 XShmPutImage(b->xm_visual->display, b->drawable, b->gc,
364 ximage, 0, 0, x, y, w, h, False);
365 #endif
366 }
367 else {
368 /* twiddle from ximage buffer to temp tile */
369 twiddle_tile((uint *) xm_buf->data + offset, tmpTile);
370 /* display temp tile data */
371 ximage->data = (char *) tmpTile;
372 XPutImage(b->xm_visual->display, b->drawable, b->gc,
373 ximage, 0, 0, x, y, w, h);
374 }
375 }
376 }
377 }
378
379
380 /**
381 * Display/copy the image in the surface into the X window specified
382 * by the XMesaBuffer.
383 */
384 void
385 xmesa_display_surface(XMesaBuffer b, const struct pipe_surface *surf)
386 {
387 XImage *ximage;
388 struct xm_buffer *xm_buf = xm_buffer(surf->buffer);
389 static boolean no_swap = 0;
390 static boolean firsttime = 1;
391 static int tileSize = 0;
392
393 if (firsttime) {
394 no_swap = getenv("SP_NO_RAST") != NULL;
395 #ifdef GALLIUM_CELL
396 if (!getenv("GALLIUM_NOCELL")) {
397 tileSize = 32; /** probably temporary */
398 }
399 #endif
400 firsttime = 0;
401 }
402
403 if (no_swap)
404 return;
405
406 if (tileSize) {
407 xmesa_display_surface_tiled(b, surf);
408 return;
409 }
410
411 if (XSHM_ENABLED(xm_buf) && (xm_buf->tempImage == NULL)) {
412 assert(surf->block.width == 1);
413 assert(surf->block.height == 1);
414 alloc_shm_ximage(xm_buf, b, surf->stride/surf->block.size, surf->height);
415 }
416
417 ximage = (XSHM_ENABLED(xm_buf)) ? xm_buf->tempImage : b->tempImage;
418 ximage->data = xm_buf->data;
419
420 /* display image in Window */
421 if (XSHM_ENABLED(xm_buf)) {
422 #if defined(USE_XSHM) && !defined(XFree86Server)
423 XShmPutImage(b->xm_visual->display, b->drawable, b->gc,
424 ximage, 0, 0, 0, 0, surf->width, surf->height, False);
425 #endif
426 } else {
427 /* check that the XImage has been previously initialized */
428 assert(ximage->format);
429 assert(ximage->bitmap_unit);
430
431 /* update XImage's fields */
432 ximage->width = surf->width;
433 ximage->height = surf->height;
434 ximage->bytes_per_line = surf->stride;
435
436 XPutImage(b->xm_visual->display, b->drawable, b->gc,
437 ximage, 0, 0, 0, 0, surf->width, surf->height);
438 }
439 }
440
441
442 static void
443 xm_flush_frontbuffer(struct pipe_winsys *pws,
444 struct pipe_surface *surf,
445 void *context_private)
446 {
447 /*
448 * The front color buffer is actually just another XImage buffer.
449 * This function copies that XImage to the actual X Window.
450 */
451 XMesaContext xmctx = (XMesaContext) context_private;
452 xmesa_display_surface(xmctx->xm_buffer, surf);
453 }
454
455
456
457 static const char *
458 xm_get_name(struct pipe_winsys *pws)
459 {
460 return "Xlib";
461 }
462
463
464 static struct pipe_buffer *
465 xm_buffer_create(struct pipe_winsys *pws,
466 unsigned alignment,
467 unsigned usage,
468 unsigned size)
469 {
470 struct xm_buffer *buffer = CALLOC_STRUCT(xm_buffer);
471 #if defined(USE_XSHM) && !defined(XFree86Server)
472 struct xmesa_pipe_winsys *xpws = (struct xmesa_pipe_winsys *) pws;
473 #endif
474
475 buffer->base.refcount = 1;
476 buffer->base.alignment = alignment;
477 buffer->base.usage = usage;
478 buffer->base.size = size;
479
480
481 #if defined(USE_XSHM) && !defined(XFree86Server)
482 buffer->shminfo.shmid = -1;
483 buffer->shminfo.shmaddr = (char *) -1;
484
485 if (xpws->shm && (usage & PIPE_BUFFER_USAGE_PIXEL) != 0) {
486 buffer->shm = xpws->shm;
487
488 if (alloc_shm(buffer, size)) {
489 buffer->data = buffer->shminfo.shmaddr;
490 }
491 }
492 #endif
493
494 if (buffer->data == NULL) {
495 buffer->shm = 0;
496
497 /* align to 16-byte multiple for Cell */
498 buffer->data = align_malloc(size, max(alignment, 16));
499 }
500
501 return &buffer->base;
502 }
503
504
505 /**
506 * Create buffer which wraps user-space data.
507 */
508 static struct pipe_buffer *
509 xm_user_buffer_create(struct pipe_winsys *pws, void *ptr, unsigned bytes)
510 {
511 struct xm_buffer *buffer = CALLOC_STRUCT(xm_buffer);
512 buffer->base.refcount = 1;
513 buffer->base.size = bytes;
514 buffer->userBuffer = TRUE;
515 buffer->data = ptr;
516 buffer->shm = 0;
517
518 return &buffer->base;
519 }
520
521
522
523 /**
524 * Round n up to next multiple.
525 */
526 static INLINE unsigned
527 round_up(unsigned n, unsigned multiple)
528 {
529 return (n + multiple - 1) & ~(multiple - 1);
530 }
531
532 static int
533 xm_surface_alloc_storage(struct pipe_winsys *winsys,
534 struct pipe_surface *surf,
535 unsigned width, unsigned height,
536 enum pipe_format format,
537 unsigned flags,
538 unsigned tex_usage)
539 {
540 const unsigned alignment = 64;
541
542 surf->width = width;
543 surf->height = height;
544 surf->format = format;
545 pf_get_block(format, &surf->block);
546 surf->nblocksx = pf_get_nblocksx(&surf->block, width);
547 surf->nblocksy = pf_get_nblocksy(&surf->block, height);
548 surf->stride = round_up(surf->nblocksx * surf->block.size, alignment);
549 surf->usage = flags;
550
551 assert(!surf->buffer);
552 surf->buffer = winsys->buffer_create(winsys, alignment,
553 PIPE_BUFFER_USAGE_PIXEL,
554 #ifdef GALLIUM_CELL /* XXX a bit of a hack */
555 surf->stride * round_up(surf->nblocksy, TILE_SIZE));
556 #else
557 surf->stride * surf->nblocksy);
558 #endif
559
560 if(!surf->buffer)
561 return -1;
562
563 return 0;
564 }
565
566
567 /**
568 * Called via winsys->surface_alloc() to create new surfaces.
569 */
570 static struct pipe_surface *
571 xm_surface_alloc(struct pipe_winsys *ws)
572 {
573 struct pipe_surface *surface = CALLOC_STRUCT(pipe_surface);
574
575 assert(ws);
576
577 surface->refcount = 1;
578 surface->winsys = ws;
579
580 return surface;
581 }
582
583
584
585 static void
586 xm_surface_release(struct pipe_winsys *winsys, struct pipe_surface **s)
587 {
588 struct pipe_surface *surf = *s;
589 assert(!surf->texture);
590 surf->refcount--;
591 if (surf->refcount == 0) {
592 if (surf->buffer)
593 winsys_buffer_reference(winsys, &surf->buffer, NULL);
594 free(surf);
595 }
596 *s = NULL;
597 }
598
599
600 /*
601 * Fence functions - basically nothing to do, as we don't create any actual
602 * fence objects.
603 */
604
605 static void
606 xm_fence_reference(struct pipe_winsys *sws, struct pipe_fence_handle **ptr,
607 struct pipe_fence_handle *fence)
608 {
609 }
610
611
612 static int
613 xm_fence_signalled(struct pipe_winsys *sws, struct pipe_fence_handle *fence,
614 unsigned flag)
615 {
616 return 0;
617 }
618
619
620 static int
621 xm_fence_finish(struct pipe_winsys *sws, struct pipe_fence_handle *fence,
622 unsigned flag)
623 {
624 return 0;
625 }
626
627
628 /**
629 * Return pointer to a pipe_winsys object.
630 * For Xlib, this is a singleton object.
631 * Nothing special for the Xlib driver so no subclassing or anything.
632 */
633 struct pipe_winsys *
634 xmesa_get_pipe_winsys_aub(struct xmesa_visual *xm_vis)
635 {
636 static struct xmesa_pipe_winsys *ws = NULL;
637
638 if (!ws) {
639 ws = (struct xmesa_pipe_winsys *) xmesa_create_pipe_winsys_aub();
640 }
641 return &ws->base;
642 }
643
644
645 static struct pipe_winsys *
646 xmesa_get_pipe_winsys(struct xmesa_visual *xm_vis)
647 {
648 static struct xmesa_pipe_winsys *ws = NULL;
649
650 if (!ws) {
651 ws = CALLOC_STRUCT(xmesa_pipe_winsys);
652
653 ws->xm_visual = xm_vis;
654 ws->shm = xmesa_check_for_xshm(xm_vis->display);
655
656 /* Fill in this struct with callbacks that pipe will need to
657 * communicate with the window system, buffer manager, etc.
658 */
659 ws->base.buffer_create = xm_buffer_create;
660 ws->base.user_buffer_create = xm_user_buffer_create;
661 ws->base.buffer_map = xm_buffer_map;
662 ws->base.buffer_unmap = xm_buffer_unmap;
663 ws->base.buffer_destroy = xm_buffer_destroy;
664
665 ws->base.surface_alloc = xm_surface_alloc;
666 ws->base.surface_alloc_storage = xm_surface_alloc_storage;
667 ws->base.surface_release = xm_surface_release;
668
669 ws->base.fence_reference = xm_fence_reference;
670 ws->base.fence_signalled = xm_fence_signalled;
671 ws->base.fence_finish = xm_fence_finish;
672
673 ws->base.flush_frontbuffer = xm_flush_frontbuffer;
674 ws->base.get_name = xm_get_name;
675 }
676
677 return &ws->base;
678 }
679
680
681 struct pipe_context *
682 xmesa_create_pipe_context(XMesaContext xmesa, uint pixelformat)
683 {
684 struct pipe_winsys *pws;
685 struct pipe_context *pipe;
686
687 if (getenv("XM_AUB")) {
688 pws = xmesa_get_pipe_winsys_aub(xmesa->xm_visual);
689 }
690 else {
691 pws = xmesa_get_pipe_winsys(xmesa->xm_visual);
692 }
693
694 #ifdef GALLIUM_CELL
695 if (!getenv("GALLIUM_NOCELL")) {
696 struct cell_winsys *cws = cell_get_winsys(pixelformat);
697 struct pipe_screen *screen = cell_create_screen(pws);
698
699 pipe = cell_create_context(screen, cws);
700 }
701 else
702 #endif
703 {
704 struct pipe_screen *screen = softpipe_create_screen(pws);
705
706 pipe = softpipe_create(screen, pws, NULL);
707
708 #ifdef GALLIUM_TRACE
709 screen = trace_screen_create(screen);
710
711 pipe = trace_context_create(screen, pipe);
712 #endif
713 }
714
715 if (pipe)
716 pipe->priv = xmesa;
717
718 return pipe;
719 }