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