glxdri.c revision 706f2543
1/*
2 * Copyright © 2006 Red Hat, Inc
3 *
4 * Permission to use, copy, modify, distribute, and sell this software
5 * and its documentation for any purpose is hereby granted without
6 * fee, provided that the above copyright notice appear in all copies
7 * and that both that copyright notice and this permission notice
8 * appear in supporting documentation, and that the name of Red Hat,
9 * Inc not be used in advertising or publicity pertaining to
10 * distribution of the software without specific, written prior
11 * permission.  Red Hat, Inc makes no representations about the
12 * suitability of this software for any purpose.  It is provided "as
13 * is" without express or implied warranty.
14 *
15 * RED HAT, INC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
17 * NO EVENT SHALL RED HAT, INC BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
19 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
20 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 */
23
24#ifdef HAVE_DIX_CONFIG_H
25#include <dix-config.h>
26#endif
27
28#include <stdint.h>
29#include <stdio.h>
30#include <string.h>
31#include <errno.h>
32#include <sys/time.h>
33#include <dlfcn.h>
34
35#include <drm.h>
36#include <GL/gl.h>
37#include <GL/internal/dri_interface.h>
38#include <GL/glxtokens.h>
39
40#include <windowstr.h>
41#include <os.h>
42#include <damage.h>
43
44#define _XF86DRI_SERVER_
45#include <drm_sarea.h>
46#include <xf86drm.h>
47#include <X11/dri/xf86driproto.h>
48#include <xf86str.h>
49#include <xf86.h>
50#include <dri.h>
51
52#include "servermd.h"
53
54#define DRI_NEW_INTERFACE_ONLY
55#include "glxserver.h"
56#include "glxutil.h"
57#include "glxdricommon.h"
58
59#include "glapitable.h"
60#include "glapi.h"
61#include "glthread.h"
62#include "dispatch.h"
63#include "extension_string.h"
64
65typedef struct __GLXDRIscreen   __GLXDRIscreen;
66typedef struct __GLXDRIcontext  __GLXDRIcontext;
67typedef struct __GLXDRIdrawable __GLXDRIdrawable;
68
69struct __GLXDRIscreen {
70    __GLXscreen		 base;
71    __DRIscreen		*driScreen;
72    void		*driver;
73
74    xf86EnterVTProc	*enterVT;
75    xf86LeaveVTProc	*leaveVT;
76
77    const __DRIcoreExtension *core;
78    const __DRIlegacyExtension *legacy;
79    const __DRIcopySubBufferExtension *copySubBuffer;
80    const __DRIswapControlExtension *swapControl;
81
82#ifdef __DRI_TEX_OFFSET
83    const __DRItexOffsetExtension *texOffset;
84    DRITexOffsetStartProcPtr texOffsetStart;
85    DRITexOffsetFinishProcPtr texOffsetFinish;
86    __GLXDRIdrawable *texOffsetOverride[16];
87    GLuint lastTexOffsetOverride;
88#endif
89
90    unsigned char glx_enable_bits[__GLX_EXT_BYTES];
91};
92
93struct __GLXDRIcontext {
94    __GLXcontext base;
95    __DRIcontext *driContext;
96    XID hwContextID;
97};
98
99struct __GLXDRIdrawable {
100    __GLXdrawable base;
101    __DRIdrawable *driDrawable;
102
103    /* Pulled in from old __GLXpixmap */
104#ifdef __DRI_TEX_OFFSET
105    GLint texname;
106    __GLXDRIcontext *ctx;
107    unsigned long long offset;
108    DamagePtr pDamage;
109#endif
110};
111
112static void
113__glXDRIleaveServer(GLboolean rendering)
114{
115    int i;
116
117    for (i = 0; rendering && i < screenInfo.numScreens; i++) {
118	__GLXDRIscreen * const screen =
119	    (__GLXDRIscreen *) glxGetScreen(screenInfo.screens[i]);
120	GLuint lastOverride = screen->lastTexOffsetOverride;
121
122	if (lastOverride) {
123	    __GLXDRIdrawable **texOffsetOverride = screen->texOffsetOverride;
124	    int j;
125
126	    for (j = 0; j < lastOverride; j++) {
127		__GLXDRIdrawable *pGlxPix = texOffsetOverride[j];
128
129		if (pGlxPix && pGlxPix->texname) {
130		    pGlxPix->offset =
131			screen->texOffsetStart((PixmapPtr)pGlxPix->base.pDraw);
132		}
133	    }
134	}
135    }
136
137    DRIBlockHandler(NULL, NULL, NULL);
138
139    for (i = 0; rendering && i < screenInfo.numScreens; i++) {
140	__GLXDRIscreen * const screen =
141	    (__GLXDRIscreen *) glxGetScreen(screenInfo.screens[i]);
142	GLuint lastOverride = screen->lastTexOffsetOverride;
143
144	if (lastOverride) {
145	    __GLXDRIdrawable **texOffsetOverride = screen->texOffsetOverride;
146	    int j;
147
148	    for (j = 0; j < lastOverride; j++) {
149		__GLXDRIdrawable *pGlxPix = texOffsetOverride[j];
150
151		if (pGlxPix && pGlxPix->texname) {
152		    screen->texOffset->setTexOffset(pGlxPix->ctx->driContext,
153						    pGlxPix->texname,
154						    pGlxPix->offset,
155						    pGlxPix->base.pDraw->depth,
156						    ((PixmapPtr)pGlxPix->base.pDraw)->devKind);
157		}
158	    }
159	}
160    }
161}
162
163static void
164__glXDRIenterServer(GLboolean rendering)
165{
166    int i;
167
168    for (i = 0; rendering && i < screenInfo.numScreens; i++) {
169	__GLXDRIscreen * const screen = (__GLXDRIscreen *)
170	    glxGetScreen(screenInfo.screens[i]);
171
172	if (screen->lastTexOffsetOverride) {
173	    CALL_Flush(GET_DISPATCH(), ());
174	    break;
175	}
176    }
177
178    DRIWakeupHandler(NULL, 0, NULL);
179}
180
181
182static void
183__glXDRIdoReleaseTexImage(__GLXDRIscreen *screen, __GLXDRIdrawable *drawable)
184{
185    GLuint lastOverride = screen->lastTexOffsetOverride;
186
187    if (lastOverride) {
188	__GLXDRIdrawable **texOffsetOverride = screen->texOffsetOverride;
189	int i;
190
191	for (i = 0; i < lastOverride; i++) {
192	    if (texOffsetOverride[i] == drawable) {
193		if (screen->texOffsetFinish)
194		    screen->texOffsetFinish((PixmapPtr)drawable->base.pDraw);
195
196		texOffsetOverride[i] = NULL;
197
198		if (i + 1 == lastOverride) {
199		    lastOverride = 0;
200
201		    while (i--) {
202			if (texOffsetOverride[i]) {
203			    lastOverride = i + 1;
204			    break;
205			}
206		    }
207
208		    screen->lastTexOffsetOverride = lastOverride;
209
210		    break;
211		}
212	    }
213	}
214    }
215}
216
217
218static void
219__glXDRIdrawableDestroy(__GLXdrawable *drawable)
220{
221    __GLXDRIdrawable *private = (__GLXDRIdrawable *) drawable;
222    __GLXDRIscreen *screen;
223    int i;
224
225    for (i = 0; i < screenInfo.numScreens; i++) {
226	screen = (__GLXDRIscreen *) glxGetScreen(screenInfo.screens[i]);
227	__glXDRIdoReleaseTexImage(screen, private);
228    }
229
230    /* If the X window was destroyed, the dri DestroyWindow hook will
231     * aready have taken care of this, so only call if pDraw isn't NULL. */
232    if (drawable->pDraw != NULL) {
233	screen = (__GLXDRIscreen *) glxGetScreen(drawable->pDraw->pScreen);
234	(*screen->core->destroyDrawable)(private->driDrawable);
235
236	__glXenterServer(GL_FALSE);
237	DRIDestroyDrawable(drawable->pDraw->pScreen,
238			   serverClient, drawable->pDraw);
239	__glXleaveServer(GL_FALSE);
240    }
241
242    __glXDrawableRelease(drawable);
243
244    free(private);
245}
246
247static GLboolean
248__glXDRIdrawableSwapBuffers(ClientPtr client, __GLXdrawable *basePrivate)
249{
250    __GLXDRIdrawable *private = (__GLXDRIdrawable *) basePrivate;
251    __GLXDRIscreen *screen =
252	(__GLXDRIscreen *) glxGetScreen(basePrivate->pDraw->pScreen);
253
254    (*screen->core->swapBuffers)(private->driDrawable);
255
256    return TRUE;
257}
258
259
260static int
261__glXDRIdrawableSwapInterval(__GLXdrawable *baseDrawable, int interval)
262{
263    __GLXDRIdrawable *draw = (__GLXDRIdrawable *) baseDrawable;
264    __GLXDRIscreen *screen =
265	(__GLXDRIscreen *) glxGetScreen(baseDrawable->pDraw->pScreen);
266
267    if (screen->swapControl)
268	screen->swapControl->setSwapInterval(draw->driDrawable, interval);
269
270    return 0;
271}
272
273
274static void
275__glXDRIdrawableCopySubBuffer(__GLXdrawable *basePrivate,
276			       int x, int y, int w, int h)
277{
278    __GLXDRIdrawable *private = (__GLXDRIdrawable *) basePrivate;
279    __GLXDRIscreen *screen = (__GLXDRIscreen *)
280	glxGetScreen(basePrivate->pDraw->pScreen);
281
282    if (screen->copySubBuffer)
283	screen->copySubBuffer->copySubBuffer(private->driDrawable, x, y, w, h);
284}
285
286static void
287__glXDRIcontextDestroy(__GLXcontext *baseContext)
288{
289    __GLXDRIcontext *context = (__GLXDRIcontext *) baseContext;
290    __GLXDRIscreen *screen = (__GLXDRIscreen *) context->base.pGlxScreen;
291    Bool retval;
292
293    screen->core->destroyContext(context->driContext);
294
295    __glXenterServer(GL_FALSE);
296    retval = DRIDestroyContext(baseContext->pGlxScreen->pScreen,
297			       context->hwContextID);
298    __glXleaveServer(GL_FALSE);
299
300    __glXContextDestroy(&context->base);
301    free(context);
302}
303
304static int
305__glXDRIcontextMakeCurrent(__GLXcontext *baseContext)
306{
307    __GLXDRIcontext *context = (__GLXDRIcontext *) baseContext;
308    __GLXDRIscreen *screen = (__GLXDRIscreen *) context->base.pGlxScreen;
309    __GLXDRIdrawable *draw = (__GLXDRIdrawable *) baseContext->drawPriv;
310    __GLXDRIdrawable *read = (__GLXDRIdrawable *) baseContext->readPriv;
311
312    return (*screen->core->bindContext)(context->driContext,
313					draw->driDrawable,
314					read->driDrawable);
315}
316
317static int
318__glXDRIcontextLoseCurrent(__GLXcontext *baseContext)
319{
320    __GLXDRIcontext *context = (__GLXDRIcontext *) baseContext;
321    __GLXDRIscreen *screen = (__GLXDRIscreen *) context->base.pGlxScreen;
322
323    return (*screen->core->unbindContext)(context->driContext);
324}
325
326static int
327__glXDRIcontextCopy(__GLXcontext *baseDst, __GLXcontext *baseSrc,
328		    unsigned long mask)
329{
330    __GLXDRIcontext *dst = (__GLXDRIcontext *) baseDst;
331    __GLXDRIcontext *src = (__GLXDRIcontext *) baseSrc;
332    __GLXDRIscreen *screen = (__GLXDRIscreen *) dst->base.pGlxScreen;
333
334    return (*screen->core->copyContext)(dst->driContext,
335					src->driContext, mask);
336}
337
338static int
339__glXDRIcontextForceCurrent(__GLXcontext *baseContext)
340{
341    __GLXDRIcontext *context = (__GLXDRIcontext *) baseContext;
342    __GLXDRIdrawable *draw = (__GLXDRIdrawable *) baseContext->drawPriv;
343    __GLXDRIdrawable *read = (__GLXDRIdrawable *) baseContext->readPriv;
344    __GLXDRIscreen *screen = (__GLXDRIscreen *) context->base.pGlxScreen;
345
346    return (*screen->core->bindContext)(context->driContext,
347					draw->driDrawable,
348					read->driDrawable);
349}
350
351static void
352glxFillAlphaChannel (CARD32 *pixels, CARD32 rowstride, int width, int height)
353{
354    int i;
355    CARD32 *p, *end;
356
357    rowstride /= 4;
358
359    for (i = 0; i < height; i++)
360    {
361	p = pixels;
362	end = p + width;
363	while (p < end)
364	  *p++ |= 0xFF000000;
365	pixels += rowstride;
366    }
367}
368
369static Bool
370testTexOffset(__GLXDRIscreen * const screen, PixmapPtr pPixmap)
371{
372    Bool ret;
373
374    if (!screen->texOffsetStart || !screen->texOffset)
375	return FALSE;
376
377    __glXenterServer(GL_FALSE);
378    ret = screen->texOffsetStart(pPixmap) != ~0ULL;
379    __glXleaveServer(GL_FALSE);
380
381    return ret;
382}
383
384/*
385 * (sticking this here for lack of a better place)
386 * Known issues with the GLX_EXT_texture_from_pixmap implementation:
387 * - In general we ignore the fbconfig, lots of examples follow
388 * - No fbconfig handling for multiple mipmap levels
389 * - No fbconfig handling for 1D textures
390 * - No fbconfig handling for TEXTURE_TARGET
391 * - No fbconfig exposure of Y inversion state
392 * - No GenerateMipmapEXT support (due to no FBO support)
393 * - No support for anything but 16bpp and 32bpp-sparse pixmaps
394 */
395
396static int
397__glXDRIbindTexImage(__GLXcontext *baseContext,
398		     int buffer,
399		     __GLXdrawable *glxPixmap)
400{
401    RegionPtr	pRegion = NULL;
402    PixmapPtr	pixmap;
403    int		bpp, override = 0, texname;
404    GLenum	format, type;
405    ScreenPtr pScreen = glxPixmap->pDraw->pScreen;
406    __GLXDRIdrawable *driDraw = (__GLXDRIdrawable *) glxPixmap;
407    __GLXDRIscreen * const screen = (__GLXDRIscreen *) glxGetScreen(pScreen);
408
409    CALL_GetIntegerv(GET_DISPATCH(), (glxPixmap->target == GL_TEXTURE_2D ?
410				      GL_TEXTURE_BINDING_2D :
411				      GL_TEXTURE_BINDING_RECTANGLE_NV,
412				      &texname));
413
414    if (!texname)
415	return __glXError(GLXBadContextState);
416
417    pixmap = (PixmapPtr) glxPixmap->pDraw;
418
419    if (testTexOffset(screen, pixmap)) {
420	__GLXDRIdrawable **texOffsetOverride = screen->texOffsetOverride;
421	int i, firstEmpty = 16;
422
423	for (i = 0; i < 16; i++) {
424	    if (texOffsetOverride[i] == driDraw)
425		goto alreadyin;
426
427	    if (firstEmpty == 16 && !texOffsetOverride[i])
428		firstEmpty = i;
429	}
430
431	if (firstEmpty == 16) {
432	    ErrorF("%s: Failed to register texture offset override\n", __func__);
433	    goto nooverride;
434	}
435
436	if (firstEmpty >= screen->lastTexOffsetOverride)
437	    screen->lastTexOffsetOverride = firstEmpty + 1;
438
439	texOffsetOverride[firstEmpty] = driDraw;
440
441alreadyin:
442	override = 1;
443
444	driDraw->ctx = (__GLXDRIcontext*)baseContext;
445
446	if (texname == driDraw->texname)
447	    return Success;
448
449	driDraw->texname = texname;
450
451	screen->texOffset->setTexOffset(driDraw->ctx->driContext, texname, 0,
452					pixmap->drawable.depth,
453					pixmap->devKind);
454    }
455nooverride:
456
457    if (!driDraw->pDamage) {
458	if (!override) {
459	    driDraw->pDamage = DamageCreate(NULL, NULL, DamageReportNone,
460					    TRUE, pScreen, NULL);
461	    if (!driDraw->pDamage)
462		return BadAlloc;
463
464	    DamageRegister ((DrawablePtr) pixmap, driDraw->pDamage);
465	}
466
467	pRegion = NULL;
468    } else {
469	pRegion = DamageRegion(driDraw->pDamage);
470	if (RegionNil(pRegion))
471	    return Success;
472    }
473
474    /* XXX 24bpp packed, 8, etc */
475    if (pixmap->drawable.depth >= 24) {
476	bpp = 4;
477	format = GL_BGRA;
478	type =
479#if X_BYTE_ORDER == X_BIG_ENDIAN
480	    !override ? GL_UNSIGNED_INT_8_8_8_8_REV :
481#endif
482	    GL_UNSIGNED_BYTE;
483    } else {
484	bpp = 2;
485	format = GL_RGB;
486	type = GL_UNSIGNED_SHORT_5_6_5;
487    }
488
489    if (pRegion == NULL)
490    {
491	void *data = NULL;
492
493	if (!override) {
494	    unsigned pitch = PixmapBytePad(pixmap->drawable.width,
495					   pixmap->drawable.depth);
496
497	    data = malloc(pitch * pixmap->drawable.height);
498
499	    __glXenterServer(GL_FALSE);
500	    pScreen->GetImage(&pixmap->drawable, 0 /*pixmap->drawable.x*/,
501			      0 /*pixmap->drawable.y*/, pixmap->drawable.width,
502			      pixmap->drawable.height, ZPixmap, ~0, data);
503	    __glXleaveServer(GL_FALSE);
504
505	    if (pixmap->drawable.depth == 24)
506		glxFillAlphaChannel(data,
507				    pitch,
508				    pixmap->drawable.width,
509				    pixmap->drawable.height);
510
511	    CALL_PixelStorei( GET_DISPATCH(), (GL_UNPACK_ROW_LENGTH,
512					       pitch / bpp) );
513	    CALL_PixelStorei( GET_DISPATCH(), (GL_UNPACK_SKIP_PIXELS, 0) );
514	    CALL_PixelStorei( GET_DISPATCH(), (GL_UNPACK_SKIP_ROWS, 0) );
515	}
516
517	CALL_TexImage2D( GET_DISPATCH(),
518			 (glxPixmap->target,
519			  0,
520			  bpp == 4 ? 4 : 3,
521			  pixmap->drawable.width,
522			  pixmap->drawable.height,
523			  0,
524			  format,
525			  type,
526			  data) );
527
528	free(data);
529    } else if (!override) {
530        int i, numRects;
531	BoxPtr p;
532
533	numRects = RegionNumRects (pRegion);
534	p = RegionRects (pRegion);
535
536	CALL_PixelStorei( GET_DISPATCH(), (GL_UNPACK_SKIP_PIXELS, 0) );
537	CALL_PixelStorei( GET_DISPATCH(), (GL_UNPACK_SKIP_ROWS, 0) );
538
539	for (i = 0; i < numRects; i++)
540	{
541	    unsigned pitch = PixmapBytePad(p[i].x2 - p[i].x1,
542					   pixmap->drawable.depth);
543	    void *data = malloc(pitch * (p[i].y2 - p[i].y1));
544
545	    __glXenterServer(GL_FALSE);
546	    pScreen->GetImage(&pixmap->drawable, /*pixmap->drawable.x +*/ p[i].x1,
547			      /*pixmap->drawable.y*/ + p[i].y1, p[i].x2 - p[i].x1,
548			      p[i].y2 - p[i].y1, ZPixmap, ~0, data);
549	    __glXleaveServer(GL_FALSE);
550
551	    if (pixmap->drawable.depth == 24)
552		glxFillAlphaChannel(data,
553				    pitch,
554				    p[i].x2 - p[i].x1,
555				    p[i].y2 - p[i].y1);
556
557	    CALL_PixelStorei( GET_DISPATCH(), (GL_UNPACK_ROW_LENGTH,
558					       pitch / bpp) );
559
560	    CALL_TexSubImage2D( GET_DISPATCH(),
561				(glxPixmap->target,
562				 0,
563				 p[i].x1, p[i].y1,
564				 p[i].x2 - p[i].x1, p[i].y2 - p[i].y1,
565				 format,
566				 type,
567				 data) );
568
569	    free(data);
570	}
571    }
572
573    if (!override)
574	DamageEmpty(driDraw->pDamage);
575
576    return Success;
577}
578
579static int
580__glXDRIreleaseTexImage(__GLXcontext *baseContext,
581			int buffer,
582			__GLXdrawable *pixmap)
583{
584    __GLXDRIscreen *screen =
585	(__GLXDRIscreen *) glxGetScreen(pixmap->pDraw->pScreen);
586    __GLXDRIdrawable *drawable = (__GLXDRIdrawable *) pixmap;
587
588    __glXDRIdoReleaseTexImage(screen, drawable);
589
590    return Success;
591}
592
593static __GLXtextureFromPixmap __glXDRItextureFromPixmap = {
594    __glXDRIbindTexImage,
595    __glXDRIreleaseTexImage
596};
597
598static void
599__glXDRIscreenDestroy(__GLXscreen *baseScreen)
600{
601    __GLXDRIscreen *screen = (__GLXDRIscreen *) baseScreen;
602
603    screen->core->destroyScreen(screen->driScreen);
604
605    dlclose(screen->driver);
606
607    __glXScreenDestroy(baseScreen);
608
609    free(screen);
610}
611
612static __GLXcontext *
613__glXDRIscreenCreateContext(__GLXscreen *baseScreen,
614			    __GLXconfig *glxConfig,
615			    __GLXcontext *baseShareContext)
616{
617    __GLXDRIscreen *screen = (__GLXDRIscreen *) baseScreen;
618    __GLXDRIcontext *context, *shareContext;
619    __GLXDRIconfig *config = (__GLXDRIconfig *) glxConfig;
620    VisualPtr visual;
621    int i;
622    GLboolean retval;
623    __DRIcontext *driShare;
624    drm_context_t hwContext;
625    ScreenPtr pScreen = baseScreen->pScreen;
626
627    shareContext = (__GLXDRIcontext *) baseShareContext;
628    if (shareContext)
629	driShare = shareContext->driContext;
630    else
631	driShare = NULL;
632
633    if (baseShareContext && baseShareContext->isDirect)
634        return NULL;
635
636    context = calloc(1, sizeof *context);
637    if (context == NULL)
638	return NULL;
639
640    context->base.destroy           = __glXDRIcontextDestroy;
641    context->base.makeCurrent       = __glXDRIcontextMakeCurrent;
642    context->base.loseCurrent       = __glXDRIcontextLoseCurrent;
643    context->base.copy              = __glXDRIcontextCopy;
644    context->base.forceCurrent      = __glXDRIcontextForceCurrent;
645
646    context->base.textureFromPixmap = &__glXDRItextureFromPixmap;
647    /* Find the requested X visual */
648    visual = pScreen->visuals;
649    for (i = 0; i < pScreen->numVisuals; i++, visual++)
650	if (visual->vid == glxConfig->visualID)
651	    break;
652    if (i == pScreen->numVisuals)
653	return NULL;
654
655    context->hwContextID = FakeClientID(0);
656
657    __glXenterServer(GL_FALSE);
658    retval = DRICreateContext(baseScreen->pScreen, visual,
659			      context->hwContextID, &hwContext);
660    __glXleaveServer(GL_FALSE);
661
662    if (!retval)
663    	return NULL;
664
665    context->driContext =
666	screen->legacy->createNewContext(screen->driScreen,
667					 config->driConfig,
668					 0, /* render type */
669					 driShare,
670					 hwContext,
671					 context);
672
673    if (context->driContext == NULL) {
674    	__glXenterServer(GL_FALSE);
675	retval = DRIDestroyContext(baseScreen->pScreen, context->hwContextID);
676    	__glXleaveServer(GL_FALSE);
677	free(context);
678	return NULL;
679    }
680
681    return &context->base;
682}
683
684static __GLXdrawable *
685__glXDRIscreenCreateDrawable(ClientPtr client,
686			     __GLXscreen *screen,
687			     DrawablePtr pDraw,
688			     XID drawId,
689			     int type,
690			     XID glxDrawId,
691			     __GLXconfig *glxConfig)
692{
693    __GLXDRIscreen *driScreen = (__GLXDRIscreen *) screen;
694    __GLXDRIconfig *config = (__GLXDRIconfig *) glxConfig;
695    __GLXDRIdrawable *private;
696    GLboolean retval;
697    drm_drawable_t hwDrawable;
698
699    private = calloc(1, sizeof *private);
700    if (private == NULL)
701	return NULL;
702
703    if (!__glXDrawableInit(&private->base, screen,
704			   pDraw, type, glxDrawId, glxConfig)) {
705        free(private);
706	return NULL;
707    }
708
709    private->base.destroy       = __glXDRIdrawableDestroy;
710    private->base.swapBuffers   = __glXDRIdrawableSwapBuffers;
711    private->base.copySubBuffer = __glXDRIdrawableCopySubBuffer;
712    private->base.waitX		= NULL;
713    private->base.waitGL	= NULL;
714
715    __glXenterServer(GL_FALSE);
716    retval = DRICreateDrawable(screen->pScreen, serverClient,
717			       pDraw, &hwDrawable);
718    __glXleaveServer(GL_FALSE);
719
720    if (!retval) {
721        free(private);
722    	return NULL;
723    }
724
725    /* The last argument is 'attrs', which is used with pbuffers which
726     * we currently don't support. */
727
728    private->driDrawable =
729	(driScreen->legacy->createNewDrawable)(driScreen->driScreen,
730					       config->driConfig,
731					       hwDrawable, 0, NULL, private);
732
733    if (private->driDrawable == NULL) {
734	__glXenterServer(GL_FALSE);
735	DRIDestroyDrawable(screen->pScreen, serverClient, pDraw);
736	__glXleaveServer(GL_FALSE);
737	free(private);
738	return NULL;
739    }
740
741    return &private->base;
742}
743
744static GLboolean
745getDrawableInfo(__DRIdrawable *driDrawable,
746		unsigned int *index, unsigned int *stamp,
747		int *x, int *y, int *width, int *height,
748		int *numClipRects, drm_clip_rect_t **ppClipRects,
749		int *backX, int *backY,
750		int *numBackClipRects, drm_clip_rect_t **ppBackClipRects,
751		void *data)
752{
753    __GLXDRIdrawable *drawable = data;
754    ScreenPtr pScreen;
755    drm_clip_rect_t *pClipRects, *pBackClipRects;
756    GLboolean retval;
757    size_t size;
758
759    /* If the X window has been destroyed, give up here. */
760    if (drawable->base.pDraw == NULL)
761	return GL_FALSE;
762
763    pScreen = drawable->base.pDraw->pScreen;
764    __glXenterServer(GL_FALSE);
765    retval = DRIGetDrawableInfo(pScreen, drawable->base.pDraw, index, stamp,
766				x, y, width, height,
767				numClipRects, &pClipRects,
768				backX, backY,
769				numBackClipRects, &pBackClipRects);
770    __glXleaveServer(GL_FALSE);
771
772    if (retval && *numClipRects > 0) {
773	size = sizeof (drm_clip_rect_t) * *numClipRects;
774	*ppClipRects = malloc(size);
775
776	/* Clip cliprects to screen dimensions (redirected windows) */
777	if (*ppClipRects != NULL) {
778	    int i, j;
779
780	    for (i = 0, j = 0; i < *numClipRects; i++) {
781	        (*ppClipRects)[j].x1 = max(pClipRects[i].x1, 0);
782		(*ppClipRects)[j].y1 = max(pClipRects[i].y1, 0);
783		(*ppClipRects)[j].x2 = min(pClipRects[i].x2, pScreen->width);
784		(*ppClipRects)[j].y2 = min(pClipRects[i].y2, pScreen->height);
785
786		if ((*ppClipRects)[j].x1 < (*ppClipRects)[j].x2 &&
787		    (*ppClipRects)[j].y1 < (*ppClipRects)[j].y2) {
788		    j++;
789		}
790	    }
791
792	    if (*numClipRects != j) {
793		*numClipRects = j;
794		*ppClipRects = realloc(*ppClipRects,
795					 sizeof (drm_clip_rect_t) *
796					 *numClipRects);
797	    }
798	} else
799	    *numClipRects = 0;
800    }
801    else {
802      *ppClipRects = NULL;
803      *numClipRects = 0;
804    }
805
806    if (retval && *numBackClipRects > 0) {
807	size = sizeof (drm_clip_rect_t) * *numBackClipRects;
808	*ppBackClipRects = malloc(size);
809	if (*ppBackClipRects != NULL)
810	    memcpy (*ppBackClipRects, pBackClipRects, size);
811	else
812	    *numBackClipRects = 0;
813    }
814    else {
815      *ppBackClipRects = NULL;
816      *numBackClipRects = 0;
817    }
818
819    return retval;
820}
821
822static void __glXReportDamage(__DRIdrawable *driDraw,
823			      int x, int y,
824			      drm_clip_rect_t *rects, int num_rects,
825			      GLboolean front_buffer,
826			      void *data)
827{
828    __GLXDRIdrawable *drawable = data;
829    DrawablePtr pDraw = drawable->base.pDraw;
830    RegionRec region;
831
832    __glXenterServer(GL_FALSE);
833
834    if (RegionInitBoxes(&region, (BoxPtr) rects, num_rects)) {
835       RegionTranslate(&region, pDraw->x, pDraw->y);
836       DamageDamageRegion(pDraw, &region);
837       RegionUninit(&region);
838    }
839    else {
840       while (num_rects--) {
841           RegionInit (&region, (BoxPtr) rects++, 1);
842           RegionTranslate(&region, pDraw->x, pDraw->y);
843           DamageDamageRegion(pDraw, &region);
844           RegionUninit(&region);
845       }
846    }
847
848    __glXleaveServer(GL_FALSE);
849}
850
851static const __DRIgetDrawableInfoExtension getDrawableInfoExtension = {
852    { __DRI_GET_DRAWABLE_INFO, __DRI_GET_DRAWABLE_INFO_VERSION },
853    getDrawableInfo
854};
855
856static const __DRIdamageExtension damageExtension = {
857    { __DRI_DAMAGE, __DRI_DAMAGE_VERSION },
858    __glXReportDamage,
859};
860
861static const __DRIextension *loader_extensions[] = {
862    &systemTimeExtension.base,
863    &getDrawableInfoExtension.base,
864    &damageExtension.base,
865    NULL
866};
867
868
869
870static const char dri_driver_path[] = DRI_DRIVER_PATH;
871
872static Bool
873glxDRIEnterVT (int index, int flags)
874{
875    ScrnInfoPtr scrn = xf86Screens[index];
876    Bool	ret;
877    __GLXDRIscreen *screen = (__GLXDRIscreen *)
878	glxGetScreen(screenInfo.screens[index]);
879
880    LogMessage(X_INFO, "AIGLX: Resuming AIGLX clients after VT switch\n");
881
882    scrn->EnterVT = screen->enterVT;
883
884    ret = scrn->EnterVT (index, flags);
885
886    screen->enterVT = scrn->EnterVT;
887    scrn->EnterVT = glxDRIEnterVT;
888
889    if (!ret)
890	return FALSE;
891
892    glxResumeClients();
893
894    return TRUE;
895}
896
897static void
898glxDRILeaveVT (int index, int flags)
899{
900    ScrnInfoPtr scrn = xf86Screens[index];
901    __GLXDRIscreen *screen = (__GLXDRIscreen *)
902	glxGetScreen(screenInfo.screens[index]);
903
904    LogMessage(X_INFO, "AIGLX: Suspending AIGLX clients for VT switch\n");
905
906    glxSuspendClients();
907
908    scrn->LeaveVT = screen->leaveVT;
909    (*screen->leaveVT) (index, flags);
910    screen->leaveVT = scrn->LeaveVT;
911    scrn->LeaveVT = glxDRILeaveVT;
912}
913
914static void
915initializeExtensions(__GLXDRIscreen *screen)
916{
917    const __DRIextension **extensions;
918    int i;
919
920    extensions = screen->core->getExtensions(screen->driScreen);
921
922    for (i = 0; extensions[i]; i++) {
923#ifdef __DRI_READ_DRAWABLE
924	if (strcmp(extensions[i]->name, __DRI_READ_DRAWABLE) == 0) {
925	    __glXEnableExtension(screen->glx_enable_bits,
926				 "GLX_SGI_make_current_read");
927
928	    LogMessage(X_INFO, "AIGLX: enabled GLX_SGI_make_current_read\n");
929	}
930#endif
931
932#ifdef __DRI_COPY_SUB_BUFFER
933	if (strcmp(extensions[i]->name, __DRI_COPY_SUB_BUFFER) == 0) {
934	    screen->copySubBuffer = (__DRIcopySubBufferExtension *) extensions[i];
935	    __glXEnableExtension(screen->glx_enable_bits,
936				 "GLX_MESA_copy_sub_buffer");
937
938	    LogMessage(X_INFO, "AIGLX: enabled GLX_MESA_copy_sub_buffer\n");
939	}
940#endif
941
942#ifdef __DRI_SWAP_CONTROL
943	if (strcmp(extensions[i]->name, __DRI_SWAP_CONTROL) == 0) {
944	    screen->swapControl = (__DRIswapControlExtension *) extensions[i];
945	    __glXEnableExtension(screen->glx_enable_bits,
946				 "GLX_SGI_swap_control");
947	    __glXEnableExtension(screen->glx_enable_bits,
948				 "GLX_MESA_swap_control");
949
950	    LogMessage(X_INFO, "AIGLX: enabled GLX_SGI_swap_control and GLX_MESA_swap_control\n");
951	}
952#endif
953
954#ifdef __DRI_TEX_OFFSET
955	if (strcmp(extensions[i]->name, __DRI_TEX_OFFSET) == 0) {
956	    screen->texOffset = (__DRItexOffsetExtension *) extensions[i];
957	    LogMessage(X_INFO, "AIGLX: enabled GLX_texture_from_pixmap with driver support\n");
958	}
959#endif
960	/* Ignore unknown extensions */
961    }
962}
963
964static __GLXscreen *
965__glXDRIscreenProbe(ScreenPtr pScreen)
966{
967    drm_handle_t hSAREA;
968    drmAddress pSAREA = NULL;
969    char *BusID;
970    __DRIversion   ddx_version;
971    __DRIversion   dri_version;
972    __DRIversion   drm_version;
973    __DRIframebuffer  framebuffer;
974    int   fd = -1;
975    int   status;
976    drm_magic_t magic;
977    drmVersionPtr version;
978    int newlyopened;
979    char *driverName;
980    drm_handle_t  hFB;
981    int        junk;
982    __GLXDRIscreen *screen;
983    char filename[128];
984    Bool isCapable;
985    size_t buffer_size;
986    ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
987    const __DRIconfig **driConfigs;
988    const __DRIextension **extensions;
989    int i;
990
991    memset(&framebuffer, 0, sizeof(framebuffer));
992
993    if (!xf86LoaderCheckSymbol("DRIQueryDirectRenderingCapable") ||
994	!DRIQueryDirectRenderingCapable(pScreen, &isCapable) ||
995	!isCapable) {
996	LogMessage(X_INFO,
997		   "AIGLX: Screen %d is not DRI capable\n", pScreen->myNum);
998	return NULL;
999    }
1000
1001    screen = calloc(1, sizeof *screen);
1002    if (screen == NULL)
1003      return NULL;
1004
1005    screen->base.destroy        = __glXDRIscreenDestroy;
1006    screen->base.createContext  = __glXDRIscreenCreateContext;
1007    screen->base.createDrawable = __glXDRIscreenCreateDrawable;
1008    screen->base.swapInterval   = __glXDRIdrawableSwapInterval;
1009    screen->base.pScreen       = pScreen;
1010
1011    __glXInitExtensionEnableBits(screen->glx_enable_bits);
1012
1013    /* DRI protocol version. */
1014    dri_version.major = XF86DRI_MAJOR_VERSION;
1015    dri_version.minor = XF86DRI_MINOR_VERSION;
1016    dri_version.patch = XF86DRI_PATCH_VERSION;
1017
1018    if (!DRIOpenConnection(pScreen, &hSAREA, &BusID)) {
1019	LogMessage(X_ERROR, "AIGLX error: DRIOpenConnection failed\n");
1020	goto handle_error;
1021    }
1022
1023    fd = drmOpenOnce(NULL, BusID, &newlyopened);
1024
1025    if (fd < 0) {
1026	LogMessage(X_ERROR, "AIGLX error: drmOpenOnce failed (%s)\n",
1027		   strerror(-fd));
1028	goto handle_error;
1029    }
1030
1031    if (drmGetMagic(fd, &magic)) {
1032	LogMessage(X_ERROR, "AIGLX error: drmGetMagic failed\n");
1033	goto handle_error;
1034    }
1035
1036    version = drmGetVersion(fd);
1037    if (version) {
1038	drm_version.major = version->version_major;
1039	drm_version.minor = version->version_minor;
1040	drm_version.patch = version->version_patchlevel;
1041	drmFreeVersion(version);
1042    }
1043    else {
1044	drm_version.major = -1;
1045	drm_version.minor = -1;
1046	drm_version.patch = -1;
1047    }
1048
1049    if (newlyopened && !DRIAuthConnection(pScreen, magic)) {
1050	LogMessage(X_ERROR, "AIGLX error: DRIAuthConnection failed\n");
1051	goto handle_error;
1052    }
1053
1054    /* Get device name (like "tdfx") and the ddx version numbers.
1055     * We'll check the version in each DRI driver's "createNewScreen"
1056     * function. */
1057    if (!DRIGetClientDriverName(pScreen,
1058				&ddx_version.major,
1059				&ddx_version.minor,
1060				&ddx_version.patch,
1061				&driverName)) {
1062	LogMessage(X_ERROR, "AIGLX error: DRIGetClientDriverName failed\n");
1063	goto handle_error;
1064    }
1065
1066    snprintf(filename, sizeof filename, "%s/%s_dri.so",
1067             dri_driver_path, driverName);
1068
1069    screen->driver = dlopen(filename, RTLD_LAZY | RTLD_LOCAL);
1070    if (screen->driver == NULL) {
1071	LogMessage(X_ERROR, "AIGLX error: dlopen of %s failed (%s)\n",
1072		   filename, dlerror());
1073        goto handle_error;
1074    }
1075
1076    extensions = dlsym(screen->driver, __DRI_DRIVER_EXTENSIONS);
1077    if (extensions == NULL) {
1078	LogMessage(X_ERROR, "AIGLX error: %s exports no extensions (%s)\n",
1079		   driverName, dlerror());
1080	goto handle_error;
1081    }
1082
1083    for (i = 0; extensions[i]; i++) {
1084	if (strcmp(extensions[i]->name, __DRI_CORE) == 0 &&
1085	    extensions[i]->version >= __DRI_CORE_VERSION) {
1086		screen->core = (__DRIcoreExtension *) extensions[i];
1087	}
1088
1089	if (strcmp(extensions[i]->name, __DRI_LEGACY) == 0 &&
1090	    extensions[i]->version >= __DRI_LEGACY_VERSION) {
1091		screen->legacy = (__DRIlegacyExtension *) extensions[i];
1092	}
1093    }
1094
1095    if (screen->core == NULL || screen->legacy == NULL) {
1096	LogMessage(X_ERROR,
1097		   "AIGLX error: %s does not export required DRI extension\n",
1098		   driverName);
1099	goto handle_error;
1100    }
1101
1102    /*
1103     * Get device-specific info.  pDevPriv will point to a struct
1104     * (such as DRIRADEONRec in xfree86/driver/ati/radeon_dri.h) that
1105     * has information about the screen size, depth, pitch, ancilliary
1106     * buffers, DRM mmap handles, etc.
1107     */
1108    if (!DRIGetDeviceInfo(pScreen, &hFB, &junk,
1109			  &framebuffer.size, &framebuffer.stride,
1110			  &framebuffer.dev_priv_size, &framebuffer.dev_priv)) {
1111	LogMessage(X_ERROR, "AIGLX error: XF86DRIGetDeviceInfo failed\n");
1112	goto handle_error;
1113    }
1114
1115    framebuffer.width = pScreen->width;
1116    framebuffer.height = pScreen->height;
1117
1118    /* Map the framebuffer region. */
1119    status = drmMap(fd, hFB, framebuffer.size,
1120		    (drmAddressPtr)&framebuffer.base);
1121    if (status != 0) {
1122	LogMessage(X_ERROR, "AIGLX error: drmMap of framebuffer failed (%s)\n",
1123		   strerror(-status));
1124	goto handle_error;
1125    }
1126
1127    /* Map the SAREA region.  Further mmap regions may be setup in
1128     * each DRI driver's "createNewScreen" function.
1129     */
1130    status = drmMap(fd, hSAREA, SAREA_MAX, &pSAREA);
1131    if (status != 0) {
1132	LogMessage(X_ERROR, "AIGLX error: drmMap of SAREA failed (%s)\n",
1133		   strerror(-status));
1134	goto handle_error;
1135    }
1136
1137    screen->driScreen =
1138	(*screen->legacy->createNewScreen)(pScreen->myNum,
1139					   &ddx_version,
1140					   &dri_version,
1141					   &drm_version,
1142					   &framebuffer,
1143					   pSAREA,
1144					   fd,
1145					   loader_extensions,
1146					   &driConfigs,
1147					   screen);
1148
1149    if (screen->driScreen == NULL) {
1150	LogMessage(X_ERROR,
1151		   "AIGLX error: Calling driver entry point failed\n");
1152	goto handle_error;
1153    }
1154
1155    screen->base.fbconfigs = glxConvertConfigs(screen->core,
1156					       driConfigs, GLX_WINDOW_BIT);
1157
1158    initializeExtensions(screen);
1159
1160    DRIGetTexOffsetFuncs(pScreen, &screen->texOffsetStart,
1161			 &screen->texOffsetFinish);
1162
1163    __glXScreenInit(&screen->base, pScreen);
1164
1165    /* The first call simply determines the length of the extension string.
1166     * This allows us to allocate some memory to hold the extension string,
1167     * but it requires that we call __glXGetExtensionString a second time.
1168     */
1169    buffer_size = __glXGetExtensionString(screen->glx_enable_bits, NULL);
1170    if (buffer_size > 0) {
1171	free(screen->base.GLXextensions);
1172
1173	screen->base.GLXextensions = xnfalloc(buffer_size);
1174	(void) __glXGetExtensionString(screen->glx_enable_bits,
1175				       screen->base.GLXextensions);
1176    }
1177
1178    __glXsetEnterLeaveServerFuncs(__glXDRIenterServer, __glXDRIleaveServer);
1179
1180    screen->enterVT = pScrn->EnterVT;
1181    pScrn->EnterVT = glxDRIEnterVT;
1182    screen->leaveVT = pScrn->LeaveVT;
1183    pScrn->LeaveVT = glxDRILeaveVT;
1184
1185    LogMessage(X_INFO,
1186	       "AIGLX: Loaded and initialized %s\n", filename);
1187
1188    return &screen->base;
1189
1190 handle_error:
1191    if (pSAREA != NULL)
1192	drmUnmap(pSAREA, SAREA_MAX);
1193
1194    if (framebuffer.base != NULL)
1195	drmUnmap((drmAddress)framebuffer.base, framebuffer.size);
1196
1197    if (fd >= 0)
1198	drmCloseOnce(fd);
1199
1200    DRICloseConnection(pScreen);
1201
1202    if (screen->driver)
1203        dlclose(screen->driver);
1204
1205    free(screen);
1206
1207    LogMessage(X_ERROR, "AIGLX: reverting to software rendering\n");
1208
1209    return NULL;
1210}
1211
1212_X_EXPORT __GLXprovider __glXDRIProvider = {
1213    __glXDRIscreenProbe,
1214    "DRI",
1215    NULL
1216};
1217