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