drisw_glx.c revision b24f291e429b94ca7de74f8b32279e3a0375cd9c
1/*
2 * Copyright 2008 George Sapountzis
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 */
23
24#if defined(GLX_DIRECT_RENDERING) && !defined(GLX_USE_APPLEGL)
25
26#include <X11/Xlib.h>
27#include "glxclient.h"
28#include <dlfcn.h>
29#include "dri_common.h"
30
31struct drisw_display
32{
33   __GLXDRIdisplay base;
34};
35
36struct drisw_context
37{
38   struct glx_context base;
39   __DRIcontext *driContext;
40
41};
42
43struct drisw_screen
44{
45   struct glx_screen base;
46
47   __DRIscreen *driScreen;
48   __GLXDRIscreen vtable;
49   const __DRIcoreExtension *core;
50   const __DRIswrastExtension *swrast;
51   const __DRIconfig **driver_configs;
52
53   void *driver;
54};
55
56struct drisw_drawable
57{
58   __GLXDRIdrawable base;
59
60   GC gc;
61   GC swapgc;
62
63   __DRIdrawable *driDrawable;
64   XVisualInfo *visinfo;
65   XImage *ximage;
66};
67
68static Bool
69XCreateDrawable(struct drisw_drawable * pdp,
70                Display * dpy, XID drawable, int visualid)
71{
72   XGCValues gcvalues;
73   long visMask;
74   XVisualInfo visTemp;
75   int num_visuals;
76
77   /* create GC's */
78   pdp->gc = XCreateGC(dpy, drawable, 0, NULL);
79   pdp->swapgc = XCreateGC(dpy, drawable, 0, NULL);
80
81   gcvalues.function = GXcopy;
82   gcvalues.graphics_exposures = False;
83   XChangeGC(dpy, pdp->gc, GCFunction, &gcvalues);
84   XChangeGC(dpy, pdp->swapgc, GCFunction, &gcvalues);
85   XChangeGC(dpy, pdp->swapgc, GCGraphicsExposures, &gcvalues);
86
87   /* visual */
88   visTemp.screen = DefaultScreen(dpy);
89   visTemp.visualid = visualid;
90   visMask = (VisualScreenMask | VisualIDMask);
91   pdp->visinfo = XGetVisualInfo(dpy, visMask, &visTemp, &num_visuals);
92
93   /* create XImage */
94   pdp->ximage = XCreateImage(dpy,
95                              pdp->visinfo->visual,
96                              pdp->visinfo->depth,
97                              ZPixmap, 0,             /* format, offset */
98                              NULL,                   /* data */
99                              0, 0,                   /* width, height */
100                              32,                     /* bitmap_pad */
101                              0);                     /* bytes_per_line */
102
103   return True;
104}
105
106static void
107XDestroyDrawable(struct drisw_drawable * pdp, Display * dpy, XID drawable)
108{
109   XDestroyImage(pdp->ximage);
110   XFree(pdp->visinfo);
111
112   XFreeGC(dpy, pdp->gc);
113   XFreeGC(dpy, pdp->swapgc);
114}
115
116/**
117 * swrast loader functions
118 */
119
120static void
121swrastGetDrawableInfo(__DRIdrawable * draw,
122                      int *x, int *y, int *w, int *h,
123                      void *loaderPrivate)
124{
125   struct drisw_drawable *pdp = loaderPrivate;
126   __GLXDRIdrawable *pdraw = &(pdp->base);
127   Display *dpy = pdraw->psc->dpy;
128   Drawable drawable;
129
130   Window root;
131   unsigned uw, uh, bw, depth;
132
133   drawable = pdraw->xDrawable;
134
135   XGetGeometry(dpy, drawable, &root, x, y, &uw, &uh, &bw, &depth);
136   *w = uw;
137   *h = uh;
138}
139
140/**
141 * Align renderbuffer pitch.
142 *
143 * This should be chosen by the driver and the loader (libGL, xserver/glx)
144 * should use the driver provided pitch.
145 *
146 * It seems that the xorg loader (that is the xserver loading swrast_dri for
147 * indirect rendering, not client-side libGL) requires that the pitch is
148 * exactly the image width padded to 32 bits. XXX
149 *
150 * The above restriction can probably be overcome by using ScratchPixmap and
151 * CopyArea in the xserver, similar to ShmPutImage, and setting the width of
152 * the scratch pixmap to 'pitch / cpp'.
153 */
154static inline int
155bytes_per_line(unsigned pitch_bits, unsigned mul)
156{
157   unsigned mask = mul - 1;
158
159   return ((pitch_bits + mask) & ~mask) / 8;
160}
161
162static void
163swrastPutImage(__DRIdrawable * draw, int op,
164               int x, int y, int w, int h,
165               char *data, void *loaderPrivate)
166{
167   struct drisw_drawable *pdp = loaderPrivate;
168   __GLXDRIdrawable *pdraw = &(pdp->base);
169   Display *dpy = pdraw->psc->dpy;
170   Drawable drawable;
171   XImage *ximage;
172   GC gc;
173
174   switch (op) {
175   case __DRI_SWRAST_IMAGE_OP_DRAW:
176      gc = pdp->gc;
177      break;
178   case __DRI_SWRAST_IMAGE_OP_SWAP:
179      gc = pdp->swapgc;
180      break;
181   default:
182      return;
183   }
184
185   drawable = pdraw->xDrawable;
186
187   ximage = pdp->ximage;
188   ximage->data = data;
189   ximage->width = w;
190   ximage->height = h;
191   ximage->bytes_per_line = bytes_per_line(w * ximage->bits_per_pixel, 32);
192
193   XPutImage(dpy, drawable, gc, ximage, 0, 0, x, y, w, h);
194
195   ximage->data = NULL;
196}
197
198static void
199swrastGetImage(__DRIdrawable * read,
200               int x, int y, int w, int h,
201               char *data, void *loaderPrivate)
202{
203   struct drisw_drawable *prp = loaderPrivate;
204   __GLXDRIdrawable *pread = &(prp->base);
205   Display *dpy = pread->psc->dpy;
206   Drawable readable;
207   XImage *ximage;
208
209   readable = pread->xDrawable;
210
211   ximage = prp->ximage;
212   ximage->data = data;
213   ximage->width = w;
214   ximage->height = h;
215   ximage->bytes_per_line = bytes_per_line(w * ximage->bits_per_pixel, 32);
216
217   XGetSubImage(dpy, readable, x, y, w, h, ~0L, ZPixmap, ximage, 0, 0);
218
219   ximage->data = NULL;
220}
221
222static const __DRIswrastLoaderExtension swrastLoaderExtension = {
223   {__DRI_SWRAST_LOADER, __DRI_SWRAST_LOADER_VERSION},
224   swrastGetDrawableInfo,
225   swrastPutImage,
226   swrastGetImage
227};
228
229static const __DRIextension *loader_extensions[] = {
230   &systemTimeExtension.base,
231   &swrastLoaderExtension.base,
232   NULL
233};
234
235/**
236 * GLXDRI functions
237 */
238
239static void
240drisw_destroy_context(struct glx_context *context)
241{
242   struct drisw_context *pcp = (struct drisw_context *) context;
243   struct drisw_screen *psc = (struct drisw_screen *) context->psc;
244
245   if (context->xid)
246      glx_send_destroy_context(psc->base.dpy, context->xid);
247
248   if (context->extensions)
249      XFree((char *) context->extensions);
250
251   (*psc->core->destroyContext) (pcp->driContext);
252
253   Xfree(pcp);
254}
255
256static int
257drisw_bind_context(struct glx_context *context, struct glx_context *old,
258		   GLXDrawable draw, GLXDrawable read)
259{
260   struct drisw_context *pcp = (struct drisw_context *) context;
261   struct drisw_screen *psc = (struct drisw_screen *) pcp->base.psc;
262   struct drisw_drawable *pdraw, *pread;
263
264   pdraw = (struct drisw_drawable *) driFetchDrawable(context, draw);
265   pread = (struct drisw_drawable *) driFetchDrawable(context, read);
266
267   if (pdraw == NULL || pread == NULL)
268      return GLXBadDrawable;
269
270   if ((*psc->core->bindContext) (pcp->driContext,
271				  pdraw->driDrawable, pread->driDrawable))
272      return Success;
273
274   return GLXBadContext;
275}
276
277static void
278drisw_unbind_context(struct glx_context *context, struct glx_context *new)
279{
280   struct drisw_context *pcp = (struct drisw_context *) context;
281   struct drisw_screen *psc = (struct drisw_screen *) pcp->base.psc;
282
283   (*psc->core->unbindContext) (pcp->driContext);
284
285   driReleaseDrawables(&pcp->base);
286}
287
288static const struct glx_context_vtable drisw_context_vtable = {
289   drisw_destroy_context,
290   drisw_bind_context,
291   drisw_unbind_context,
292   NULL,
293   NULL,
294   DRI_glXUseXFont,
295   NULL,
296   NULL,
297};
298
299static struct glx_context *
300drisw_create_context(struct glx_screen *base,
301		     struct glx_config *config_base,
302		     struct glx_context *shareList, int renderType)
303{
304   struct drisw_context *pcp, *pcp_shared;
305   __GLXDRIconfigPrivate *config = (__GLXDRIconfigPrivate *) config_base;
306   struct drisw_screen *psc = (struct drisw_screen *) base;
307   __DRIcontext *shared = NULL;
308
309   if (!psc->base.driScreen)
310      return NULL;
311
312   if (shareList) {
313      pcp_shared = (struct drisw_context *) shareList;
314      shared = pcp_shared->driContext;
315   }
316
317   pcp = Xmalloc(sizeof *pcp);
318   if (pcp == NULL)
319      return NULL;
320
321   memset(pcp, 0, sizeof *pcp);
322   if (!glx_context_init(&pcp->base, &psc->base, &config->base)) {
323      Xfree(pcp);
324      return NULL;
325   }
326
327   pcp->driContext =
328      (*psc->core->createNewContext) (psc->driScreen,
329				      config->driConfig, shared, pcp);
330   if (pcp->driContext == NULL) {
331      Xfree(pcp);
332      return NULL;
333   }
334
335   pcp->base.vtable = &drisw_context_vtable;
336
337   return &pcp->base;
338}
339
340static void
341driDestroyDrawable(__GLXDRIdrawable * pdraw)
342{
343   struct drisw_drawable *pdp = (struct drisw_drawable *) pdraw;
344   struct drisw_screen *psc = (struct drisw_screen *) pdp->base.psc;
345
346   (*psc->core->destroyDrawable) (pdp->driDrawable);
347
348   XDestroyDrawable(pdp, pdraw->psc->dpy, pdraw->drawable);
349   Xfree(pdp);
350}
351
352static __GLXDRIdrawable *
353driCreateDrawable(struct glx_screen *base, XID xDrawable,
354		  GLXDrawable drawable, struct glx_config *modes)
355{
356   struct drisw_drawable *pdp;
357   __GLXDRIconfigPrivate *config = (__GLXDRIconfigPrivate *) modes;
358   struct drisw_screen *psc = (struct drisw_screen *) base;
359
360   const __DRIswrastExtension *swrast = psc->swrast;
361
362   /* Old dri can't handle GLX 1.3+ drawable constructors. */
363   if (xDrawable != drawable)
364      return NULL;
365
366   pdp = Xmalloc(sizeof(*pdp));
367   if (!pdp)
368      return NULL;
369
370   memset(pdp, 0, sizeof *pdp);
371   pdp->base.xDrawable = xDrawable;
372   pdp->base.drawable = drawable;
373   pdp->base.psc = &psc->base;
374
375   XCreateDrawable(pdp, psc->base.dpy, xDrawable, modes->visualID);
376
377   /* Create a new drawable */
378   pdp->driDrawable =
379      (*swrast->createNewDrawable) (psc->driScreen, config->driConfig, pdp);
380
381   if (!pdp->driDrawable) {
382      XDestroyDrawable(pdp, psc->base.dpy, xDrawable);
383      Xfree(pdp);
384      return NULL;
385   }
386
387   pdp->base.destroyDrawable = driDestroyDrawable;
388
389   return &pdp->base;
390}
391
392static int64_t
393driSwapBuffers(__GLXDRIdrawable * pdraw,
394               int64_t target_msc, int64_t divisor, int64_t remainder)
395{
396   struct drisw_drawable *pdp = (struct drisw_drawable *) pdraw;
397   struct drisw_screen *psc = (struct drisw_screen *) pdp->base.psc;
398
399   (void) target_msc;
400   (void) divisor;
401   (void) remainder;
402
403   (*psc->core->swapBuffers) (pdp->driDrawable);
404
405   return 0;
406}
407
408static void
409driDestroyScreen(struct glx_screen *base)
410{
411   struct drisw_screen *psc = (struct drisw_screen *) base;
412
413   /* Free the direct rendering per screen data */
414   (*psc->core->destroyScreen) (psc->driScreen);
415   driDestroyConfigs(psc->driver_configs);
416   psc->driScreen = NULL;
417   if (psc->driver)
418      dlclose(psc->driver);
419}
420
421static void *
422driOpenSwrast(void)
423{
424   void *driver = NULL;
425
426   if (driver == NULL)
427      driver = driOpenDriver("swrast");
428
429   if (driver == NULL)
430      driver = driOpenDriver("swrastg");
431
432   return driver;
433}
434
435static const struct glx_screen_vtable drisw_screen_vtable = {
436   drisw_create_context
437};
438
439static struct glx_screen *
440driCreateScreen(int screen, struct glx_display *priv)
441{
442   __GLXDRIscreen *psp;
443   const __DRIconfig **driver_configs;
444   const __DRIextension **extensions;
445   struct drisw_screen *psc;
446   int i;
447
448   psc = Xcalloc(1, sizeof *psc);
449   if (psc == NULL)
450      return NULL;
451
452   memset(psc, 0, sizeof *psc);
453   if (!glx_screen_init(&psc->base, screen, priv)) {
454      Xfree(psc);
455      return NULL;
456   }
457
458   psc->driver = driOpenSwrast();
459   if (psc->driver == NULL)
460      goto handle_error;
461
462   extensions = dlsym(psc->driver, __DRI_DRIVER_EXTENSIONS);
463   if (extensions == NULL) {
464      ErrorMessageF("driver exports no extensions (%s)\n", dlerror());
465      goto handle_error;
466   }
467
468   for (i = 0; extensions[i]; i++) {
469      if (strcmp(extensions[i]->name, __DRI_CORE) == 0)
470	 psc->core = (__DRIcoreExtension *) extensions[i];
471      if (strcmp(extensions[i]->name, __DRI_SWRAST) == 0)
472	 psc->swrast = (__DRIswrastExtension *) extensions[i];
473   }
474
475   if (psc->core == NULL || psc->swrast == NULL) {
476      ErrorMessageF("core dri extension not found\n");
477      goto handle_error;
478   }
479
480   psc->driScreen =
481      psc->swrast->createNewScreen(screen, loader_extensions,
482				   &driver_configs, psc);
483   if (psc->driScreen == NULL) {
484      ErrorMessageF("failed to create dri screen\n");
485      goto handle_error;
486   }
487
488   psc->base.configs =
489      driConvertConfigs(psc->core, psc->base.configs, driver_configs);
490   psc->base.visuals =
491      driConvertConfigs(psc->core, psc->base.visuals, driver_configs);
492
493   psc->driver_configs = driver_configs;
494
495   psc->base.vtable = &drisw_screen_vtable;
496   psp = &psc->vtable;
497   psc->base.driScreen = psp;
498   psp->destroyScreen = driDestroyScreen;
499   psp->createDrawable = driCreateDrawable;
500   psp->swapBuffers = driSwapBuffers;
501
502   return &psc->base;
503
504 handle_error:
505   if (psc->driver)
506      dlclose(psc->driver);
507   glx_screen_cleanup(&psc->base);
508   Xfree(psc);
509
510   ErrorMessageF("reverting to indirect rendering\n");
511
512   return NULL;
513}
514
515/* Called from __glXFreeDisplayPrivate.
516 */
517static void
518driDestroyDisplay(__GLXDRIdisplay * dpy)
519{
520   Xfree(dpy);
521}
522
523/*
524 * Allocate, initialize and return a __DRIdisplayPrivate object.
525 * This is called from __glXInitialize() when we are given a new
526 * display pointer.
527 */
528_X_HIDDEN __GLXDRIdisplay *
529driswCreateDisplay(Display * dpy)
530{
531   struct drisw_display *pdpyp;
532
533   pdpyp = Xmalloc(sizeof *pdpyp);
534   if (pdpyp == NULL)
535      return NULL;
536
537   pdpyp->base.destroyDisplay = driDestroyDisplay;
538   pdpyp->base.createScreen = driCreateScreen;
539
540   return &pdpyp->base;
541}
542
543#endif /* GLX_DIRECT_RENDERING */
544