glxdri2.c revision 4642e01f
1/*
2 * Copyright © 2007 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 <dlfcn.h>
33
34#include <drm.h>
35#include <GL/gl.h>
36#include <GL/internal/dri_interface.h>
37#include <GL/glxtokens.h>
38
39#include <windowstr.h>
40#include <os.h>
41
42#define _XF86DRI_SERVER_
43#include <xf86drm.h>
44#include <xf86.h>
45#include <dri2.h>
46
47#include "glxserver.h"
48#include "glxutil.h"
49#include "glxdricommon.h"
50
51#include "g_disptab.h"
52#include "glapitable.h"
53#include "glapi.h"
54#include "glthread.h"
55#include "dispatch.h"
56#include "extension_string.h"
57
58typedef struct __GLXDRIscreen   __GLXDRIscreen;
59typedef struct __GLXDRIcontext  __GLXDRIcontext;
60typedef struct __GLXDRIdrawable __GLXDRIdrawable;
61
62struct __GLXDRIscreen {
63    __GLXscreen		 base;
64    __DRIscreen		*driScreen;
65    void		*driver;
66    int			 fd;
67
68    xf86EnterVTProc	*enterVT;
69    xf86LeaveVTProc	*leaveVT;
70
71    const __DRIcoreExtension *core;
72    const __DRIdri2Extension *dri2;
73    const __DRIcopySubBufferExtension *copySubBuffer;
74    const __DRIswapControlExtension *swapControl;
75    const __DRItexBufferExtension *texBuffer;
76
77    unsigned char glx_enable_bits[__GLX_EXT_BYTES];
78};
79
80struct __GLXDRIcontext {
81    __GLXcontext	 base;
82    __DRIcontext	*driContext;
83};
84
85struct __GLXDRIdrawable {
86    __GLXdrawable	 base;
87    __DRIdrawable	*driDrawable;
88    __GLXDRIscreen	*screen;
89
90    /* Dimensions as last reported by DRI2GetBuffers. */
91    int width;
92    int height;
93    __DRIbuffer buffers[5];
94    int count;
95};
96
97static void
98__glXDRIdrawableDestroy(__GLXdrawable *drawable)
99{
100    __GLXDRIdrawable *private = (__GLXDRIdrawable *) drawable;
101    const __DRIcoreExtension *core = private->screen->core;
102
103    (*core->destroyDrawable)(private->driDrawable);
104
105    /* If the X window was destroyed, the dri DestroyWindow hook will
106     * aready have taken care of this, so only call if pDraw isn't NULL. */
107    if (drawable->pDraw != NULL)
108	DRI2DestroyDrawable(drawable->pDraw);
109
110    __glXDrawableRelease(drawable);
111
112    xfree(private);
113}
114
115static void
116__glXDRIdrawableCopySubBuffer(__GLXdrawable *drawable,
117			       int x, int y, int w, int h)
118{
119    __GLXDRIdrawable *private = (__GLXDRIdrawable *) drawable;
120    BoxRec box;
121    RegionRec region;
122
123    box.x1 = x;
124    box.y1 = private->height - y - h;
125    box.x2 = x + w;
126    box.y2 = private->height - y;
127    REGION_INIT(drawable->pDraw->pScreen, &region, &box, 0);
128
129    DRI2CopyRegion(drawable->pDraw, &region,
130		   DRI2BufferFrontLeft, DRI2BufferBackLeft);
131}
132
133static GLboolean
134__glXDRIdrawableSwapBuffers(__GLXdrawable *drawable)
135{
136    __GLXDRIdrawable *private = (__GLXDRIdrawable *) drawable;
137
138    __glXDRIdrawableCopySubBuffer(drawable, 0, 0,
139				  private->width, private->height);
140
141    return TRUE;
142}
143
144
145static int
146__glXDRIdrawableSwapInterval(__GLXdrawable *drawable, int interval)
147{
148    return 0;
149}
150
151static void
152__glXDRIcontextDestroy(__GLXcontext *baseContext)
153{
154    __GLXDRIcontext *context = (__GLXDRIcontext *) baseContext;
155    __GLXDRIscreen *screen = (__GLXDRIscreen *) context->base.pGlxScreen;
156
157    (*screen->core->destroyContext)(context->driContext);
158    __glXContextDestroy(&context->base);
159    xfree(context);
160}
161
162static int
163__glXDRIcontextMakeCurrent(__GLXcontext *baseContext)
164{
165    __GLXDRIcontext *context = (__GLXDRIcontext *) baseContext;
166    __GLXDRIdrawable *draw = (__GLXDRIdrawable *) baseContext->drawPriv;
167    __GLXDRIdrawable *read = (__GLXDRIdrawable *) baseContext->readPriv;
168    __GLXDRIscreen *screen = (__GLXDRIscreen *) context->base.pGlxScreen;
169
170    return (*screen->core->bindContext)(context->driContext,
171					draw->driDrawable,
172					read->driDrawable);
173}
174
175static int
176__glXDRIcontextLoseCurrent(__GLXcontext *baseContext)
177{
178    __GLXDRIcontext *context = (__GLXDRIcontext *) baseContext;
179    __GLXDRIscreen *screen = (__GLXDRIscreen *) context->base.pGlxScreen;
180
181    return (*screen->core->unbindContext)(context->driContext);
182}
183
184static int
185__glXDRIcontextCopy(__GLXcontext *baseDst, __GLXcontext *baseSrc,
186		    unsigned long mask)
187{
188    __GLXDRIcontext *dst = (__GLXDRIcontext *) baseDst;
189    __GLXDRIcontext *src = (__GLXDRIcontext *) baseSrc;
190    __GLXDRIscreen *screen = (__GLXDRIscreen *) dst->base.pGlxScreen;
191
192    return (*screen->core->copyContext)(dst->driContext,
193					src->driContext, mask);
194}
195
196static int
197__glXDRIcontextForceCurrent(__GLXcontext *baseContext)
198{
199    __GLXDRIcontext *context = (__GLXDRIcontext *) baseContext;
200    __GLXDRIdrawable *draw = (__GLXDRIdrawable *) baseContext->drawPriv;
201    __GLXDRIdrawable *read = (__GLXDRIdrawable *) baseContext->readPriv;
202    __GLXDRIscreen *screen = (__GLXDRIscreen *) context->base.pGlxScreen;
203
204    return (*screen->core->bindContext)(context->driContext,
205					draw->driDrawable,
206					read->driDrawable);
207}
208
209#ifdef __DRI_TEX_BUFFER
210
211static int
212__glXDRIbindTexImage(__GLXcontext *baseContext,
213		     int buffer,
214		     __GLXdrawable *glxPixmap)
215{
216    __GLXDRIdrawable *drawable = (__GLXDRIdrawable *) glxPixmap;
217    const __DRItexBufferExtension *texBuffer = drawable->screen->texBuffer;
218    __GLXDRIcontext *context = (__GLXDRIcontext *) baseContext;
219
220    if (texBuffer == NULL)
221        return Success;
222
223    texBuffer->setTexBuffer(context->driContext,
224			    glxPixmap->target,
225			    drawable->driDrawable);
226
227    return Success;
228}
229
230static int
231__glXDRIreleaseTexImage(__GLXcontext *baseContext,
232			int buffer,
233			__GLXdrawable *pixmap)
234{
235    /* FIXME: Just unbind the texture? */
236    return Success;
237}
238
239#else
240
241static int
242__glXDRIbindTexImage(__GLXcontext *baseContext,
243		     int buffer,
244		     __GLXdrawable *glxPixmap)
245{
246    return Success;
247}
248
249static int
250__glXDRIreleaseTexImage(__GLXcontext *baseContext,
251			int buffer,
252			__GLXdrawable *pixmap)
253{
254    return Success;
255}
256
257#endif
258
259static __GLXtextureFromPixmap __glXDRItextureFromPixmap = {
260    __glXDRIbindTexImage,
261    __glXDRIreleaseTexImage
262};
263
264static void
265__glXDRIscreenDestroy(__GLXscreen *baseScreen)
266{
267    __GLXDRIscreen *screen = (__GLXDRIscreen *) baseScreen;
268
269    (*screen->core->destroyScreen)(screen->driScreen);
270
271    dlclose(screen->driver);
272
273    __glXScreenDestroy(baseScreen);
274
275    xfree(screen);
276}
277
278static __GLXcontext *
279__glXDRIscreenCreateContext(__GLXscreen *baseScreen,
280			    __GLXconfig *glxConfig,
281			    __GLXcontext *baseShareContext)
282{
283    __GLXDRIscreen *screen = (__GLXDRIscreen *) baseScreen;
284    __GLXDRIcontext *context, *shareContext;
285    __GLXDRIconfig *config = (__GLXDRIconfig *) glxConfig;
286    __DRIcontext *driShare;
287
288    shareContext = (__GLXDRIcontext *) baseShareContext;
289    if (shareContext)
290	driShare = shareContext->driContext;
291    else
292	driShare = NULL;
293
294    context = xcalloc(1, sizeof *context);
295    if (context == NULL)
296	return NULL;
297
298    context->base.destroy           = __glXDRIcontextDestroy;
299    context->base.makeCurrent       = __glXDRIcontextMakeCurrent;
300    context->base.loseCurrent       = __glXDRIcontextLoseCurrent;
301    context->base.copy              = __glXDRIcontextCopy;
302    context->base.forceCurrent      = __glXDRIcontextForceCurrent;
303    context->base.textureFromPixmap = &__glXDRItextureFromPixmap;
304
305    context->driContext =
306	(*screen->dri2->createNewContext)(screen->driScreen,
307					  config->driConfig,
308					  driShare, context);
309    if (context->driContext == NULL) {
310	    xfree(context);
311        return NULL;
312    }
313
314    return &context->base;
315}
316
317static __GLXdrawable *
318__glXDRIscreenCreateDrawable(__GLXscreen *screen,
319			     DrawablePtr pDraw,
320			     int type,
321			     XID drawId,
322			     __GLXconfig *glxConfig)
323{
324    __GLXDRIscreen *driScreen = (__GLXDRIscreen *) screen;
325    __GLXDRIconfig *config = (__GLXDRIconfig *) glxConfig;
326    __GLXDRIdrawable *private;
327
328    private = xcalloc(1, sizeof *private);
329    if (private == NULL)
330	return NULL;
331
332    private->screen = driScreen;
333    if (!__glXDrawableInit(&private->base, screen,
334			   pDraw, type, drawId, glxConfig)) {
335        xfree(private);
336	return NULL;
337    }
338
339    private->base.destroy       = __glXDRIdrawableDestroy;
340    private->base.swapBuffers   = __glXDRIdrawableSwapBuffers;
341    private->base.copySubBuffer = __glXDRIdrawableCopySubBuffer;
342
343    if (DRI2CreateDrawable(pDraw)) {
344	    xfree(private);
345	    return NULL;
346    }
347
348    private->driDrawable =
349	(*driScreen->dri2->createNewDrawable)(driScreen->driScreen,
350					      config->driConfig, private);
351
352    return &private->base;
353}
354
355static __DRIbuffer *
356dri2GetBuffers(__DRIdrawable *driDrawable,
357	       int *width, int *height,
358	       unsigned int *attachments, int count,
359	       int *out_count, void *loaderPrivate)
360{
361    __GLXDRIdrawable *private = loaderPrivate;
362    DRI2BufferPtr buffers;
363    int i;
364
365    buffers = DRI2GetBuffers(private->base.pDraw,
366			     width, height, attachments, count, out_count);
367    if (*out_count > 5) {
368	*out_count = 0;
369	return NULL;
370    }
371
372    private->width = *width;
373    private->height = *height;
374
375    /* This assumes the DRI2 buffer attachment tokens matches the
376     * __DRIbuffer tokens. */
377    for (i = 0; i < *out_count; i++) {
378	private->buffers[i].attachment = buffers[i].attachment;
379	private->buffers[i].name = buffers[i].name;
380	private->buffers[i].pitch = buffers[i].pitch;
381	private->buffers[i].cpp = buffers[i].cpp;
382	private->buffers[i].flags = buffers[i].flags;
383    }
384
385    return private->buffers;
386}
387
388static const __DRIdri2LoaderExtension loaderExtension = {
389    { __DRI_DRI2_LOADER, __DRI_DRI2_LOADER_VERSION },
390    dri2GetBuffers,
391};
392
393static const __DRIextension *loader_extensions[] = {
394    &systemTimeExtension.base,
395    &loaderExtension.base,
396    NULL
397};
398
399static const char dri_driver_path[] = DRI_DRIVER_PATH;
400
401static Bool
402glxDRIEnterVT (int index, int flags)
403{
404    __GLXDRIscreen *screen = (__GLXDRIscreen *)
405	glxGetScreen(screenInfo.screens[index]);
406
407    LogMessage(X_INFO, "AIGLX: Resuming AIGLX clients after VT switch\n");
408
409    if (!(*screen->enterVT) (index, flags))
410	return FALSE;
411
412    glxResumeClients();
413
414    return TRUE;
415}
416
417static void
418glxDRILeaveVT (int index, int flags)
419{
420    __GLXDRIscreen *screen = (__GLXDRIscreen *)
421	glxGetScreen(screenInfo.screens[index]);
422
423    LogMessage(X_INFO, "AIGLX: Suspending AIGLX clients for VT switch\n");
424
425    glxSuspendClients();
426
427    return (*screen->leaveVT) (index, flags);
428}
429
430static void
431initializeExtensions(__GLXDRIscreen *screen)
432{
433    const __DRIextension **extensions;
434    int i;
435
436    extensions = screen->core->getExtensions(screen->driScreen);
437
438    __glXEnableExtension(screen->glx_enable_bits,
439			 "GLX_MESA_copy_sub_buffer");
440    LogMessage(X_INFO, "AIGLX: enabled GLX_MESA_copy_sub_buffer\n");
441
442    for (i = 0; extensions[i]; i++) {
443#ifdef __DRI_SWAP_CONTROL
444	if (strcmp(extensions[i]->name, __DRI_SWAP_CONTROL) == 0) {
445	    screen->swapControl =
446		(const __DRIswapControlExtension *) extensions[i];
447	    __glXEnableExtension(screen->glx_enable_bits,
448				 "GLX_SGI_swap_control");
449	    __glXEnableExtension(screen->glx_enable_bits,
450				 "GLX_MESA_swap_control");
451
452	    LogMessage(X_INFO, "AIGLX: enabled GLX_SGI_swap_control and GLX_MESA_swap_control\n");
453	}
454#endif
455
456#ifdef __DRI_TEX_BUFFER
457	if (strcmp(extensions[i]->name, __DRI_TEX_BUFFER) == 0) {
458	    screen->texBuffer =
459		(const __DRItexBufferExtension *) extensions[i];
460	    /* GLX_EXT_texture_from_pixmap is always enabled. */
461	    LogMessage(X_INFO, "AIGLX: GLX_EXT_texture_from_pixmap backed by buffer objects\n");
462	}
463#endif
464	/* Ignore unknown extensions */
465    }
466}
467
468static __GLXscreen *
469__glXDRIscreenProbe(ScreenPtr pScreen)
470{
471    const char *driverName, *deviceName;
472    __GLXDRIscreen *screen;
473    char filename[128];
474    size_t buffer_size;
475    ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
476    const __DRIextension **extensions;
477    const __DRIconfig **driConfigs;
478    int i;
479
480    screen = xcalloc(1, sizeof *screen);
481    if (screen == NULL)
482	return NULL;
483
484    if (!xf86LoaderCheckSymbol("DRI2Connect") ||
485	!DRI2Connect(pScreen, DRI2DriverDRI,
486		     &screen->fd, &driverName, &deviceName)) {
487	LogMessage(X_INFO,
488		   "AIGLX: Screen %d is not DRI2 capable\n", pScreen->myNum);
489	return NULL;
490    }
491
492    screen->base.destroy        = __glXDRIscreenDestroy;
493    screen->base.createContext  = __glXDRIscreenCreateContext;
494    screen->base.createDrawable = __glXDRIscreenCreateDrawable;
495    screen->base.swapInterval   = __glXDRIdrawableSwapInterval;
496    screen->base.pScreen       = pScreen;
497
498    __glXInitExtensionEnableBits(screen->glx_enable_bits);
499
500    snprintf(filename, sizeof filename,
501	     "%s/%s_dri.so", dri_driver_path, driverName);
502
503    screen->driver = dlopen(filename, RTLD_LAZY | RTLD_LOCAL);
504    if (screen->driver == NULL) {
505	LogMessage(X_ERROR, "AIGLX error: dlopen of %s failed (%s)\n",
506		   filename, dlerror());
507        goto handle_error;
508    }
509
510    extensions = dlsym(screen->driver, __DRI_DRIVER_EXTENSIONS);
511    if (extensions == NULL) {
512	LogMessage(X_ERROR, "AIGLX error: %s exports no extensions (%s)\n",
513		   driverName, dlerror());
514	goto handle_error;
515    }
516
517    for (i = 0; extensions[i]; i++) {
518        if (strcmp(extensions[i]->name, __DRI_CORE) == 0 &&
519	    extensions[i]->version >= __DRI_CORE_VERSION) {
520		screen->core = (const __DRIcoreExtension *) extensions[i];
521	}
522        if (strcmp(extensions[i]->name, __DRI_DRI2) == 0 &&
523	    extensions[i]->version >= __DRI_DRI2_VERSION) {
524		screen->dri2 = (const __DRIdri2Extension *) extensions[i];
525	}
526    }
527
528    if (screen->core == NULL || screen->dri2 == NULL) {
529	LogMessage(X_ERROR, "AIGLX error: %s exports no DRI extension\n",
530		   driverName);
531	goto handle_error;
532    }
533
534    screen->driScreen =
535	(*screen->dri2->createNewScreen)(pScreen->myNum,
536					 screen->fd,
537					 loader_extensions,
538					 &driConfigs,
539					 screen);
540
541    if (screen->driScreen == NULL) {
542	LogMessage(X_ERROR,
543		   "AIGLX error: Calling driver entry point failed\n");
544	goto handle_error;
545    }
546
547    initializeExtensions(screen);
548
549    screen->base.fbconfigs = glxConvertConfigs(screen->core, driConfigs);
550
551    __glXScreenInit(&screen->base, pScreen);
552
553    buffer_size = __glXGetExtensionString(screen->glx_enable_bits, NULL);
554    if (buffer_size > 0) {
555	if (screen->base.GLXextensions != NULL) {
556	    xfree(screen->base.GLXextensions);
557	}
558
559	screen->base.GLXextensions = xnfalloc(buffer_size);
560	(void) __glXGetExtensionString(screen->glx_enable_bits,
561				       screen->base.GLXextensions);
562    }
563
564    screen->enterVT = pScrn->EnterVT;
565    pScrn->EnterVT = glxDRIEnterVT;
566    screen->leaveVT = pScrn->LeaveVT;
567    pScrn->LeaveVT = glxDRILeaveVT;
568
569    LogMessage(X_INFO,
570	       "AIGLX: Loaded and initialized %s\n", filename);
571
572    return &screen->base;
573
574 handle_error:
575    if (screen->driver)
576        dlclose(screen->driver);
577
578    xfree(screen);
579
580    LogMessage(X_ERROR, "AIGLX: reverting to software rendering\n");
581
582    return NULL;
583}
584
585__GLXprovider __glXDRI2Provider = {
586    __glXDRIscreenProbe,
587    "DRI2",
588    NULL
589};
590