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