gallium: refactor/replace p_util.h with util/u_memory.h and util/u_math.h
[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 * Display a surface that's in a tiled configuration. That is, all the
280 * pixels for a TILE_SIZExTILE_SIZE block are contiguous in memory.
281 */
282 static void
283 xmesa_display_surface_tiled(XMesaBuffer b, const struct pipe_surface *surf)
284 {
285 XImage *ximage;
286 struct xm_buffer *xm_buf = xm_buffer(surf->buffer);
287 const uint tilesPerRow = (surf->width + TILE_SIZE - 1) / TILE_SIZE;
288 uint x, y;
289
290 /* check that the XImage has been previously initialized */
291 assert(ximage->format);
292 assert(ximage->bitmap_unit);
293
294 if (XSHM_ENABLED(xm_buf) && (xm_buf->tempImage == NULL)) {
295 alloc_shm_ximage(xm_buf, b, TILE_SIZE, TILE_SIZE);
296 }
297
298 ximage = (XSHM_ENABLED(xm_buf)) ? xm_buf->tempImage : b->tempImage;
299
300 if (!XSHM_ENABLED(xm_buf)) {
301 /* update XImage's fields */
302 ximage->width = TILE_SIZE;
303 ximage->height = TILE_SIZE;
304 ximage->bytes_per_line = TILE_SIZE * 4;
305 }
306
307 for (y = 0; y < surf->height; y += TILE_SIZE) {
308 for (x = 0; x < surf->width; x += TILE_SIZE) {
309 int dx = x;
310 int dy = y;
311 int tx = x / TILE_SIZE;
312 int ty = y / TILE_SIZE;
313 int offset = ty * tilesPerRow + tx;
314
315 offset *= 4 * TILE_SIZE * TILE_SIZE;
316
317 ximage->data = (char *) xm_buf->data + offset;
318
319 if (XSHM_ENABLED(xm_buf)) {
320 #if defined(USE_XSHM) && !defined(XFree86Server)
321 XShmPutImage(b->xm_visual->display, b->drawable, b->gc,
322 ximage, 0, 0, x, y, TILE_SIZE, TILE_SIZE, False);
323 #endif
324 } else {
325 XPutImage(b->xm_visual->display, b->drawable, b->gc,
326 ximage, 0, 0, dx, dy, TILE_SIZE, TILE_SIZE);
327 }
328 }
329 }
330 }
331
332
333 /**
334 * Display/copy the image in the surface into the X window specified
335 * by the XMesaBuffer.
336 */
337 void
338 xmesa_display_surface(XMesaBuffer b, const struct pipe_surface *surf)
339 {
340 XImage *ximage;
341 struct xm_buffer *xm_buf = xm_buffer(surf->buffer);
342 static boolean no_swap = 0;
343 static boolean firsttime = 1;
344 static int tileSize = 0;
345
346 if (firsttime) {
347 no_swap = getenv("SP_NO_RAST") != NULL;
348 #ifdef GALLIUM_CELL
349 if (!getenv("GALLIUM_NOCELL")) {
350 tileSize = 32; /** probably temporary */
351 }
352 #endif
353 firsttime = 0;
354 }
355
356 if (no_swap)
357 return;
358
359 if (tileSize) {
360 xmesa_display_surface_tiled(b, surf);
361 return;
362 }
363
364 if (XSHM_ENABLED(xm_buf) && (xm_buf->tempImage == NULL)) {
365 assert(surf->block.width == 1);
366 assert(surf->block.height == 1);
367 alloc_shm_ximage(xm_buf, b, surf->stride/surf->block.size, surf->height);
368 }
369
370 ximage = (XSHM_ENABLED(xm_buf)) ? xm_buf->tempImage : b->tempImage;
371 ximage->data = xm_buf->data;
372
373 /* display image in Window */
374 if (XSHM_ENABLED(xm_buf)) {
375 #if defined(USE_XSHM) && !defined(XFree86Server)
376 XShmPutImage(b->xm_visual->display, b->drawable, b->gc,
377 ximage, 0, 0, 0, 0, surf->width, surf->height, False);
378 #endif
379 } else {
380 /* check that the XImage has been previously initialized */
381 assert(ximage->format);
382 assert(ximage->bitmap_unit);
383
384 /* update XImage's fields */
385 ximage->width = surf->width;
386 ximage->height = surf->height;
387 ximage->bytes_per_line = surf->stride;
388
389 XPutImage(b->xm_visual->display, b->drawable, b->gc,
390 ximage, 0, 0, 0, 0, surf->width, surf->height);
391 }
392 }
393
394
395 static void
396 xm_flush_frontbuffer(struct pipe_winsys *pws,
397 struct pipe_surface *surf,
398 void *context_private)
399 {
400 /*
401 * The front color buffer is actually just another XImage buffer.
402 * This function copies that XImage to the actual X Window.
403 */
404 XMesaContext xmctx = (XMesaContext) context_private;
405 xmesa_display_surface(xmctx->xm_buffer, surf);
406 }
407
408
409
410 static const char *
411 xm_get_name(struct pipe_winsys *pws)
412 {
413 return "Xlib";
414 }
415
416
417 static struct pipe_buffer *
418 xm_buffer_create(struct pipe_winsys *pws,
419 unsigned alignment,
420 unsigned usage,
421 unsigned size)
422 {
423 struct xm_buffer *buffer = CALLOC_STRUCT(xm_buffer);
424 #if defined(USE_XSHM) && !defined(XFree86Server)
425 struct xmesa_pipe_winsys *xpws = (struct xmesa_pipe_winsys *) pws;
426 #endif
427
428 buffer->base.refcount = 1;
429 buffer->base.alignment = alignment;
430 buffer->base.usage = usage;
431 buffer->base.size = size;
432
433
434 #if defined(USE_XSHM) && !defined(XFree86Server)
435 buffer->shminfo.shmid = -1;
436 buffer->shminfo.shmaddr = (char *) -1;
437
438 if (xpws->shm && (usage & PIPE_BUFFER_USAGE_PIXEL) != 0) {
439 buffer->shm = xpws->shm;
440
441 if (alloc_shm(buffer, size)) {
442 buffer->data = buffer->shminfo.shmaddr;
443 }
444 }
445 #endif
446
447 if (buffer->data == NULL) {
448 buffer->shm = 0;
449
450 /* align to 16-byte multiple for Cell */
451 buffer->data = align_malloc(size, max(alignment, 16));
452 }
453
454 return &buffer->base;
455 }
456
457
458 /**
459 * Create buffer which wraps user-space data.
460 */
461 static struct pipe_buffer *
462 xm_user_buffer_create(struct pipe_winsys *pws, void *ptr, unsigned bytes)
463 {
464 struct xm_buffer *buffer = CALLOC_STRUCT(xm_buffer);
465 buffer->base.refcount = 1;
466 buffer->base.size = bytes;
467 buffer->userBuffer = TRUE;
468 buffer->data = ptr;
469 buffer->shm = 0;
470
471 return &buffer->base;
472 }
473
474
475
476 /**
477 * Round n up to next multiple.
478 */
479 static INLINE unsigned
480 round_up(unsigned n, unsigned multiple)
481 {
482 return (n + multiple - 1) & ~(multiple - 1);
483 }
484
485 static int
486 xm_surface_alloc_storage(struct pipe_winsys *winsys,
487 struct pipe_surface *surf,
488 unsigned width, unsigned height,
489 enum pipe_format format,
490 unsigned flags,
491 unsigned tex_usage)
492 {
493 const unsigned alignment = 64;
494
495 surf->width = width;
496 surf->height = height;
497 surf->format = format;
498 pf_get_block(format, &surf->block);
499 surf->nblocksx = pf_get_nblocksx(&surf->block, width);
500 surf->nblocksy = pf_get_nblocksy(&surf->block, height);
501 surf->stride = round_up(surf->nblocksx * surf->block.size, alignment);
502 surf->usage = flags;
503
504 assert(!surf->buffer);
505 surf->buffer = winsys->buffer_create(winsys, alignment,
506 PIPE_BUFFER_USAGE_PIXEL,
507 #ifdef GALLIUM_CELL /* XXX a bit of a hack */
508 surf->stride * round_up(surf->nblocksy, TILE_SIZE));
509 #else
510 surf->stride * surf->nblocksy);
511 #endif
512
513 if(!surf->buffer)
514 return -1;
515
516 return 0;
517 }
518
519
520 /**
521 * Called via winsys->surface_alloc() to create new surfaces.
522 */
523 static struct pipe_surface *
524 xm_surface_alloc(struct pipe_winsys *ws)
525 {
526 struct pipe_surface *surface = CALLOC_STRUCT(pipe_surface);
527
528 assert(ws);
529
530 surface->refcount = 1;
531 surface->winsys = ws;
532
533 return surface;
534 }
535
536
537
538 static void
539 xm_surface_release(struct pipe_winsys *winsys, struct pipe_surface **s)
540 {
541 struct pipe_surface *surf = *s;
542 assert(!surf->texture);
543 surf->refcount--;
544 if (surf->refcount == 0) {
545 if (surf->buffer)
546 pipe_buffer_reference(winsys, &surf->buffer, NULL);
547 free(surf);
548 }
549 *s = NULL;
550 }
551
552
553 /*
554 * Fence functions - basically nothing to do, as we don't create any actual
555 * fence objects.
556 */
557
558 static void
559 xm_fence_reference(struct pipe_winsys *sws, struct pipe_fence_handle **ptr,
560 struct pipe_fence_handle *fence)
561 {
562 }
563
564
565 static int
566 xm_fence_signalled(struct pipe_winsys *sws, struct pipe_fence_handle *fence,
567 unsigned flag)
568 {
569 return 0;
570 }
571
572
573 static int
574 xm_fence_finish(struct pipe_winsys *sws, struct pipe_fence_handle *fence,
575 unsigned flag)
576 {
577 return 0;
578 }
579
580
581 /**
582 * Return pointer to a pipe_winsys object.
583 * For Xlib, this is a singleton object.
584 * Nothing special for the Xlib driver so no subclassing or anything.
585 */
586 struct pipe_winsys *
587 xmesa_get_pipe_winsys_aub(struct xmesa_visual *xm_vis)
588 {
589 static struct xmesa_pipe_winsys *ws = NULL;
590
591 if (!ws) {
592 ws = (struct xmesa_pipe_winsys *) xmesa_create_pipe_winsys_aub();
593 }
594 return &ws->base;
595 }
596
597
598 static struct pipe_winsys *
599 xmesa_get_pipe_winsys(struct xmesa_visual *xm_vis)
600 {
601 static struct xmesa_pipe_winsys *ws = NULL;
602
603 if (!ws) {
604 ws = CALLOC_STRUCT(xmesa_pipe_winsys);
605
606 ws->xm_visual = xm_vis;
607 ws->shm = xmesa_check_for_xshm(xm_vis->display);
608
609 /* Fill in this struct with callbacks that pipe will need to
610 * communicate with the window system, buffer manager, etc.
611 */
612 ws->base.buffer_create = xm_buffer_create;
613 ws->base.user_buffer_create = xm_user_buffer_create;
614 ws->base.buffer_map = xm_buffer_map;
615 ws->base.buffer_unmap = xm_buffer_unmap;
616 ws->base.buffer_destroy = xm_buffer_destroy;
617
618 ws->base.surface_alloc = xm_surface_alloc;
619 ws->base.surface_alloc_storage = xm_surface_alloc_storage;
620 ws->base.surface_release = xm_surface_release;
621
622 ws->base.fence_reference = xm_fence_reference;
623 ws->base.fence_signalled = xm_fence_signalled;
624 ws->base.fence_finish = xm_fence_finish;
625
626 ws->base.flush_frontbuffer = xm_flush_frontbuffer;
627 ws->base.get_name = xm_get_name;
628 }
629
630 return &ws->base;
631 }
632
633
634 struct pipe_context *
635 xmesa_create_pipe_context(XMesaContext xmesa, uint pixelformat)
636 {
637 struct pipe_winsys *pws;
638 struct pipe_context *pipe;
639
640 if (getenv("XM_AUB")) {
641 pws = xmesa_get_pipe_winsys_aub(xmesa->xm_visual);
642 }
643 else {
644 pws = xmesa_get_pipe_winsys(xmesa->xm_visual);
645 }
646
647 #ifdef GALLIUM_CELL
648 if (!getenv("GALLIUM_NOCELL")) {
649 struct cell_winsys *cws = cell_get_winsys(pixelformat);
650 struct pipe_screen *screen = cell_create_screen(pws);
651
652 pipe = cell_create_context(screen, cws);
653 }
654 else
655 #endif
656 {
657 struct pipe_screen *screen = softpipe_create_screen(pws);
658
659 pipe = softpipe_create(screen, pws, NULL);
660
661 #ifdef GALLIUM_TRACE
662 screen = trace_screen_create(screen);
663
664 pipe = trace_context_create(screen, pipe);
665 #endif
666 }
667
668 if (pipe)
669 pipe->priv = xmesa;
670
671 return pipe;
672 }