native_ximage.c revision 25357696c3f253d44e83798e2a7e7f1f60c5adc5
1/*
2 * Mesa 3-D graphics library
3 * Version:  7.8
4 *
5 * Copyright (C) 2009-2010 Chia-I Wu <olv@0xlab.org>
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
21 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25#include <assert.h>
26#include <sys/ipc.h>
27#include <sys/types.h>
28#include <sys/shm.h>
29#include <X11/Xlib.h>
30#include <X11/Xutil.h>
31#include <X11/extensions/XShm.h>
32#include "util/u_memory.h"
33#include "util/u_math.h"
34#include "util/u_format.h"
35#include "pipe/p_compiler.h"
36#include "util/u_simple_screen.h"
37#include "softpipe/sp_winsys.h"
38#include "egllog.h"
39
40#include "sw_winsys.h"
41#include "native_x11.h"
42#include "x11_screen.h"
43
44enum ximage_surface_type {
45   XIMAGE_SURFACE_TYPE_WINDOW,
46   XIMAGE_SURFACE_TYPE_PIXMAP,
47   XIMAGE_SURFACE_TYPE_PBUFFER
48};
49
50struct ximage_display {
51   struct native_display base;
52   Display *dpy;
53   boolean own_dpy;
54
55   struct x11_screen *xscr;
56   int xscr_number;
57
58   boolean use_xshm;
59
60   struct pipe_winsys *winsys;
61   struct ximage_config *configs;
62   int num_configs;
63};
64
65struct ximage_buffer {
66   XImage *ximage;
67
68   struct pipe_texture *texture;
69   XShmSegmentInfo *shm_info;
70   boolean xshm_attached;
71};
72
73struct ximage_surface {
74   struct native_surface base;
75   Drawable drawable;
76   enum ximage_surface_type type;
77   enum pipe_format color_format;
78   XVisualInfo visual;
79   struct ximage_display *xdpy;
80
81   int width, height;
82   GC gc;
83
84   struct ximage_buffer buffers[NUM_NATIVE_ATTACHMENTS];
85   unsigned int sequence_number;
86};
87
88struct ximage_config {
89   struct native_config base;
90   const XVisualInfo *visual;
91};
92
93static INLINE struct ximage_display *
94ximage_display(const struct native_display *ndpy)
95{
96   return (struct ximage_display *) ndpy;
97}
98
99static INLINE struct ximage_surface *
100ximage_surface(const struct native_surface *nsurf)
101{
102   return (struct ximage_surface *) nsurf;
103}
104
105static INLINE struct ximage_config *
106ximage_config(const struct native_config *nconf)
107{
108   return (struct ximage_config *) nconf;
109}
110
111static void
112ximage_surface_free_buffer(struct native_surface *nsurf,
113                           enum native_attachment which)
114{
115   struct ximage_surface *xsurf = ximage_surface(nsurf);
116   struct ximage_buffer *xbuf = &xsurf->buffers[which];
117
118   pipe_texture_reference(&xbuf->texture, NULL);
119
120   if (xbuf->shm_info) {
121      if (xbuf->xshm_attached)
122         XShmDetach(xsurf->xdpy->dpy, xbuf->shm_info);
123      if (xbuf->shm_info->shmaddr != (void *) -1)
124         shmdt(xbuf->shm_info->shmaddr);
125      if (xbuf->shm_info->shmid != -1)
126         shmctl(xbuf->shm_info->shmid, IPC_RMID, 0);
127
128      xbuf->shm_info->shmaddr = (void *) -1;
129      xbuf->shm_info->shmid = -1;
130   }
131}
132
133static boolean
134ximage_surface_alloc_buffer(struct native_surface *nsurf,
135                            enum native_attachment which)
136{
137   struct ximage_surface *xsurf = ximage_surface(nsurf);
138   struct ximage_buffer *xbuf = &xsurf->buffers[which];
139   struct pipe_screen *screen = xsurf->xdpy->base.screen;
140   struct pipe_texture templ;
141
142   /* free old data */
143   if (xbuf->texture)
144      ximage_surface_free_buffer(&xsurf->base, which);
145
146   memset(&templ, 0, sizeof(templ));
147   templ.target = PIPE_TEXTURE_2D;
148   templ.format = xsurf->color_format;
149   templ.width0 = xsurf->width;
150   templ.height0 = xsurf->height;
151   templ.depth0 = 1;
152   templ.tex_usage = PIPE_TEXTURE_USAGE_RENDER_TARGET;
153
154   if (xbuf->shm_info) {
155      struct pipe_buffer *pbuf;
156      unsigned stride, size;
157      void *addr = NULL;
158
159      stride = util_format_get_stride(xsurf->color_format, xsurf->width);
160      /* alignment should depend on visual? */
161      stride = align(stride, 4);
162      size = stride * xsurf->height;
163
164      /* create and attach shm object */
165      xbuf->shm_info->shmid = shmget(IPC_PRIVATE, size, 0755);
166      if (xbuf->shm_info->shmid != -1) {
167         xbuf->shm_info->shmaddr =
168            shmat(xbuf->shm_info->shmid, NULL, 0);
169         if (xbuf->shm_info->shmaddr != (void *) -1) {
170            if (XShmAttach(xsurf->xdpy->dpy, xbuf->shm_info)) {
171               addr = xbuf->shm_info->shmaddr;
172               xbuf->xshm_attached = TRUE;
173            }
174         }
175      }
176
177      if (addr) {
178         pbuf = screen->user_buffer_create(screen, addr, size);
179         if (pbuf) {
180            xbuf->texture =
181               screen->texture_blanket(screen, &templ, &stride, pbuf);
182            pipe_buffer_reference(&pbuf, NULL);
183         }
184      }
185   }
186   else {
187      xbuf->texture = screen->texture_create(screen, &templ);
188   }
189
190   /* clean up the buffer if allocation failed */
191   if (!xbuf->texture)
192      ximage_surface_free_buffer(&xsurf->base, which);
193
194   return (xbuf->texture != NULL);
195}
196
197static boolean
198ximage_surface_draw_buffer(struct native_surface *nsurf,
199                           enum native_attachment which)
200{
201   struct ximage_surface *xsurf = ximage_surface(nsurf);
202   struct ximage_buffer *xbuf = &xsurf->buffers[which];
203   struct pipe_screen *screen = xsurf->xdpy->base.screen;
204   struct pipe_transfer *transfer;
205
206   if (xsurf->type == XIMAGE_SURFACE_TYPE_PBUFFER)
207      return TRUE;
208
209   assert(xsurf->drawable && xbuf->ximage && xbuf->texture);
210
211   transfer = screen->get_tex_transfer(screen, xbuf->texture,
212         0, 0, 0, PIPE_TRANSFER_READ, 0, 0, xsurf->width, xsurf->height);
213   if (!transfer)
214      return FALSE;
215
216   xbuf->ximage->bytes_per_line = transfer->stride;
217   xbuf->ximage->data = screen->transfer_map(screen, transfer);
218   if (!xbuf->ximage->data) {
219      screen->tex_transfer_destroy(transfer);
220      return FALSE;
221   }
222
223
224   if (xbuf->shm_info)
225      XShmPutImage(xsurf->xdpy->dpy, xsurf->drawable, xsurf->gc,
226            xbuf->ximage, 0, 0, 0, 0, xsurf->width, xsurf->height, False);
227   else
228      XPutImage(xsurf->xdpy->dpy, xsurf->drawable, xsurf->gc,
229            xbuf->ximage, 0, 0, 0, 0, xsurf->width, xsurf->height);
230
231   xbuf->ximage->data = NULL;
232   screen->transfer_unmap(screen, transfer);
233
234   /*
235    * softpipe allows the pipe transfer to be re-used, but we don't want to
236    * rely on that behavior.
237    */
238   screen->tex_transfer_destroy(transfer);
239
240   XSync(xsurf->xdpy->dpy, FALSE);
241
242   return TRUE;
243}
244
245static boolean
246ximage_surface_flush_frontbuffer(struct native_surface *nsurf)
247{
248   return ximage_surface_draw_buffer(nsurf, NATIVE_ATTACHMENT_FRONT_LEFT);
249}
250
251static boolean
252ximage_surface_swap_buffers(struct native_surface *nsurf)
253{
254   struct ximage_surface *xsurf = ximage_surface(nsurf);
255   struct ximage_buffer *xfront, *xback, xtmp;
256
257   xfront = &xsurf->buffers[NATIVE_ATTACHMENT_FRONT_LEFT];
258   xback = &xsurf->buffers[NATIVE_ATTACHMENT_BACK_LEFT];
259
260   /* draw the back buffer directly if there is no front buffer */
261   if (!xfront->texture)
262      return ximage_surface_draw_buffer(nsurf, NATIVE_ATTACHMENT_BACK_LEFT);
263
264   /* swap the buffers */
265   xtmp = *xfront;
266   *xfront = *xback;
267   *xback = xtmp;
268
269   /* the front/back textures are swapped */
270   xsurf->sequence_number++;
271
272   return ximage_surface_draw_buffer(nsurf, NATIVE_ATTACHMENT_FRONT_LEFT);
273}
274
275static void
276ximage_surface_update_geometry(struct native_surface *nsurf)
277{
278   struct ximage_surface *xsurf = ximage_surface(nsurf);
279   Status ok;
280   Window root;
281   int x, y;
282   unsigned int w, h, border, depth;
283
284   /* pbuffer has fixed geometry */
285   if (xsurf->type == XIMAGE_SURFACE_TYPE_PBUFFER)
286      return;
287
288   ok = XGetGeometry(xsurf->xdpy->dpy, xsurf->drawable,
289         &root, &x, &y, &w, &h, &border, &depth);
290   if (ok) {
291      xsurf->width = w;
292      xsurf->height = h;
293   }
294}
295
296static boolean
297ximage_surface_validate(struct native_surface *nsurf, uint attachment_mask,
298                        unsigned int *seq_num, struct pipe_texture **textures,
299                        int *width, int *height)
300{
301   struct ximage_surface *xsurf = ximage_surface(nsurf);
302   boolean new_buffers = FALSE;
303   int att;
304
305   ximage_surface_update_geometry(&xsurf->base);
306
307   for (att = 0; att < NUM_NATIVE_ATTACHMENTS; att++) {
308      struct ximage_buffer *xbuf = &xsurf->buffers[att];
309
310      /* delay the allocation */
311      if (!native_attachment_mask_test(attachment_mask, att))
312         continue;
313
314      /* reallocate the texture */
315      if (!xbuf->texture ||
316          xsurf->width != xbuf->texture->width0 ||
317          xsurf->height != xbuf->texture->height0) {
318         new_buffers = TRUE;
319         if (ximage_surface_alloc_buffer(&xsurf->base, att)) {
320            /* update ximage */
321            if (xbuf->ximage) {
322               xbuf->ximage->width = xsurf->width;
323               xbuf->ximage->height = xsurf->height;
324            }
325         }
326      }
327
328      if (textures) {
329         textures[att] = NULL;
330         pipe_texture_reference(&textures[att], xbuf->texture);
331      }
332   }
333
334   /* increase the sequence number so that caller knows */
335   if (new_buffers)
336      xsurf->sequence_number++;
337
338   if (seq_num)
339      *seq_num = xsurf->sequence_number;
340   if (width)
341      *width = xsurf->width;
342   if (height)
343      *height = xsurf->height;
344
345   return TRUE;
346}
347
348static void
349ximage_surface_wait(struct native_surface *nsurf)
350{
351   struct ximage_surface *xsurf = ximage_surface(nsurf);
352   XSync(xsurf->xdpy->dpy, FALSE);
353   /* TODO XGetImage and update the front texture */
354}
355
356static void
357ximage_surface_destroy(struct native_surface *nsurf)
358{
359   struct ximage_surface *xsurf = ximage_surface(nsurf);
360   int i;
361
362   for (i = 0; i < NUM_NATIVE_ATTACHMENTS; i++) {
363      struct ximage_buffer *xbuf = &xsurf->buffers[i];
364      ximage_surface_free_buffer(&xsurf->base, i);
365      /* xbuf->shm_info is owned by xbuf->ximage? */
366      if (xbuf->ximage) {
367         XDestroyImage(xbuf->ximage);
368         xbuf->ximage = NULL;
369      }
370   }
371
372   if (xsurf->type != XIMAGE_SURFACE_TYPE_PBUFFER)
373      XFreeGC(xsurf->xdpy->dpy, xsurf->gc);
374   free(xsurf);
375}
376
377static struct ximage_surface *
378ximage_display_create_surface(struct native_display *ndpy,
379                              enum ximage_surface_type type,
380                              Drawable drawable,
381                              const struct native_config *nconf)
382{
383   struct ximage_display *xdpy = ximage_display(ndpy);
384   struct ximage_config *xconf = ximage_config(nconf);
385   struct ximage_surface *xsurf;
386   int i;
387
388   xsurf = CALLOC_STRUCT(ximage_surface);
389   if (!xsurf)
390      return NULL;
391
392   xsurf->xdpy = xdpy;
393   xsurf->type = type;
394   xsurf->color_format = xconf->base.color_format;
395   xsurf->drawable = drawable;
396
397   if (xsurf->type != XIMAGE_SURFACE_TYPE_PBUFFER) {
398      xsurf->drawable = drawable;
399      xsurf->visual = *xconf->visual;
400
401      xsurf->gc = XCreateGC(xdpy->dpy, xsurf->drawable, 0, NULL);
402      if (!xsurf->gc) {
403         free(xsurf);
404         return NULL;
405      }
406
407      for (i = 0; i < NUM_NATIVE_ATTACHMENTS; i++) {
408         struct ximage_buffer *xbuf = &xsurf->buffers[i];
409
410         if (xdpy->use_xshm) {
411            xbuf->shm_info = calloc(1, sizeof(*xbuf->shm_info));
412            if (xbuf->shm_info) {
413               /* initialize shm info */
414               xbuf->shm_info->shmid = -1;
415               xbuf->shm_info->shmaddr = (void *) -1;
416               xbuf->shm_info->readOnly = TRUE;
417
418               xbuf->ximage = XShmCreateImage(xsurf->xdpy->dpy,
419                     xsurf->visual.visual,
420                     xsurf->visual.depth,
421                     ZPixmap, NULL,
422                     xbuf->shm_info,
423                     0, 0);
424            }
425         }
426         else {
427            xbuf->ximage = XCreateImage(xsurf->xdpy->dpy,
428                  xsurf->visual.visual,
429                  xsurf->visual.depth,
430                  ZPixmap, 0,   /* format, offset */
431                  NULL,         /* data */
432                  0, 0,         /* size */
433                  8,            /* bitmap_pad */
434                  0);           /* bytes_per_line */
435         }
436
437         if (!xbuf->ximage) {
438            XFreeGC(xdpy->dpy, xsurf->gc);
439            free(xsurf);
440            return NULL;
441         }
442      }
443   }
444
445   xsurf->base.destroy = ximage_surface_destroy;
446   xsurf->base.swap_buffers = ximage_surface_swap_buffers;
447   xsurf->base.flush_frontbuffer = ximage_surface_flush_frontbuffer;
448   xsurf->base.validate = ximage_surface_validate;
449   xsurf->base.wait = ximage_surface_wait;
450
451   return xsurf;
452}
453
454static struct native_surface *
455ximage_display_create_window_surface(struct native_display *ndpy,
456                                     EGLNativeWindowType win,
457                                     const struct native_config *nconf)
458{
459   struct ximage_surface *xsurf;
460
461   xsurf = ximage_display_create_surface(ndpy, XIMAGE_SURFACE_TYPE_WINDOW,
462         (Drawable) win, nconf);
463   return (xsurf) ? &xsurf->base : NULL;
464}
465
466static struct native_surface *
467ximage_display_create_pixmap_surface(struct native_display *ndpy,
468                                     EGLNativePixmapType pix,
469                                     const struct native_config *nconf)
470{
471   struct ximage_surface *xsurf;
472
473   xsurf = ximage_display_create_surface(ndpy, XIMAGE_SURFACE_TYPE_PIXMAP,
474         (Drawable) pix, nconf);
475   return (xsurf) ? &xsurf->base : NULL;
476}
477
478static struct native_surface *
479ximage_display_create_pbuffer_surface(struct native_display *ndpy,
480                                      const struct native_config *nconf,
481                                      uint width, uint height)
482{
483   struct ximage_surface *xsurf;
484
485   xsurf = ximage_display_create_surface(ndpy, XIMAGE_SURFACE_TYPE_PBUFFER,
486         (Drawable) None, nconf);
487   if (xsurf) {
488      xsurf->width = width;
489      xsurf->height = height;
490   }
491   return (xsurf) ? &xsurf->base : NULL;
492}
493
494static struct pipe_context *
495ximage_display_create_context(struct native_display *ndpy,
496                              void *context_private)
497{
498   struct pipe_context *pctx = softpipe_create(ndpy->screen);
499   if (pctx)
500      pctx->priv = context_private;
501   return pctx;
502}
503
504static enum pipe_format
505choose_format(const XVisualInfo *vinfo)
506{
507   enum pipe_format fmt;
508   /* TODO elaborate the formats */
509   switch (vinfo->depth) {
510   case 32:
511      fmt = PIPE_FORMAT_A8R8G8B8_UNORM;
512      break;
513   case 24:
514      fmt = PIPE_FORMAT_X8R8G8B8_UNORM;
515      break;
516   case 16:
517      fmt = PIPE_FORMAT_R5G6B5_UNORM;
518      break;
519   default:
520      fmt = PIPE_FORMAT_NONE;
521      break;
522   }
523
524   return fmt;
525}
526
527static const struct native_config **
528ximage_display_get_configs(struct native_display *ndpy, int *num_configs)
529{
530   struct ximage_display *xdpy = ximage_display(ndpy);
531   const struct native_config **configs;
532   int i;
533
534   /* first time */
535   if (!xdpy->configs) {
536      const XVisualInfo *visuals;
537      int num_visuals, count, j;
538
539      visuals = x11_screen_get_visuals(xdpy->xscr, &num_visuals);
540      if (!visuals)
541         return NULL;
542
543      /*
544       * Create two configs for each visual.
545       * One with depth/stencil buffer; one without
546       */
547      xdpy->configs = calloc(num_visuals * 2, sizeof(*xdpy->configs));
548      if (!xdpy->configs)
549         return NULL;
550
551      count = 0;
552      for (i = 0; i < num_visuals; i++) {
553         for (j = 0; j < 2; j++) {
554            struct ximage_config *xconf = &xdpy->configs[count];
555            __GLcontextModes *mode = &xconf->base.mode;
556
557            xconf->visual = &visuals[i];
558            xconf->base.color_format = choose_format(xconf->visual);
559            if (xconf->base.color_format == PIPE_FORMAT_NONE)
560               continue;
561
562            x11_screen_convert_visual(xdpy->xscr, xconf->visual, mode);
563            /* support double buffer mode */
564            mode->doubleBufferMode = TRUE;
565
566            xconf->base.depth_format = PIPE_FORMAT_NONE;
567            xconf->base.stencil_format = PIPE_FORMAT_NONE;
568            /* create the second config with depth/stencil buffer */
569            if (j == 1) {
570               xconf->base.depth_format = PIPE_FORMAT_S8Z24_UNORM;
571               xconf->base.stencil_format = PIPE_FORMAT_S8Z24_UNORM;
572               mode->depthBits = 24;
573               mode->stencilBits = 8;
574               mode->haveDepthBuffer = TRUE;
575               mode->haveStencilBuffer = TRUE;
576            }
577
578            mode->maxPbufferWidth = 4096;
579            mode->maxPbufferHeight = 4096;
580            mode->maxPbufferPixels = 4096 * 4096;
581            mode->drawableType =
582               GLX_WINDOW_BIT | GLX_PIXMAP_BIT | GLX_PBUFFER_BIT;
583            mode->swapMethod = GLX_SWAP_EXCHANGE_OML;
584
585            if (mode->alphaBits)
586               mode->bindToTextureRgba = TRUE;
587            else
588               mode->bindToTextureRgb = TRUE;
589
590            count++;
591         }
592      }
593
594      xdpy->num_configs = count;
595   }
596
597   configs = malloc(xdpy->num_configs * sizeof(*configs));
598   if (configs) {
599      for (i = 0; i < xdpy->num_configs; i++)
600         configs[i] = (const struct native_config *) &xdpy->configs[i];
601      if (num_configs)
602         *num_configs = xdpy->num_configs;
603   }
604   return configs;
605}
606
607static boolean
608ximage_display_is_pixmap_supported(struct native_display *ndpy,
609                                   EGLNativePixmapType pix,
610                                   const struct native_config *nconf)
611{
612   struct ximage_display *xdpy = ximage_display(ndpy);
613   enum pipe_format fmt;
614   uint depth;
615
616   depth = x11_drawable_get_depth(xdpy->xscr, (Drawable) pix);
617   switch (depth) {
618   case 32:
619      fmt = PIPE_FORMAT_A8R8G8B8_UNORM;
620      break;
621   case 24:
622      fmt = PIPE_FORMAT_X8R8G8B8_UNORM;
623      break;
624   case 16:
625      fmt = PIPE_FORMAT_R5G6B5_UNORM;
626      break;
627   default:
628      fmt = PIPE_FORMAT_NONE;
629      break;
630   }
631
632   return (fmt == nconf->color_format);
633}
634
635static void
636ximage_display_destroy(struct native_display *ndpy)
637{
638   struct ximage_display *xdpy = ximage_display(ndpy);
639
640   if (xdpy->configs)
641      free(xdpy->configs);
642
643   xdpy->base.screen->destroy(xdpy->base.screen);
644   free(xdpy->winsys);
645
646   x11_screen_destroy(xdpy->xscr);
647   if (xdpy->own_dpy)
648      XCloseDisplay(xdpy->dpy);
649   free(xdpy);
650}
651
652struct native_display *
653x11_create_ximage_display(EGLNativeDisplayType dpy, boolean use_xshm)
654{
655   struct ximage_display *xdpy;
656
657   xdpy = CALLOC_STRUCT(ximage_display);
658   if (!xdpy)
659      return NULL;
660
661   xdpy->dpy = dpy;
662   if (!xdpy->dpy) {
663      xdpy->dpy = XOpenDisplay(NULL);
664      if (!xdpy->dpy) {
665         free(xdpy);
666         return NULL;
667      }
668      xdpy->own_dpy = TRUE;
669   }
670
671   xdpy->xscr_number = DefaultScreen(xdpy->dpy);
672   xdpy->xscr = x11_screen_create(xdpy->dpy, xdpy->xscr_number);
673   if (!xdpy->xscr) {
674      free(xdpy);
675      return NULL;
676   }
677
678   xdpy->use_xshm =
679      (use_xshm && x11_screen_support(xdpy->xscr, X11_SCREEN_EXTENSION_XSHM));
680
681   xdpy->winsys = create_sw_winsys();
682   xdpy->base.screen = softpipe_create_screen(xdpy->winsys);
683
684   xdpy->base.destroy = ximage_display_destroy;
685
686   xdpy->base.get_configs = ximage_display_get_configs;
687   xdpy->base.is_pixmap_supported = ximage_display_is_pixmap_supported;
688   xdpy->base.create_context = ximage_display_create_context;
689   xdpy->base.create_window_surface = ximage_display_create_window_surface;
690   xdpy->base.create_pixmap_surface = ximage_display_create_pixmap_surface;
691   xdpy->base.create_pbuffer_surface = ximage_display_create_pbuffer_surface;
692
693   return &xdpy->base;
694}
695