drisw_glx.c revision 296fe21ae5be838268e51fdd9a4a3009ab961265
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 __DRItexBufferExtension *texBuffer;
52
53   const __DRIconfig **driver_configs;
54
55   void *driver;
56};
57
58struct drisw_drawable
59{
60   __GLXDRIdrawable base;
61
62   GC gc;
63   GC swapgc;
64
65   __DRIdrawable *driDrawable;
66   XVisualInfo *visinfo;
67   XImage *ximage;
68};
69
70static Bool
71XCreateDrawable(struct drisw_drawable * pdp,
72                Display * dpy, XID drawable, int visualid)
73{
74   XGCValues gcvalues;
75   long visMask;
76   XVisualInfo visTemp;
77   int num_visuals;
78
79   /* create GC's */
80   pdp->gc = XCreateGC(dpy, drawable, 0, NULL);
81   pdp->swapgc = XCreateGC(dpy, drawable, 0, NULL);
82
83   gcvalues.function = GXcopy;
84   gcvalues.graphics_exposures = False;
85   XChangeGC(dpy, pdp->gc, GCFunction, &gcvalues);
86   XChangeGC(dpy, pdp->swapgc, GCFunction, &gcvalues);
87   XChangeGC(dpy, pdp->swapgc, GCGraphicsExposures, &gcvalues);
88
89   /* visual */
90   visTemp.screen = DefaultScreen(dpy);
91   visTemp.visualid = visualid;
92   visMask = (VisualScreenMask | VisualIDMask);
93   pdp->visinfo = XGetVisualInfo(dpy, visMask, &visTemp, &num_visuals);
94
95   /* create XImage */
96   pdp->ximage = XCreateImage(dpy,
97                              pdp->visinfo->visual,
98                              pdp->visinfo->depth,
99                              ZPixmap, 0,             /* format, offset */
100                              NULL,                   /* data */
101                              0, 0,                   /* width, height */
102                              32,                     /* bitmap_pad */
103                              0);                     /* bytes_per_line */
104
105  /**
106   * swrast does not handle 24-bit depth with 24 bpp, so let X do the
107   * the conversion for us.
108   */
109  if (pdp->ximage->bits_per_pixel == 24)
110     pdp->ximage->bits_per_pixel = 32;
111
112   return True;
113}
114
115static void
116XDestroyDrawable(struct drisw_drawable * pdp, Display * dpy, XID drawable)
117{
118   XDestroyImage(pdp->ximage);
119   XFree(pdp->visinfo);
120
121   XFreeGC(dpy, pdp->gc);
122   XFreeGC(dpy, pdp->swapgc);
123}
124
125/**
126 * swrast loader functions
127 */
128
129static void
130swrastGetDrawableInfo(__DRIdrawable * draw,
131                      int *x, int *y, int *w, int *h,
132                      void *loaderPrivate)
133{
134   struct drisw_drawable *pdp = loaderPrivate;
135   __GLXDRIdrawable *pdraw = &(pdp->base);
136   Display *dpy = pdraw->psc->dpy;
137   Drawable drawable;
138
139   Window root;
140   unsigned uw, uh, bw, depth;
141
142   drawable = pdraw->xDrawable;
143
144   XGetGeometry(dpy, drawable, &root, x, y, &uw, &uh, &bw, &depth);
145   *w = uw;
146   *h = uh;
147}
148
149/**
150 * Align renderbuffer pitch.
151 *
152 * This should be chosen by the driver and the loader (libGL, xserver/glx)
153 * should use the driver provided pitch.
154 *
155 * It seems that the xorg loader (that is the xserver loading swrast_dri for
156 * indirect rendering, not client-side libGL) requires that the pitch is
157 * exactly the image width padded to 32 bits. XXX
158 *
159 * The above restriction can probably be overcome by using ScratchPixmap and
160 * CopyArea in the xserver, similar to ShmPutImage, and setting the width of
161 * the scratch pixmap to 'pitch / cpp'.
162 */
163static inline int
164bytes_per_line(unsigned pitch_bits, unsigned mul)
165{
166   unsigned mask = mul - 1;
167
168   return ((pitch_bits + mask) & ~mask) / 8;
169}
170
171static void
172swrastPutImage(__DRIdrawable * draw, int op,
173               int x, int y, int w, int h,
174               char *data, void *loaderPrivate)
175{
176   struct drisw_drawable *pdp = loaderPrivate;
177   __GLXDRIdrawable *pdraw = &(pdp->base);
178   Display *dpy = pdraw->psc->dpy;
179   Drawable drawable;
180   XImage *ximage;
181   GC gc;
182
183   switch (op) {
184   case __DRI_SWRAST_IMAGE_OP_DRAW:
185      gc = pdp->gc;
186      break;
187   case __DRI_SWRAST_IMAGE_OP_SWAP:
188      gc = pdp->swapgc;
189      break;
190   default:
191      return;
192   }
193
194   drawable = pdraw->xDrawable;
195
196   ximage = pdp->ximage;
197   ximage->data = data;
198   ximage->width = w;
199   ximage->height = h;
200   ximage->bytes_per_line = bytes_per_line(w * ximage->bits_per_pixel, 32);
201
202   XPutImage(dpy, drawable, gc, ximage, 0, 0, x, y, w, h);
203
204   ximage->data = NULL;
205}
206
207static void
208swrastGetImage(__DRIdrawable * read,
209               int x, int y, int w, int h,
210               char *data, void *loaderPrivate)
211{
212   struct drisw_drawable *prp = loaderPrivate;
213   __GLXDRIdrawable *pread = &(prp->base);
214   Display *dpy = pread->psc->dpy;
215   Drawable readable;
216   XImage *ximage;
217
218   readable = pread->xDrawable;
219
220   ximage = prp->ximage;
221   ximage->data = data;
222   ximage->width = w;
223   ximage->height = h;
224   ximage->bytes_per_line = bytes_per_line(w * ximage->bits_per_pixel, 32);
225
226   XGetSubImage(dpy, readable, x, y, w, h, ~0L, ZPixmap, ximage, 0, 0);
227
228   ximage->data = NULL;
229}
230
231static const __DRIswrastLoaderExtension swrastLoaderExtension = {
232   {__DRI_SWRAST_LOADER, __DRI_SWRAST_LOADER_VERSION},
233   swrastGetDrawableInfo,
234   swrastPutImage,
235   swrastGetImage
236};
237
238static const __DRIextension *loader_extensions[] = {
239   &systemTimeExtension.base,
240   &swrastLoaderExtension.base,
241   NULL
242};
243
244/**
245 * GLXDRI functions
246 */
247
248static void
249drisw_destroy_context(struct glx_context *context)
250{
251   struct drisw_context *pcp = (struct drisw_context *) context;
252   struct drisw_screen *psc = (struct drisw_screen *) context->psc;
253
254   driReleaseDrawables(&pcp->base);
255
256   if (context->extensions)
257      XFree((char *) context->extensions);
258
259   (*psc->core->destroyContext) (pcp->driContext);
260
261   Xfree(pcp);
262}
263
264static int
265drisw_bind_context(struct glx_context *context, struct glx_context *old,
266		   GLXDrawable draw, GLXDrawable read)
267{
268   struct drisw_context *pcp = (struct drisw_context *) context;
269   struct drisw_screen *psc = (struct drisw_screen *) pcp->base.psc;
270   struct drisw_drawable *pdraw, *pread;
271
272   pdraw = (struct drisw_drawable *) driFetchDrawable(context, draw);
273   pread = (struct drisw_drawable *) driFetchDrawable(context, read);
274
275   driReleaseDrawables(&pcp->base);
276
277   if (pdraw == NULL || pread == NULL)
278      return GLXBadDrawable;
279
280   if ((*psc->core->bindContext) (pcp->driContext,
281				  pdraw->driDrawable, pread->driDrawable))
282      return Success;
283
284   return GLXBadContext;
285}
286
287static void
288drisw_unbind_context(struct glx_context *context, struct glx_context *new)
289{
290   struct drisw_context *pcp = (struct drisw_context *) context;
291   struct drisw_screen *psc = (struct drisw_screen *) pcp->base.psc;
292
293   (*psc->core->unbindContext) (pcp->driContext);
294}
295
296static void
297drisw_bind_tex_image(Display * dpy,
298		    GLXDrawable drawable,
299		    int buffer, const int *attrib_list)
300{
301   struct glx_context *gc = __glXGetCurrentContext();
302   struct drisw_context *pcp = (struct drisw_context *) gc;
303   __GLXDRIdrawable *base = GetGLXDRIDrawable(dpy, drawable);
304   struct drisw_drawable *pdraw = (struct drisw_drawable *) base;
305   struct drisw_screen *psc;
306
307   __glXInitialize(dpy);
308
309   if (pdraw != NULL) {
310      psc = (struct drisw_screen *) base->psc;
311
312      if (!psc->texBuffer)
313         return;
314
315      if (psc->texBuffer->base.version >= 2 &&
316        psc->texBuffer->setTexBuffer2 != NULL) {
317	      (*psc->texBuffer->setTexBuffer2) (pcp->driContext,
318					   pdraw->base.textureTarget,
319					   pdraw->base.textureFormat,
320					   pdraw->driDrawable);
321      }
322      else {
323	      (*psc->texBuffer->setTexBuffer) (pcp->driContext,
324					  pdraw->base.textureTarget,
325					  pdraw->driDrawable);
326      }
327   }
328}
329
330static void
331drisw_release_tex_image(Display * dpy, GLXDrawable drawable, int buffer)
332{
333#if __DRI_TEX_BUFFER_VERSION >= 3
334   struct glx_context *gc = __glXGetCurrentContext();
335   struct dri2_context *pcp = (struct dri2_context *) gc;
336   __GLXDRIdrawable *base = GetGLXDRIDrawable(dpy, drawable);
337   struct glx_display *dpyPriv = __glXInitialize(dpy);
338   struct dri2_drawable *pdraw = (struct dri2_drawable *) base;
339   struct dri2_screen *psc;
340
341   if (pdraw != NULL) {
342      psc = (struct dri2_screen *) base->psc;
343
344      if (!psc->texBuffer)
345         return;
346
347      if (psc->texBuffer->base.version >= 3 &&
348          psc->texBuffer->releaseTexBuffer != NULL) {
349         (*psc->texBuffer->releaseTexBuffer) (pcp->driContext,
350                                           pdraw->base.textureTarget,
351                                           pdraw->driDrawable);
352      }
353   }
354#endif
355}
356
357static const struct glx_context_vtable drisw_context_vtable = {
358   drisw_destroy_context,
359   drisw_bind_context,
360   drisw_unbind_context,
361   NULL,
362   NULL,
363   DRI_glXUseXFont,
364   drisw_bind_tex_image,
365   drisw_release_tex_image,
366   NULL, /* get_proc_address */
367};
368
369static struct glx_context *
370drisw_create_context(struct glx_screen *base,
371		     struct glx_config *config_base,
372		     struct glx_context *shareList, int renderType)
373{
374   struct drisw_context *pcp, *pcp_shared;
375   __GLXDRIconfigPrivate *config = (__GLXDRIconfigPrivate *) config_base;
376   struct drisw_screen *psc = (struct drisw_screen *) base;
377   __DRIcontext *shared = NULL;
378
379   if (!psc->base.driScreen)
380      return NULL;
381
382   if (shareList) {
383      /* If the shareList context is not a DRISW context, we cannot possibly
384       * create a DRISW context that shares it.
385       */
386      if (shareList->vtable->destroy != drisw_destroy_context) {
387	 return NULL;
388      }
389
390      pcp_shared = (struct drisw_context *) shareList;
391      shared = pcp_shared->driContext;
392   }
393
394   pcp = Xmalloc(sizeof *pcp);
395   if (pcp == NULL)
396      return NULL;
397
398   memset(pcp, 0, sizeof *pcp);
399   if (!glx_context_init(&pcp->base, &psc->base, &config->base)) {
400      Xfree(pcp);
401      return NULL;
402   }
403
404   pcp->driContext =
405      (*psc->core->createNewContext) (psc->driScreen,
406				      config->driConfig, shared, pcp);
407   if (pcp->driContext == NULL) {
408      Xfree(pcp);
409      return NULL;
410   }
411
412   pcp->base.vtable = &drisw_context_vtable;
413
414   return &pcp->base;
415}
416
417static struct glx_context *
418drisw_create_context_attribs(struct glx_screen *base,
419			     struct glx_config *config_base,
420			     struct glx_context *shareList,
421			     unsigned num_attribs,
422			     const uint32_t *attribs,
423			     unsigned *error)
424{
425   struct drisw_context *pcp, *pcp_shared;
426   __GLXDRIconfigPrivate *config = (__GLXDRIconfigPrivate *) config_base;
427   struct drisw_screen *psc = (struct drisw_screen *) base;
428   __DRIcontext *shared = NULL;
429
430   uint32_t minor_ver = 1;
431   uint32_t major_ver = 0;
432   uint32_t flags = 0;
433   unsigned api;
434   uint32_t ctx_attribs[2 * 4];
435   unsigned num_ctx_attribs = 0;
436
437   if (!psc->base.driScreen)
438      return NULL;
439
440   if (psc->swrast->base.version < 3)
441      return NULL;
442
443   /* Remap the GLX tokens to DRI2 tokens.
444    */
445   if (!dri2_convert_glx_attribs(num_attribs, attribs,
446				 &major_ver, &minor_ver, &flags, &api,
447				 error))
448      return NULL;
449
450   if (shareList) {
451      pcp_shared = (struct drisw_context *) shareList;
452      shared = pcp_shared->driContext;
453   }
454
455   pcp = Xmalloc(sizeof *pcp);
456   if (pcp == NULL)
457      return NULL;
458
459   memset(pcp, 0, sizeof *pcp);
460   if (!glx_context_init(&pcp->base, &psc->base, &config->base)) {
461      Xfree(pcp);
462      return NULL;
463   }
464
465   ctx_attribs[num_ctx_attribs++] = __DRI_CTX_ATTRIB_MAJOR_VERSION;
466   ctx_attribs[num_ctx_attribs++] = major_ver;
467   ctx_attribs[num_ctx_attribs++] = __DRI_CTX_ATTRIB_MINOR_VERSION;
468   ctx_attribs[num_ctx_attribs++] = minor_ver;
469
470   if (flags != 0) {
471      ctx_attribs[num_ctx_attribs++] = __DRI_CTX_ATTRIB_FLAGS;
472
473      /* The current __DRI_CTX_FLAG_* values are identical to the
474       * GLX_CONTEXT_*_BIT values.
475       */
476      ctx_attribs[num_ctx_attribs++] = flags;
477   }
478
479   pcp->driContext =
480      (*psc->swrast->createContextAttribs) (psc->driScreen,
481					    api,
482					    config->driConfig,
483					    shared,
484					    num_ctx_attribs / 2,
485					    ctx_attribs,
486					    error,
487					    pcp);
488   if (pcp->driContext == NULL) {
489      Xfree(pcp);
490      return NULL;
491   }
492
493   pcp->base.vtable = &drisw_context_vtable;
494
495   return &pcp->base;
496}
497
498static void
499driswDestroyDrawable(__GLXDRIdrawable * pdraw)
500{
501   struct drisw_drawable *pdp = (struct drisw_drawable *) pdraw;
502   struct drisw_screen *psc = (struct drisw_screen *) pdp->base.psc;
503
504   (*psc->core->destroyDrawable) (pdp->driDrawable);
505
506   XDestroyDrawable(pdp, pdraw->psc->dpy, pdraw->drawable);
507   Xfree(pdp);
508}
509
510static __GLXDRIdrawable *
511driswCreateDrawable(struct glx_screen *base, XID xDrawable,
512		    GLXDrawable drawable, struct glx_config *modes)
513{
514   struct drisw_drawable *pdp;
515   __GLXDRIconfigPrivate *config = (__GLXDRIconfigPrivate *) modes;
516   struct drisw_screen *psc = (struct drisw_screen *) base;
517
518   const __DRIswrastExtension *swrast = psc->swrast;
519
520   pdp = Xmalloc(sizeof(*pdp));
521   if (!pdp)
522      return NULL;
523
524   memset(pdp, 0, sizeof *pdp);
525   pdp->base.xDrawable = xDrawable;
526   pdp->base.drawable = drawable;
527   pdp->base.psc = &psc->base;
528
529   XCreateDrawable(pdp, psc->base.dpy, xDrawable, modes->visualID);
530
531   /* Create a new drawable */
532   pdp->driDrawable =
533      (*swrast->createNewDrawable) (psc->driScreen, config->driConfig, pdp);
534
535   if (!pdp->driDrawable) {
536      XDestroyDrawable(pdp, psc->base.dpy, xDrawable);
537      Xfree(pdp);
538      return NULL;
539   }
540
541   pdp->base.destroyDrawable = driswDestroyDrawable;
542
543   return &pdp->base;
544}
545
546static int64_t
547driswSwapBuffers(__GLXDRIdrawable * pdraw,
548                 int64_t target_msc, int64_t divisor, int64_t remainder)
549{
550   struct drisw_drawable *pdp = (struct drisw_drawable *) pdraw;
551   struct drisw_screen *psc = (struct drisw_screen *) pdp->base.psc;
552
553   (void) target_msc;
554   (void) divisor;
555   (void) remainder;
556
557   (*psc->core->swapBuffers) (pdp->driDrawable);
558
559   return 0;
560}
561
562static void
563driswDestroyScreen(struct glx_screen *base)
564{
565   struct drisw_screen *psc = (struct drisw_screen *) base;
566
567   /* Free the direct rendering per screen data */
568   (*psc->core->destroyScreen) (psc->driScreen);
569   driDestroyConfigs(psc->driver_configs);
570   psc->driScreen = NULL;
571   if (psc->driver)
572      dlclose(psc->driver);
573}
574
575static void *
576driOpenSwrast(void)
577{
578   void *driver = NULL;
579
580   if (driver == NULL)
581      driver = driOpenDriver("swrast");
582
583   return driver;
584}
585
586static const struct glx_screen_vtable drisw_screen_vtable = {
587   drisw_create_context,
588   drisw_create_context_attribs
589};
590
591static void
592driswBindExtensions(struct drisw_screen *psc, const __DRIextension **extensions)
593{
594   int i;
595
596   __glXEnableDirectExtension(&psc->base, "GLX_SGI_make_current_read");
597
598   if (psc->swrast->base.version >= 3) {
599      __glXEnableDirectExtension(&psc->base, "GLX_ARB_create_context");
600      __glXEnableDirectExtension(&psc->base, "GLX_ARB_create_context_profile");
601   }
602
603   /* FIXME: Figure out what other extensions can be ported here from dri2. */
604   for (i = 0; extensions[i]; i++) {
605      if ((strcmp(extensions[i]->name, __DRI_TEX_BUFFER) == 0)) {
606	 psc->texBuffer = (__DRItexBufferExtension *) extensions[i];
607	 __glXEnableDirectExtension(&psc->base, "GLX_EXT_texture_from_pixmap");
608      }
609   }
610}
611
612static struct glx_screen *
613driswCreateScreen(int screen, struct glx_display *priv)
614{
615   __GLXDRIscreen *psp;
616   const __DRIconfig **driver_configs;
617   const __DRIextension **extensions;
618   struct drisw_screen *psc;
619   struct glx_config *configs = NULL, *visuals = NULL;
620   int i;
621
622   psc = Xcalloc(1, sizeof *psc);
623   if (psc == NULL)
624      return NULL;
625
626   memset(psc, 0, sizeof *psc);
627   if (!glx_screen_init(&psc->base, screen, priv)) {
628      Xfree(psc);
629      return NULL;
630   }
631
632   psc->driver = driOpenSwrast();
633   if (psc->driver == NULL)
634      goto handle_error;
635
636   extensions = dlsym(psc->driver, __DRI_DRIVER_EXTENSIONS);
637   if (extensions == NULL) {
638      ErrorMessageF("driver exports no extensions (%s)\n", dlerror());
639      goto handle_error;
640   }
641
642   for (i = 0; extensions[i]; i++) {
643      if (strcmp(extensions[i]->name, __DRI_CORE) == 0)
644	 psc->core = (__DRIcoreExtension *) extensions[i];
645      if (strcmp(extensions[i]->name, __DRI_SWRAST) == 0)
646	 psc->swrast = (__DRIswrastExtension *) extensions[i];
647   }
648
649   if (psc->core == NULL || psc->swrast == NULL) {
650      ErrorMessageF("core dri extension not found\n");
651      goto handle_error;
652   }
653
654   psc->driScreen =
655      psc->swrast->createNewScreen(screen, loader_extensions,
656				   &driver_configs, psc);
657   if (psc->driScreen == NULL) {
658      ErrorMessageF("failed to create dri screen\n");
659      goto handle_error;
660   }
661
662   extensions = psc->core->getExtensions(psc->driScreen);
663   driswBindExtensions(psc, extensions);
664
665   configs = driConvertConfigs(psc->core, psc->base.configs, driver_configs);
666   visuals = driConvertConfigs(psc->core, psc->base.visuals, driver_configs);
667
668   if (!configs || !visuals)
669       goto handle_error;
670
671   glx_config_destroy_list(psc->base.configs);
672   psc->base.configs = configs;
673   glx_config_destroy_list(psc->base.visuals);
674   psc->base.visuals = visuals;
675
676   psc->driver_configs = driver_configs;
677
678   psc->base.vtable = &drisw_screen_vtable;
679   psp = &psc->vtable;
680   psc->base.driScreen = psp;
681   psp->destroyScreen = driswDestroyScreen;
682   psp->createDrawable = driswCreateDrawable;
683   psp->swapBuffers = driswSwapBuffers;
684
685   return &psc->base;
686
687 handle_error:
688   if (configs)
689       glx_config_destroy_list(configs);
690   if (visuals)
691       glx_config_destroy_list(visuals);
692   if (psc->driScreen)
693       psc->core->destroyScreen(psc->driScreen);
694   psc->driScreen = NULL;
695
696   if (psc->driver)
697      dlclose(psc->driver);
698   glx_screen_cleanup(&psc->base);
699   Xfree(psc);
700
701   ErrorMessageF("reverting to indirect rendering\n");
702
703   return NULL;
704}
705
706/* Called from __glXFreeDisplayPrivate.
707 */
708static void
709driswDestroyDisplay(__GLXDRIdisplay * dpy)
710{
711   Xfree(dpy);
712}
713
714/*
715 * Allocate, initialize and return a __DRIdisplayPrivate object.
716 * This is called from __glXInitialize() when we are given a new
717 * display pointer.
718 */
719_X_HIDDEN __GLXDRIdisplay *
720driswCreateDisplay(Display * dpy)
721{
722   struct drisw_display *pdpyp;
723
724   pdpyp = Xmalloc(sizeof *pdpyp);
725   if (pdpyp == NULL)
726      return NULL;
727
728   pdpyp->base.destroyDisplay = driswDestroyDisplay;
729   pdpyp->base.createScreen = driswCreateScreen;
730
731   return &pdpyp->base;
732}
733
734#endif /* GLX_DIRECT_RENDERING */
735