drisw_glx.c revision e71c44cedd6d22930bf873504d89faf68d93e1cc
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   Status stat;
132   unsigned uw, uh, bw, depth;
133
134   drawable = pdraw->xDrawable;
135
136   stat = XGetGeometry(dpy, drawable, &root,
137                       x, y, &uw, &uh, &bw, &depth);
138   *w = uw;
139   *h = uh;
140}
141
142/**
143 * Align renderbuffer pitch.
144 *
145 * This should be chosen by the driver and the loader (libGL, xserver/glx)
146 * should use the driver provided pitch.
147 *
148 * It seems that the xorg loader (that is the xserver loading swrast_dri for
149 * indirect rendering, not client-side libGL) requires that the pitch is
150 * exactly the image width padded to 32 bits. XXX
151 *
152 * The above restriction can probably be overcome by using ScratchPixmap and
153 * CopyArea in the xserver, similar to ShmPutImage, and setting the width of
154 * the scratch pixmap to 'pitch / cpp'.
155 */
156static inline int
157bytes_per_line(unsigned pitch_bits, unsigned mul)
158{
159   unsigned mask = mul - 1;
160
161   return ((pitch_bits + mask) & ~mask) / 8;
162}
163
164static void
165swrastPutImage(__DRIdrawable * draw, int op,
166               int x, int y, int w, int h,
167               char *data, void *loaderPrivate)
168{
169   struct drisw_drawable *pdp = loaderPrivate;
170   __GLXDRIdrawable *pdraw = &(pdp->base);
171   Display *dpy = pdraw->psc->dpy;
172   Drawable drawable;
173   XImage *ximage;
174   GC gc;
175
176   switch (op) {
177   case __DRI_SWRAST_IMAGE_OP_DRAW:
178      gc = pdp->gc;
179      break;
180   case __DRI_SWRAST_IMAGE_OP_SWAP:
181      gc = pdp->swapgc;
182      break;
183   default:
184      return;
185   }
186
187   drawable = pdraw->xDrawable;
188
189   ximage = pdp->ximage;
190   ximage->data = data;
191   ximage->width = w;
192   ximage->height = h;
193   ximage->bytes_per_line = bytes_per_line(w * ximage->bits_per_pixel, 32);
194
195   XPutImage(dpy, drawable, gc, ximage, 0, 0, x, y, w, h);
196
197   ximage->data = NULL;
198}
199
200static void
201swrastGetImage(__DRIdrawable * read,
202               int x, int y, int w, int h,
203               char *data, void *loaderPrivate)
204{
205   struct drisw_drawable *prp = loaderPrivate;
206   __GLXDRIdrawable *pread = &(prp->base);
207   Display *dpy = pread->psc->dpy;
208   Drawable readable;
209   XImage *ximage;
210
211   readable = pread->xDrawable;
212
213   ximage = prp->ximage;
214   ximage->data = data;
215   ximage->width = w;
216   ximage->height = h;
217   ximage->bytes_per_line = bytes_per_line(w * ximage->bits_per_pixel, 32);
218
219   XGetSubImage(dpy, readable, x, y, w, h, ~0L, ZPixmap, ximage, 0, 0);
220
221   ximage->data = NULL;
222}
223
224static const __DRIswrastLoaderExtension swrastLoaderExtension = {
225   {__DRI_SWRAST_LOADER, __DRI_SWRAST_LOADER_VERSION},
226   swrastGetDrawableInfo,
227   swrastPutImage,
228   swrastGetImage
229};
230
231static const __DRIextension *loader_extensions[] = {
232   &systemTimeExtension.base,
233   &swrastLoaderExtension.base,
234   NULL
235};
236
237/**
238 * GLXDRI functions
239 */
240
241static void
242drisw_destroy_context(struct glx_context *context)
243{
244   struct drisw_context *pcp = (struct drisw_context *) context;
245   struct drisw_screen *psc = (struct drisw_screen *) context->psc;
246
247   if (context->xid)
248      glx_send_destroy_context(psc->base.dpy, context->xid);
249
250   if (context->extensions)
251      XFree((char *) context->extensions);
252
253   GarbageCollectDRIDrawables(context->psc);
254
255   (*psc->core->destroyContext) (pcp->driContext);
256
257   Xfree(pcp);
258}
259
260static int
261drisw_bind_context(struct glx_context *context, struct glx_context *old,
262		   GLXDrawable draw, GLXDrawable read)
263{
264   struct drisw_context *pcp = (struct drisw_context *) context;
265   struct drisw_screen *psc = (struct drisw_screen *) pcp->base.psc;
266   struct drisw_drawable *pdraw, *pread;
267
268   pdraw = (struct drisw_drawable *) driFetchDrawable(context, draw);
269   pread = (struct drisw_drawable *) driFetchDrawable(context, read);
270
271   if (pdraw == NULL || pread == NULL)
272      return GLXBadDrawable;
273
274   if ((*psc->core->bindContext) (pcp->driContext,
275				  pdraw->driDrawable, pread->driDrawable))
276      return Success;
277
278   return GLXBadContext;
279}
280
281static void
282drisw_unbind_context(struct glx_context *context, struct glx_context *new)
283{
284   struct drisw_context *pcp = (struct drisw_context *) context;
285   struct drisw_screen *psc = (struct drisw_screen *) pcp->base.psc;
286
287   (*psc->core->unbindContext) (pcp->driContext);
288}
289
290static const struct glx_context_vtable drisw_context_vtable = {
291   drisw_destroy_context,
292   drisw_bind_context,
293   drisw_unbind_context,
294   NULL,
295   NULL,
296   DRI_glXUseXFont,
297   NULL,
298   NULL,
299};
300
301static struct glx_context *
302drisw_create_context(struct glx_screen *base,
303		     struct glx_config *config_base,
304		     struct glx_context *shareList, int renderType)
305{
306   struct drisw_context *pcp, *pcp_shared;
307   __GLXDRIconfigPrivate *config = (__GLXDRIconfigPrivate *) config_base;
308   struct drisw_screen *psc = (struct drisw_screen *) base;
309   __DRIcontext *shared = NULL;
310
311   if (!psc->base.driScreen)
312      return NULL;
313
314   if (shareList) {
315      pcp_shared = (struct drisw_context *) shareList;
316      shared = pcp_shared->driContext;
317   }
318
319   pcp = Xmalloc(sizeof *pcp);
320   if (pcp == NULL)
321      return NULL;
322
323   memset(pcp, 0, sizeof *pcp);
324   if (!glx_context_init(&pcp->base, &psc->base, &config->base)) {
325      Xfree(pcp);
326      return NULL;
327   }
328
329   pcp->driContext =
330      (*psc->core->createNewContext) (psc->driScreen,
331				      config->driConfig, shared, pcp);
332   if (pcp->driContext == NULL) {
333      Xfree(pcp);
334      return NULL;
335   }
336
337   pcp->base.vtable = &drisw_context_vtable;
338
339   return &pcp->base;
340}
341
342static void
343driDestroyDrawable(__GLXDRIdrawable * pdraw)
344{
345   struct drisw_drawable *pdp = (struct drisw_drawable *) pdraw;
346   struct drisw_screen *psc = (struct drisw_screen *) pdp->base.psc;
347
348   (*psc->core->destroyDrawable) (pdp->driDrawable);
349
350   XDestroyDrawable(pdp, pdraw->psc->dpy, pdraw->drawable);
351   Xfree(pdp);
352}
353
354static __GLXDRIdrawable *
355driCreateDrawable(struct glx_screen *base, XID xDrawable,
356		  GLXDrawable drawable, struct glx_config *modes)
357{
358   struct drisw_drawable *pdp;
359   __GLXDRIconfigPrivate *config = (__GLXDRIconfigPrivate *) modes;
360   struct drisw_screen *psc = (struct drisw_screen *) base;
361
362   const __DRIswrastExtension *swrast = psc->swrast;
363
364   /* Old dri can't handle GLX 1.3+ drawable constructors. */
365   if (xDrawable != drawable)
366      return NULL;
367
368   pdp = Xmalloc(sizeof(*pdp));
369   if (!pdp)
370      return NULL;
371
372   memset(pdp, 0, sizeof *pdp);
373   pdp->base.xDrawable = xDrawable;
374   pdp->base.drawable = drawable;
375   pdp->base.psc = &psc->base;
376
377   XCreateDrawable(pdp, psc->base.dpy, xDrawable, modes->visualID);
378
379   /* Create a new drawable */
380   pdp->driDrawable =
381      (*swrast->createNewDrawable) (psc->driScreen, config->driConfig, pdp);
382
383   if (!pdp->driDrawable) {
384      XDestroyDrawable(pdp, psc->base.dpy, xDrawable);
385      Xfree(pdp);
386      return NULL;
387   }
388
389   pdp->base.destroyDrawable = driDestroyDrawable;
390
391   return &pdp->base;
392}
393
394static int64_t
395driSwapBuffers(__GLXDRIdrawable * pdraw,
396               int64_t target_msc, int64_t divisor, int64_t remainder)
397{
398   struct drisw_drawable *pdp = (struct drisw_drawable *) pdraw;
399   struct drisw_screen *psc = (struct drisw_screen *) pdp->base.psc;
400
401   (void) target_msc;
402   (void) divisor;
403   (void) remainder;
404
405   (*psc->core->swapBuffers) (pdp->driDrawable);
406
407   return 0;
408}
409
410static void
411driDestroyScreen(struct glx_screen *base)
412{
413   struct drisw_screen *psc = (struct drisw_screen *) base;
414
415   /* Free the direct rendering per screen data */
416   (*psc->core->destroyScreen) (psc->driScreen);
417   driDestroyConfigs(psc->driver_configs);
418   psc->driScreen = NULL;
419   if (psc->driver)
420      dlclose(psc->driver);
421}
422
423static void *
424driOpenSwrast(void)
425{
426   void *driver = NULL;
427
428   if (driver == NULL)
429      driver = driOpenDriver("swrast");
430
431   if (driver == NULL)
432      driver = driOpenDriver("swrastg");
433
434   return driver;
435}
436
437static const struct glx_screen_vtable drisw_screen_vtable = {
438   drisw_create_context
439};
440
441static struct glx_screen *
442driCreateScreen(int screen, struct glx_display *priv)
443{
444   __GLXDRIscreen *psp;
445   const __DRIconfig **driver_configs;
446   const __DRIextension **extensions;
447   struct drisw_screen *psc;
448   int i;
449
450   psc = Xcalloc(1, sizeof *psc);
451   if (psc == NULL)
452      return NULL;
453
454   memset(psc, 0, sizeof *psc);
455   if (!glx_screen_init(&psc->base, screen, priv))
456       return NULL;
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   Xfree(psc);
506
507   if (psc->driver)
508      dlclose(psc->driver);
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