dri2.c revision 9b784069ce76e1914eaafac0542458f6a84d9fc0
1/*
2 * Mesa 3-D graphics library
3 * Version:  7.9
4 *
5 * Copyright 2009, VMware, Inc.
6 * All Rights Reserved.
7 * Copyright (C) 2010 LunarG Inc.
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the "Software"),
11 * to deal in the Software without restriction, including without limitation
12 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 * and/or sell copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included
17 * in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
22 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
23 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 * Authors:
27 *    Keith Whitwell <keithw@vmware.com>
28 *    Jakob Bornecrantz <wallbraker@gmail.com>
29 *    Chia-I Wu <olv@lunarg.com>
30 */
31
32#include "util/u_memory.h"
33#include "util/u_inlines.h"
34#include "util/u_format.h"
35#include "util/u_debug.h"
36#include "state_tracker/drm_driver.h"
37
38#include "dri_screen.h"
39#include "dri_context.h"
40#include "dri_drawable.h"
41#include "dri2_buffer.h"
42
43/**
44 * DRI2 flush extension.
45 */
46static void
47dri2_flush_drawable(__DRIdrawable *draw)
48{
49   struct dri_drawable *drawable = dri_drawable(draw);
50   struct dri_context *ctx = dri_get_current(draw->driScreenPriv);
51
52   ctx->st->flush(ctx->st, 0, NULL);
53}
54
55static void
56dri2_invalidate_drawable(__DRIdrawable *dPriv)
57{
58   struct dri_drawable *drawable = dri_drawable(dPriv);
59
60   dri2InvalidateDrawable(dPriv);
61   drawable->dPriv->lastStamp = *drawable->dPriv->pStamp;
62
63   p_atomic_inc(&drawable->base.stamp);
64}
65
66static const __DRI2flushExtension dri2FlushExtension = {
67    { __DRI2_FLUSH, __DRI2_FLUSH_VERSION },
68    dri2_flush_drawable,
69    dri2_invalidate_drawable,
70};
71
72/**
73 * Retrieve __DRIbuffer from the DRI loader.
74 */
75static __DRIbuffer *
76dri2_drawable_get_buffers(struct dri_drawable *drawable,
77                          const enum st_attachment_type *statts,
78                          unsigned *count)
79{
80   __DRIdrawable *dri_drawable = drawable->dPriv;
81   struct __DRIdri2LoaderExtensionRec *loader = drawable->sPriv->dri2.loader;
82   boolean with_format;
83   __DRIbuffer *buffers;
84   int num_buffers;
85   unsigned attachments[10];
86   unsigned num_attachments, i;
87
88   assert(loader);
89   with_format = dri_with_format(drawable->sPriv);
90
91   num_attachments = 0;
92
93   /* for Xserver 1.6.0 (DRI2 version 1) we always need to ask for the front */
94   if (!with_format)
95      attachments[num_attachments++] = __DRI_BUFFER_FRONT_LEFT;
96
97   for (i = 0; i < *count; i++) {
98      enum pipe_format format;
99      unsigned bind;
100      int att, bpp;
101
102      dri_drawable_get_format(drawable, statts[i], &format, &bind);
103      if (format == PIPE_FORMAT_NONE)
104         continue;
105
106      switch (statts[i]) {
107      case ST_ATTACHMENT_FRONT_LEFT:
108         /* already added */
109         if (!with_format)
110            continue;
111         att = __DRI_BUFFER_FRONT_LEFT;
112         break;
113      case ST_ATTACHMENT_BACK_LEFT:
114         att = __DRI_BUFFER_BACK_LEFT;
115         break;
116      case ST_ATTACHMENT_FRONT_RIGHT:
117         att = __DRI_BUFFER_FRONT_RIGHT;
118         break;
119      case ST_ATTACHMENT_BACK_RIGHT:
120         att = __DRI_BUFFER_BACK_RIGHT;
121         break;
122      case ST_ATTACHMENT_DEPTH_STENCIL:
123         att = __DRI_BUFFER_DEPTH_STENCIL;
124         break;
125      default:
126         att = -1;
127         break;
128      }
129
130      bpp = util_format_get_blocksizebits(format);
131
132      if (att >= 0) {
133         attachments[num_attachments++] = att;
134         if (with_format) {
135            attachments[num_attachments++] = bpp;
136         }
137      }
138   }
139
140   if (with_format) {
141      num_attachments /= 2;
142      buffers = loader->getBuffersWithFormat(dri_drawable,
143            &dri_drawable->w, &dri_drawable->h,
144            attachments, num_attachments,
145            &num_buffers, dri_drawable->loaderPrivate);
146   }
147   else {
148      buffers = loader->getBuffers(dri_drawable,
149            &dri_drawable->w, &dri_drawable->h,
150            attachments, num_attachments,
151            &num_buffers, dri_drawable->loaderPrivate);
152   }
153
154   if (buffers) {
155      /* set one cliprect to cover the whole dri_drawable */
156      dri_drawable->x = 0;
157      dri_drawable->y = 0;
158      dri_drawable->backX = 0;
159      dri_drawable->backY = 0;
160      dri_drawable->numClipRects = 1;
161      dri_drawable->pClipRects[0].x1 = 0;
162      dri_drawable->pClipRects[0].y1 = 0;
163      dri_drawable->pClipRects[0].x2 = dri_drawable->w;
164      dri_drawable->pClipRects[0].y2 = dri_drawable->h;
165      dri_drawable->numBackClipRects = 1;
166      dri_drawable->pBackClipRects[0].x1 = 0;
167      dri_drawable->pBackClipRects[0].y1 = 0;
168      dri_drawable->pBackClipRects[0].x2 = dri_drawable->w;
169      dri_drawable->pBackClipRects[0].y2 = dri_drawable->h;
170
171      *count = num_buffers;
172   }
173
174   return buffers;
175}
176
177/**
178 * Process __DRIbuffer and convert them into pipe_resources.
179 */
180static void
181dri2_drawable_process_buffers(struct dri_drawable *drawable,
182                              __DRIbuffer *buffers, unsigned count)
183{
184   struct dri_screen *screen = dri_screen(drawable->sPriv);
185   __DRIdrawable *dri_drawable = drawable->dPriv;
186   struct pipe_resource templ;
187   struct winsys_handle whandle;
188   boolean have_depth = FALSE;
189   unsigned i, bind;
190
191   if (drawable->old_num == count &&
192       drawable->old_w == dri_drawable->w &&
193       drawable->old_h == dri_drawable->h &&
194       memcmp(drawable->old, buffers, sizeof(__DRIbuffer) * count) == 0)
195      return;
196
197   for (i = 0; i < ST_ATTACHMENT_COUNT; i++)
198      pipe_resource_reference(&drawable->textures[i], NULL);
199
200   memset(&templ, 0, sizeof(templ));
201   templ.target = screen->target;
202   templ.last_level = 0;
203   templ.width0 = dri_drawable->w;
204   templ.height0 = dri_drawable->h;
205   templ.depth0 = 1;
206   templ.array_size = 1;
207
208   memset(&whandle, 0, sizeof(whandle));
209
210   for (i = 0; i < count; i++) {
211      __DRIbuffer *buf = &buffers[i];
212      enum st_attachment_type statt;
213      enum pipe_format format;
214
215      switch (buf->attachment) {
216      case __DRI_BUFFER_FRONT_LEFT:
217         if (!screen->auto_fake_front) {
218            statt = ST_ATTACHMENT_INVALID;
219            break;
220         }
221         /* fallthrough */
222      case __DRI_BUFFER_FAKE_FRONT_LEFT:
223         statt = ST_ATTACHMENT_FRONT_LEFT;
224         break;
225      case __DRI_BUFFER_BACK_LEFT:
226         statt = ST_ATTACHMENT_BACK_LEFT;
227         break;
228      case __DRI_BUFFER_DEPTH:
229      case __DRI_BUFFER_DEPTH_STENCIL:
230      case __DRI_BUFFER_STENCIL:
231         /* use only the first depth/stencil buffer */
232         if (!have_depth) {
233            have_depth = TRUE;
234            statt = ST_ATTACHMENT_DEPTH_STENCIL;
235         }
236         else {
237            statt = ST_ATTACHMENT_INVALID;
238         }
239         break;
240      default:
241         statt = ST_ATTACHMENT_INVALID;
242         break;
243      }
244
245      dri_drawable_get_format(drawable, statt, &format, &bind);
246      if (statt == ST_ATTACHMENT_INVALID || format == PIPE_FORMAT_NONE)
247         continue;
248
249      templ.format = format;
250      templ.bind = bind;
251      whandle.handle = buf->name;
252      whandle.stride = buf->pitch;
253
254      drawable->textures[statt] =
255         screen->base.screen->resource_from_handle(screen->base.screen,
256               &templ, &whandle);
257   }
258
259   drawable->old_num = count;
260   drawable->old_w = dri_drawable->w;
261   drawable->old_h = dri_drawable->h;
262   memcpy(drawable->old, buffers, sizeof(__DRIbuffer) * count);
263}
264
265static __DRIbuffer *
266dri2_allocate_buffer(__DRIscreen *sPriv,
267                     unsigned attachment, unsigned format,
268                     int width, int height)
269{
270   struct dri_screen *screen = dri_screen(sPriv);
271   struct dri2_buffer *buffer;
272   struct pipe_resource templ;
273   enum pipe_format pf;
274   unsigned bind = 0;
275   struct winsys_handle whandle;
276
277   switch (attachment) {
278      case __DRI_BUFFER_FRONT_LEFT:
279      case __DRI_BUFFER_FAKE_FRONT_LEFT:
280         bind = PIPE_BIND_RENDER_TARGET | PIPE_BIND_SAMPLER_VIEW;
281         break;
282      case __DRI_BUFFER_BACK_LEFT:
283         bind = PIPE_BIND_RENDER_TARGET | PIPE_BIND_SAMPLER_VIEW;
284         break;
285      case __DRI_BUFFER_DEPTH:
286      case __DRI_BUFFER_DEPTH_STENCIL:
287      case __DRI_BUFFER_STENCIL:
288            bind = PIPE_BIND_DEPTH_STENCIL; /* XXX sampler? */
289         break;
290   }
291
292   switch (format) {
293      case 32:
294         pf = PIPE_FORMAT_B8G8R8X8_UNORM;
295         break;
296      case 16:
297         pf = PIPE_FORMAT_Z16_UNORM;
298         break;
299      default:
300         return NULL;
301   }
302
303   buffer = CALLOC_STRUCT(dri2_buffer);
304   if (!buffer)
305      return NULL;
306
307   memset(&templ, 0, sizeof(templ));
308   templ.bind = bind;
309   templ.format = pf;
310   templ.target = PIPE_TEXTURE_2D;
311   templ.last_level = 0;
312   templ.width0 = width;
313   templ.height0 = height;
314   templ.depth0 = 1;
315   templ.array_size = 1;
316
317   buffer->resource =
318      screen->base.screen->resource_create(screen->base.screen, &templ);
319   if (!buffer->resource)
320      return NULL;
321
322   memset(&whandle, 0, sizeof(whandle));
323   whandle.type = DRM_API_HANDLE_TYPE_SHARED;
324   screen->base.screen->resource_get_handle(screen->base.screen,
325         buffer->resource, &whandle);
326
327   buffer->base.attachment = attachment;
328   buffer->base.name = whandle.handle;
329   buffer->base.cpp = util_format_get_blocksize(pf);
330   buffer->base.pitch = whandle.stride;
331
332   return &buffer->base;
333}
334
335static void
336dri2_release_buffer(__DRIscreen *sPriv, __DRIbuffer *bPriv)
337{
338   struct dri2_buffer *buffer = dri2_buffer(bPriv);
339
340   pipe_resource_reference(&buffer->resource, NULL);
341   FREE(buffer);
342}
343
344/*
345 * Backend functions for st_framebuffer interface.
346 */
347
348static void
349dri2_allocate_textures(struct dri_drawable *drawable,
350                       const enum st_attachment_type *statts,
351                       unsigned count)
352{
353   __DRIbuffer *buffers;
354   unsigned num_buffers = count;
355
356   buffers = dri2_drawable_get_buffers(drawable, statts, &num_buffers);
357   if (buffers)
358      dri2_drawable_process_buffers(drawable, buffers, num_buffers);
359}
360
361static void
362dri2_flush_frontbuffer(struct dri_drawable *drawable,
363                       enum st_attachment_type statt)
364{
365   __DRIdrawable *dri_drawable = drawable->dPriv;
366   struct __DRIdri2LoaderExtensionRec *loader = drawable->sPriv->dri2.loader;
367
368   if (loader->flushFrontBuffer == NULL)
369      return;
370
371   if (statt == ST_ATTACHMENT_FRONT_LEFT) {
372      loader->flushFrontBuffer(dri_drawable, dri_drawable->loaderPrivate);
373   }
374}
375
376static __DRIimage *
377dri2_lookup_egl_image(struct dri_screen *screen, void *handle)
378{
379   __DRIimageLookupExtension *loader = screen->sPriv->dri2.image;
380   __DRIimage *img;
381
382   if (!loader->lookupEGLImage)
383      return NULL;
384
385   img = loader->lookupEGLImage(screen->sPriv,
386				handle, screen->sPriv->loaderPrivate);
387
388   return img;
389}
390
391static __DRIimage *
392dri2_create_image_from_name(__DRIscreen *_screen,
393                            int width, int height, int format,
394                            int name, int pitch, void *loaderPrivate)
395{
396   struct dri_screen *screen = dri_screen(_screen);
397   __DRIimage *img;
398   struct pipe_resource templ;
399   struct winsys_handle whandle;
400   unsigned tex_usage;
401   enum pipe_format pf;
402
403   tex_usage = PIPE_BIND_RENDER_TARGET | PIPE_BIND_SAMPLER_VIEW;
404
405   switch (format) {
406   case __DRI_IMAGE_FORMAT_RGB565:
407      pf = PIPE_FORMAT_B5G6R5_UNORM;
408      break;
409   case __DRI_IMAGE_FORMAT_XRGB8888:
410      pf = PIPE_FORMAT_B8G8R8X8_UNORM;
411      break;
412   case __DRI_IMAGE_FORMAT_ARGB8888:
413      pf = PIPE_FORMAT_B8G8R8A8_UNORM;
414      break;
415   default:
416      pf = PIPE_FORMAT_NONE;
417      break;
418   }
419   if (pf == PIPE_FORMAT_NONE)
420      return NULL;
421
422   img = CALLOC_STRUCT(__DRIimageRec);
423   if (!img)
424      return NULL;
425
426   memset(&templ, 0, sizeof(templ));
427   templ.bind = tex_usage;
428   templ.format = pf;
429   templ.target = screen->target;
430   templ.last_level = 0;
431   templ.width0 = width;
432   templ.height0 = height;
433   templ.depth0 = 1;
434   templ.array_size = 1;
435
436   memset(&whandle, 0, sizeof(whandle));
437   whandle.handle = name;
438   whandle.stride = pitch * util_format_get_blocksize(pf);
439
440   img->texture = screen->base.screen->resource_from_handle(screen->base.screen,
441         &templ, &whandle);
442   if (!img->texture) {
443      FREE(img);
444      return NULL;
445   }
446
447   img->level = 0;
448   img->layer = 0;
449   img->loader_private = loaderPrivate;
450
451   return img;
452}
453
454static __DRIimage *
455dri2_create_image_from_renderbuffer(__DRIcontext *context,
456				    int renderbuffer, void *loaderPrivate)
457{
458   struct dri_context *ctx = dri_context(context);
459
460   if (!ctx->st->get_resource_for_egl_image)
461      return NULL;
462
463   /* TODO */
464   return NULL;
465}
466
467static __DRIimage *
468dri2_create_image(__DRIscreen *_screen,
469                   int width, int height, int format,
470                   unsigned int use, void *loaderPrivate)
471{
472   struct dri_screen *screen = dri_screen(_screen);
473   __DRIimage *img;
474   struct pipe_resource templ;
475   unsigned tex_usage;
476   enum pipe_format pf;
477
478   tex_usage = PIPE_BIND_RENDER_TARGET | PIPE_BIND_SAMPLER_VIEW;
479   if (use & __DRI_IMAGE_USE_SCANOUT)
480      tex_usage |= PIPE_BIND_SCANOUT;
481   if (use & __DRI_IMAGE_USE_SHARE)
482      tex_usage |= PIPE_BIND_SHARED;
483   if (use & __DRI_IMAGE_USE_CURSOR) {
484      if (width != 64 || height != 64)
485         return NULL;
486      tex_usage |= PIPE_BIND_CURSOR;
487   }
488
489   switch (format) {
490   case __DRI_IMAGE_FORMAT_RGB565:
491      pf = PIPE_FORMAT_B5G6R5_UNORM;
492      break;
493   case __DRI_IMAGE_FORMAT_XRGB8888:
494      pf = PIPE_FORMAT_B8G8R8X8_UNORM;
495      break;
496   case __DRI_IMAGE_FORMAT_ARGB8888:
497      pf = PIPE_FORMAT_B8G8R8A8_UNORM;
498      break;
499   default:
500      pf = PIPE_FORMAT_NONE;
501      break;
502   }
503   if (pf == PIPE_FORMAT_NONE)
504      return NULL;
505
506   img = CALLOC_STRUCT(__DRIimageRec);
507   if (!img)
508      return NULL;
509
510   memset(&templ, 0, sizeof(templ));
511   templ.bind = tex_usage;
512   templ.format = pf;
513   templ.target = PIPE_TEXTURE_2D;
514   templ.last_level = 0;
515   templ.width0 = width;
516   templ.height0 = height;
517   templ.depth0 = 1;
518   templ.array_size = 1;
519
520   img->texture = screen->base.screen->resource_create(screen->base.screen, &templ);
521   if (!img->texture) {
522      FREE(img);
523      return NULL;
524   }
525
526   img->level = 0;
527   img->layer = 0;
528
529   img->loader_private = loaderPrivate;
530   return img;
531}
532
533static GLboolean
534dri2_query_image(__DRIimage *image, int attrib, int *value)
535{
536   struct winsys_handle whandle;
537   memset(&whandle, 0, sizeof(whandle));
538
539   switch (attrib) {
540   case __DRI_IMAGE_ATTRIB_STRIDE:
541      image->texture->screen->resource_get_handle(image->texture->screen,
542            image->texture, &whandle);
543      *value = whandle.stride;
544      return GL_TRUE;
545   case __DRI_IMAGE_ATTRIB_HANDLE:
546      whandle.type = DRM_API_HANDLE_TYPE_KMS;
547      image->texture->screen->resource_get_handle(image->texture->screen,
548         image->texture, &whandle);
549      *value = whandle.handle;
550      return GL_TRUE;
551   case __DRI_IMAGE_ATTRIB_NAME:
552      whandle.type = DRM_API_HANDLE_TYPE_SHARED;
553      image->texture->screen->resource_get_handle(image->texture->screen,
554         image->texture, &whandle);
555      *value = whandle.handle;
556      return GL_TRUE;
557   default:
558      return GL_FALSE;
559   }
560}
561
562static __DRIimage *
563dri2_dup_image(__DRIimage *image, void *loaderPrivate)
564{
565   __DRIimage *img;
566
567   img = CALLOC_STRUCT(__DRIimageRec);
568   if (!img)
569      return NULL;
570
571   img->texture = NULL;
572   pipe_resource_reference(&img->texture, image->texture);
573   img->level = image->level;
574   img->layer = image->layer;
575   img->loader_private = loaderPrivate;
576
577   return img;
578}
579
580static void
581dri2_destroy_image(__DRIimage *img)
582{
583   pipe_resource_reference(&img->texture, NULL);
584   FREE(img);
585}
586
587static struct __DRIimageExtensionRec dri2ImageExtension = {
588    { __DRI_IMAGE, __DRI_IMAGE_VERSION },
589    dri2_create_image_from_name,
590    dri2_create_image_from_renderbuffer,
591    dri2_destroy_image,
592    dri2_create_image,
593    dri2_query_image,
594    dri2_dup_image,
595};
596
597/*
598 * Backend function init_screen.
599 */
600
601static const __DRIextension *dri_screen_extensions[] = {
602   &driReadDrawableExtension,
603   &driCopySubBufferExtension.base,
604   &driSwapControlExtension.base,
605   &driMediaStreamCounterExtension.base,
606   &driTexBufferExtension.base,
607   &dri2FlushExtension.base,
608   &dri2ImageExtension.base,
609   &dri2ConfigQueryExtension.base,
610   NULL
611};
612
613/**
614 * This is the driver specific part of the createNewScreen entry point.
615 *
616 * Returns the struct gl_config supported by this driver.
617 */
618static const __DRIconfig **
619dri2_init_screen(__DRIscreen * sPriv)
620{
621   const __DRIconfig **configs;
622   struct dri_screen *screen;
623   struct pipe_screen *pscreen;
624
625   screen = CALLOC_STRUCT(dri_screen);
626   if (!screen)
627      return NULL;
628
629   screen->sPriv = sPriv;
630   screen->fd = sPriv->fd;
631
632   sPriv->private = (void *)screen;
633   sPriv->extensions = dri_screen_extensions;
634
635   pscreen = driver_descriptor.create_screen(screen->fd);
636   /* dri_init_screen_helper checks pscreen for us */
637
638   configs = dri_init_screen_helper(screen, pscreen, 32);
639   if (!configs)
640      goto fail;
641
642   sPriv->api_mask = 0;
643   if (screen->st_api->profile_mask & ST_PROFILE_DEFAULT_MASK)
644      sPriv->api_mask |= 1 << __DRI_API_OPENGL;
645   if (screen->st_api->profile_mask & ST_PROFILE_OPENGL_ES1_MASK)
646      sPriv->api_mask |= 1 << __DRI_API_GLES;
647   if (screen->st_api->profile_mask & ST_PROFILE_OPENGL_ES2_MASK)
648      sPriv->api_mask |= 1 << __DRI_API_GLES2;
649
650   screen->auto_fake_front = dri_with_format(sPriv);
651   screen->broken_invalidate = !sPriv->dri2.useInvalidate;
652   screen->lookup_egl_image = dri2_lookup_egl_image;
653
654   return configs;
655fail:
656   dri_destroy_screen_helper(screen);
657   FREE(screen);
658   return NULL;
659}
660
661static boolean
662dri2_create_buffer(__DRIscreen * sPriv,
663                   __DRIdrawable * dPriv,
664                   const struct gl_config * visual, boolean isPixmap)
665{
666   struct dri_drawable *drawable = NULL;
667
668   if (!dri_create_buffer(sPriv, dPriv, visual, isPixmap))
669      return FALSE;
670
671   drawable = dPriv->driverPrivate;
672
673   drawable->allocate_textures = dri2_allocate_textures;
674   drawable->flush_frontbuffer = dri2_flush_frontbuffer;
675
676   return TRUE;
677}
678
679/**
680 * DRI driver virtual function table.
681 *
682 * DRI versions differ in their implementation of init_screen and swap_buffers.
683 */
684const struct __DriverAPIRec driDriverAPI = {
685   .InitScreen = NULL,
686   .InitScreen2 = dri2_init_screen,
687   .DestroyScreen = dri_destroy_screen,
688   .CreateContext = dri_create_context,
689   .DestroyContext = dri_destroy_context,
690   .CreateBuffer = dri2_create_buffer,
691   .DestroyBuffer = dri_destroy_buffer,
692   .MakeCurrent = dri_make_current,
693   .UnbindContext = dri_unbind_context,
694
695   .GetSwapInfo = NULL,
696   .GetDrawableMSC = NULL,
697   .WaitForMSC = NULL,
698
699   .SwapBuffers = NULL,
700   .CopySubBuffer = NULL,
701
702   .AllocateBuffer = dri2_allocate_buffer,
703   .ReleaseBuffer  = dri2_release_buffer,
704};
705
706/* This is the table of extensions that the loader will dlsym() for. */
707PUBLIC const __DRIextension *__driDriverExtensions[] = {
708    &driCoreExtension.base,
709    &driLegacyExtension.base,
710    &driDRI2Extension.base,
711    NULL
712};
713
714/* vim: set sw=3 ts=8 sts=3 expandtab: */
715