/* * Copyright © 2007 Red Hat, Inc * * Permission to use, copy, modify, distribute, and sell this software * and its documentation for any purpose is hereby granted without * fee, provided that the above copyright notice appear in all copies * and that both that copyright notice and this permission notice * appear in supporting documentation, and that the name of Red Hat, * Inc not be used in advertising or publicity pertaining to * distribution of the software without specific, written prior * permission. Red Hat, Inc makes no representations about the * suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * RED HAT, INC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN * NO EVENT SHALL RED HAT, INC BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifdef HAVE_DIX_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #define _XF86DRI_SERVER_ #include #include #include #include "glxserver.h" #include "glxutil.h" #include "g_disptab.h" #include "glapitable.h" #include "glapi.h" #include "glthread.h" #include "dispatch.h" #include "extension_string.h" typedef struct __GLXDRIscreen __GLXDRIscreen; typedef struct __GLXDRIcontext __GLXDRIcontext; typedef struct __GLXDRIdrawable __GLXDRIdrawable; typedef struct __GLXDRIconfig __GLXDRIconfig; struct __GLXDRIscreen { __GLXscreen base; __DRIscreen *driScreen; void *driver; int fd; xf86EnterVTProc *enterVT; xf86LeaveVTProc *leaveVT; const __DRIcoreExtension *core; const __DRIcopySubBufferExtension *copySubBuffer; const __DRIswapControlExtension *swapControl; const __DRItexBufferExtension *texBuffer; unsigned char glx_enable_bits[__GLX_EXT_BYTES]; }; struct __GLXDRIcontext { __GLXcontext base; __DRIcontext *driContext; }; struct __GLXDRIdrawable { __GLXdrawable base; __DRIdrawable *driDrawable; __GLXDRIscreen *screen; }; struct __GLXDRIconfig { __GLXconfig config; const __DRIconfig *driConfig; }; static void __glXDRIdrawableDestroy(__GLXdrawable *drawable) { __GLXDRIdrawable *private = (__GLXDRIdrawable *) drawable; const __DRIcoreExtension *core = private->screen->core; (*core->destroyDrawable)(private->driDrawable); /* If the X window was destroyed, the dri DestroyWindow hook will * aready have taken care of this, so only call if pDraw isn't NULL. */ if (drawable->pDraw != NULL) DRI2DestroyDrawable(drawable->pDraw); xfree(private); } static GLboolean __glXDRIdrawableResize(__GLXdrawable *glxPriv) { /* Nothing to do here, the DRI driver asks the server for drawable * geometry when it sess the SAREA timestamps change.*/ return GL_TRUE; } static GLboolean __glXDRIdrawableSwapBuffers(__GLXdrawable *drawable) { __GLXDRIdrawable *private = (__GLXDRIdrawable *) drawable; const __DRIcoreExtension *core = private->screen->core; (*core->swapBuffers)(private->driDrawable); return TRUE; } static int __glXDRIdrawableSwapInterval(__GLXdrawable *drawable, int interval) { __GLXDRIdrawable *private = (__GLXDRIdrawable *) drawable; const __DRIswapControlExtension *swapControl = private->screen->swapControl; if (swapControl) swapControl->setSwapInterval(private->driDrawable, interval); return 0; } static void __glXDRIdrawableCopySubBuffer(__GLXdrawable *basePrivate, int x, int y, int w, int h) { __GLXDRIdrawable *private = (__GLXDRIdrawable *) basePrivate; const __DRIcopySubBufferExtension *copySubBuffer = private->screen->copySubBuffer; if (copySubBuffer) (*copySubBuffer->copySubBuffer)(private->driDrawable, x, y, w, h); } static void __glXDRIcontextDestroy(__GLXcontext *baseContext) { __GLXDRIcontext *context = (__GLXDRIcontext *) baseContext; __GLXDRIscreen *screen = (__GLXDRIscreen *) context->base.pGlxScreen; (*screen->core->destroyContext)(context->driContext); __glXContextDestroy(&context->base); xfree(context); } static int __glXDRIcontextMakeCurrent(__GLXcontext *baseContext) { __GLXDRIcontext *context = (__GLXDRIcontext *) baseContext; __GLXDRIdrawable *draw = (__GLXDRIdrawable *) baseContext->drawPriv; __GLXDRIdrawable *read = (__GLXDRIdrawable *) baseContext->readPriv; __GLXDRIscreen *screen = (__GLXDRIscreen *) context->base.pGlxScreen; return (*screen->core->bindContext)(context->driContext, draw->driDrawable, read->driDrawable); } static int __glXDRIcontextLoseCurrent(__GLXcontext *baseContext) { __GLXDRIcontext *context = (__GLXDRIcontext *) baseContext; __GLXDRIscreen *screen = (__GLXDRIscreen *) context->base.pGlxScreen; return (*screen->core->unbindContext)(context->driContext); } static int __glXDRIcontextCopy(__GLXcontext *baseDst, __GLXcontext *baseSrc, unsigned long mask) { __GLXDRIcontext *dst = (__GLXDRIcontext *) baseDst; __GLXDRIcontext *src = (__GLXDRIcontext *) baseSrc; __GLXDRIscreen *screen = (__GLXDRIscreen *) dst->base.pGlxScreen; return (*screen->core->copyContext)(dst->driContext, src->driContext, mask); } static int __glXDRIcontextForceCurrent(__GLXcontext *baseContext) { __GLXDRIcontext *context = (__GLXDRIcontext *) baseContext; __GLXDRIdrawable *draw = (__GLXDRIdrawable *) baseContext->drawPriv; __GLXDRIdrawable *read = (__GLXDRIdrawable *) baseContext->readPriv; __GLXDRIscreen *screen = (__GLXDRIscreen *) context->base.pGlxScreen; return (*screen->core->bindContext)(context->driContext, draw->driDrawable, read->driDrawable); } #ifdef __DRI_TEX_BUFFER static int __glXDRIbindTexImage(__GLXcontext *baseContext, int buffer, __GLXdrawable *glxPixmap) { __GLXDRIdrawable *drawable = (__GLXDRIdrawable *) glxPixmap; const __DRItexBufferExtension *texBuffer = drawable->screen->texBuffer; __GLXDRIcontext *context = (__GLXDRIcontext *) baseContext; if (texBuffer == NULL) return Success; texBuffer->setTexBuffer(context->driContext, glxPixmap->target, drawable->driDrawable); return Success; } static int __glXDRIreleaseTexImage(__GLXcontext *baseContext, int buffer, __GLXdrawable *pixmap) { /* FIXME: Just unbind the texture? */ return Success; } #else static int __glXDRIbindTexImage(__GLXcontext *baseContext, int buffer, __GLXdrawable *glxPixmap) { return Success; } static int __glXDRIreleaseTexImage(__GLXcontext *baseContext, int buffer, __GLXdrawable *pixmap) { return Success; } #endif static __GLXtextureFromPixmap __glXDRItextureFromPixmap = { __glXDRIbindTexImage, __glXDRIreleaseTexImage }; static void __glXDRIscreenDestroy(__GLXscreen *baseScreen) { __GLXDRIscreen *screen = (__GLXDRIscreen *) baseScreen; (*screen->core->destroyScreen)(screen->driScreen); dlclose(screen->driver); __glXScreenDestroy(baseScreen); xfree(screen); } static __GLXcontext * __glXDRIscreenCreateContext(__GLXscreen *baseScreen, __GLXconfig *glxConfig, __GLXcontext *baseShareContext) { __GLXDRIscreen *screen = (__GLXDRIscreen *) baseScreen; __GLXDRIcontext *context, *shareContext; __GLXDRIconfig *config = (__GLXDRIconfig *) glxConfig; const __DRIcoreExtension *core = screen->core; __DRIcontext *driShare; shareContext = (__GLXDRIcontext *) baseShareContext; if (shareContext) driShare = shareContext->driContext; else driShare = NULL; context = xalloc(sizeof *context); if (context == NULL) return NULL; memset(context, 0, sizeof *context); context->base.destroy = __glXDRIcontextDestroy; context->base.makeCurrent = __glXDRIcontextMakeCurrent; context->base.loseCurrent = __glXDRIcontextLoseCurrent; context->base.copy = __glXDRIcontextCopy; context->base.forceCurrent = __glXDRIcontextForceCurrent; context->base.textureFromPixmap = &__glXDRItextureFromPixmap; context->driContext = (*core->createNewContext)(screen->driScreen, config->driConfig, driShare, context); return &context->base; } static __GLXdrawable * __glXDRIscreenCreateDrawable(__GLXscreen *screen, DrawablePtr pDraw, int type, XID drawId, __GLXconfig *glxConfig) { __GLXDRIscreen *driScreen = (__GLXDRIscreen *) screen; __GLXDRIconfig *config = (__GLXDRIconfig *) glxConfig; __GLXDRIdrawable *private; GLboolean retval; unsigned int handle, head; private = xalloc(sizeof *private); if (private == NULL) return NULL; memset(private, 0, sizeof *private); private->screen = driScreen; if (!__glXDrawableInit(&private->base, screen, pDraw, type, drawId, glxConfig)) { xfree(private); return NULL; } private->base.destroy = __glXDRIdrawableDestroy; private->base.resize = __glXDRIdrawableResize; private->base.swapBuffers = __glXDRIdrawableSwapBuffers; private->base.copySubBuffer = __glXDRIdrawableCopySubBuffer; retval = DRI2CreateDrawable(pDraw, &handle, &head); private->driDrawable = (*driScreen->core->createNewDrawable)(driScreen->driScreen, config->driConfig, handle, head, private); return &private->base; } static int getUST(int64_t *ust) { struct timeval tv; if (ust == NULL) return -EFAULT; if (gettimeofday(&tv, NULL) == 0) { ust[0] = (tv.tv_sec * 1000000) + tv.tv_usec; return 0; } else { return -errno; } } static const __DRIsystemTimeExtension systemTimeExtension = { { __DRI_SYSTEM_TIME, __DRI_SYSTEM_TIME_VERSION }, getUST, NULL, }; static void dri2ReemitDrawableInfo(__DRIdrawable *draw, unsigned int *tail, void *loaderPrivate) { __GLXDRIdrawable *pdraw = loaderPrivate; DRI2ReemitDrawableInfo(pdraw->base.pDraw, tail); } static void dri2PostDamage(__DRIdrawable *draw, struct drm_clip_rect *rects, int numRects, void *loaderPrivate) { __GLXDRIdrawable *drawable = loaderPrivate; DrawablePtr pDraw = drawable->base.pDraw; RegionRec region; REGION_INIT(pDraw->pScreen, ®ion, (BoxPtr) rects, numRects); REGION_TRANSLATE(pScreen, ®ion, pDraw->x, pDraw->y); DamageDamageRegion(pDraw, ®ion); REGION_UNINIT(pDraw->pScreen, ®ion); } static const __DRIloaderExtension loaderExtension = { { __DRI_LOADER, __DRI_LOADER_VERSION }, dri2ReemitDrawableInfo, dri2PostDamage }; static const __DRIextension *loader_extensions[] = { &systemTimeExtension.base, &loaderExtension.base, NULL }; static const char dri_driver_path[] = DRI_DRIVER_PATH; static Bool glxDRIEnterVT (int index, int flags) { __GLXDRIscreen *screen = (__GLXDRIscreen *) glxGetScreen(screenInfo.screens[index]); LogMessage(X_INFO, "AIGLX: Resuming AIGLX clients after VT switch\n"); if (!(*screen->enterVT) (index, flags)) return FALSE; glxResumeClients(); return TRUE; } static void glxDRILeaveVT (int index, int flags) { __GLXDRIscreen *screen = (__GLXDRIscreen *) glxGetScreen(screenInfo.screens[index]); LogMessage(X_INFO, "AIGLX: Suspending AIGLX clients for VT switch\n"); glxSuspendClients(); return (*screen->leaveVT) (index, flags); } static void initializeExtensions(__GLXDRIscreen *screen) { const __DRIextension **extensions; int i; extensions = screen->core->getExtensions(screen->driScreen); for (i = 0; extensions[i]; i++) { #ifdef __DRI_COPY_SUB_BUFFER if (strcmp(extensions[i]->name, __DRI_COPY_SUB_BUFFER) == 0) { screen->copySubBuffer = (const __DRIcopySubBufferExtension *) extensions[i]; __glXEnableExtension(screen->glx_enable_bits, "GLX_MESA_copy_sub_buffer"); LogMessage(X_INFO, "AIGLX: enabled GLX_MESA_copy_sub_buffer\n"); } #endif #ifdef __DRI_SWAP_CONTROL if (strcmp(extensions[i]->name, __DRI_SWAP_CONTROL) == 0) { screen->swapControl = (const __DRIswapControlExtension *) extensions[i]; __glXEnableExtension(screen->glx_enable_bits, "GLX_SGI_swap_control"); __glXEnableExtension(screen->glx_enable_bits, "GLX_MESA_swap_control"); LogMessage(X_INFO, "AIGLX: enabled GLX_SGI_swap_control and GLX_MESA_swap_control\n"); } #endif #ifdef __DRI_TEX_BUFFER if (strcmp(extensions[i]->name, __DRI_TEX_BUFFER) == 0) { screen->texBuffer = (const __DRItexBufferExtension *) extensions[i]; /* GLX_EXT_texture_from_pixmap is always enabled. */ LogMessage(X_INFO, "AIGLX: GLX_EXT_texture_from_pixmap backed by buffer objects\n"); } #endif /* Ignore unknown extensions */ } } #define __ATTRIB(attrib, field) \ { attrib, offsetof(__GLXconfig, field) } static const struct { unsigned int attrib, offset; } attribMap[] = { __ATTRIB(__DRI_ATTRIB_BUFFER_SIZE, rgbBits), __ATTRIB(__DRI_ATTRIB_LEVEL, level), __ATTRIB(__DRI_ATTRIB_RED_SIZE, redBits), __ATTRIB(__DRI_ATTRIB_GREEN_SIZE, greenBits), __ATTRIB(__DRI_ATTRIB_BLUE_SIZE, blueBits), __ATTRIB(__DRI_ATTRIB_ALPHA_SIZE, alphaBits), __ATTRIB(__DRI_ATTRIB_DEPTH_SIZE, depthBits), __ATTRIB(__DRI_ATTRIB_STENCIL_SIZE, stencilBits), __ATTRIB(__DRI_ATTRIB_ACCUM_RED_SIZE, accumRedBits), __ATTRIB(__DRI_ATTRIB_ACCUM_GREEN_SIZE, accumGreenBits), __ATTRIB(__DRI_ATTRIB_ACCUM_BLUE_SIZE, accumBlueBits), __ATTRIB(__DRI_ATTRIB_ACCUM_ALPHA_SIZE, accumAlphaBits), __ATTRIB(__DRI_ATTRIB_SAMPLE_BUFFERS, sampleBuffers), __ATTRIB(__DRI_ATTRIB_SAMPLES, samples), __ATTRIB(__DRI_ATTRIB_DOUBLE_BUFFER, doubleBufferMode), __ATTRIB(__DRI_ATTRIB_STEREO, stereoMode), __ATTRIB(__DRI_ATTRIB_AUX_BUFFERS, numAuxBuffers), __ATTRIB(__DRI_ATTRIB_TRANSPARENT_TYPE, transparentPixel), __ATTRIB(__DRI_ATTRIB_TRANSPARENT_INDEX_VALUE, transparentPixel), __ATTRIB(__DRI_ATTRIB_TRANSPARENT_RED_VALUE, transparentRed), __ATTRIB(__DRI_ATTRIB_TRANSPARENT_GREEN_VALUE, transparentGreen), __ATTRIB(__DRI_ATTRIB_TRANSPARENT_BLUE_VALUE, transparentBlue), __ATTRIB(__DRI_ATTRIB_TRANSPARENT_ALPHA_VALUE, transparentAlpha), __ATTRIB(__DRI_ATTRIB_FLOAT_MODE, floatMode), __ATTRIB(__DRI_ATTRIB_RED_MASK, redMask), __ATTRIB(__DRI_ATTRIB_GREEN_MASK, greenMask), __ATTRIB(__DRI_ATTRIB_BLUE_MASK, blueMask), __ATTRIB(__DRI_ATTRIB_ALPHA_MASK, alphaMask), __ATTRIB(__DRI_ATTRIB_MAX_PBUFFER_WIDTH, maxPbufferWidth), __ATTRIB(__DRI_ATTRIB_MAX_PBUFFER_HEIGHT, maxPbufferHeight), __ATTRIB(__DRI_ATTRIB_MAX_PBUFFER_PIXELS, maxPbufferPixels), __ATTRIB(__DRI_ATTRIB_OPTIMAL_PBUFFER_WIDTH, optimalPbufferWidth), __ATTRIB(__DRI_ATTRIB_OPTIMAL_PBUFFER_HEIGHT, optimalPbufferHeight), __ATTRIB(__DRI_ATTRIB_SWAP_METHOD, swapMethod), __ATTRIB(__DRI_ATTRIB_BIND_TO_TEXTURE_RGB, bindToTextureRgb), __ATTRIB(__DRI_ATTRIB_BIND_TO_TEXTURE_RGBA, bindToTextureRgba), __ATTRIB(__DRI_ATTRIB_BIND_TO_MIPMAP_TEXTURE, bindToMipmapTexture), __ATTRIB(__DRI_ATTRIB_YINVERTED, yInverted), }; #define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0])) static void setScalar(__GLXconfig *config, unsigned int attrib, unsigned int value) { int i; for (i = 0; i < ARRAY_SIZE(attribMap); i++) if (attribMap[i].attrib == attrib) { *(unsigned int *) ((char *) config + attribMap[i].offset) = value; return; } } static __GLXconfig * createModeFromConfig(const __DRIcoreExtension *core, const __DRIconfig *driConfig, unsigned int visualType) { __GLXDRIconfig *config; unsigned int attrib, value; int i; config = xalloc(sizeof *config); config->driConfig = driConfig; i = 0; while (core->indexConfigAttrib(driConfig, i++, &attrib, &value)) { switch (attrib) { case __DRI_ATTRIB_RENDER_TYPE: if (value & __DRI_ATTRIB_RGBA_BIT) { config->config.renderType |= GLX_RGBA_BIT; config->config.rgbMode = GL_TRUE; } else if (value & __DRI_ATTRIB_COLOR_INDEX_BIT) { config->config.renderType |= GLX_COLOR_INDEX_BIT; config->config.rgbMode = GL_FALSE; } else { config->config.renderType = 0; config->config.rgbMode = GL_FALSE; } break; case __DRI_ATTRIB_CONFIG_CAVEAT: if (value & __DRI_ATTRIB_NON_CONFORMANT_CONFIG) config->config.visualRating = GLX_NON_CONFORMANT_CONFIG; else if (value & __DRI_ATTRIB_SLOW_BIT) config->config.visualRating = GLX_SLOW_CONFIG; else config->config.visualRating = GLX_NONE; break; case __DRI_ATTRIB_BIND_TO_TEXTURE_TARGETS: config->config.bindToTextureTargets = 0; if (value & __DRI_ATTRIB_TEXTURE_1D_BIT) config->config.bindToTextureTargets |= GLX_TEXTURE_1D_BIT_EXT; if (value & __DRI_ATTRIB_TEXTURE_2D_BIT) config->config.bindToTextureTargets |= GLX_TEXTURE_2D_BIT_EXT; if (value & __DRI_ATTRIB_TEXTURE_RECTANGLE_BIT) config->config.bindToTextureTargets |= GLX_TEXTURE_RECTANGLE_BIT_EXT; break; default: setScalar(&config->config, attrib, value); break; } } config->config.next = NULL; config->config.xRenderable = GL_TRUE; config->config.visualType = visualType; config->config.drawableType = GLX_WINDOW_BIT | GLX_PIXMAP_BIT; return &config->config; } __GLXconfig * glxConvertConfigs(const __DRIcoreExtension *core, const __DRIconfig **configs); __GLXconfig * glxConvertConfigs(const __DRIcoreExtension *core, const __DRIconfig **configs) { __GLXconfig head, *tail; int i; tail = &head; head.next = NULL; for (i = 0; configs[i]; i++) { tail->next = createModeFromConfig(core, configs[i], GLX_TRUE_COLOR); if (tail->next == NULL) break; tail = tail->next; } for (i = 0; configs[i]; i++) { tail->next = createModeFromConfig(core, configs[i], GLX_DIRECT_COLOR); if (tail->next == NULL) break; tail = tail->next; } return head.next; } static __GLXscreen * __glXDRIscreenProbe(ScreenPtr pScreen) { const char *driverName; __GLXDRIscreen *screen; char filename[128]; size_t buffer_size; ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; unsigned int sareaHandle; const __DRIextension **extensions; const __DRIconfig **driConfigs; int i; screen = xalloc(sizeof *screen); if (screen == NULL) return NULL; memset(screen, 0, sizeof *screen); if (!xf86LoaderCheckSymbol("DRI2Connect") || !DRI2Connect(pScreen, &screen->fd, &driverName, &sareaHandle)) { LogMessage(X_INFO, "AIGLX: Screen %d is not DRI2 capable\n", pScreen->myNum); return NULL; } screen->base.destroy = __glXDRIscreenDestroy; screen->base.createContext = __glXDRIscreenCreateContext; screen->base.createDrawable = __glXDRIscreenCreateDrawable; screen->base.swapInterval = __glXDRIdrawableSwapInterval; screen->base.pScreen = pScreen; __glXInitExtensionEnableBits(screen->glx_enable_bits); snprintf(filename, sizeof filename, "%s/%s_dri.so", dri_driver_path, driverName); screen->driver = dlopen(filename, RTLD_LAZY | RTLD_LOCAL); if (screen->driver == NULL) { LogMessage(X_ERROR, "AIGLX error: dlopen of %s failed (%s)\n", filename, dlerror()); goto handle_error; } extensions = dlsym(screen->driver, __DRI_DRIVER_EXTENSIONS); if (extensions == NULL) { LogMessage(X_ERROR, "AIGLX error: %s exports no extensions (%s)\n", driverName, dlerror()); goto handle_error; } for (i = 0; extensions[i]; i++) { if (strcmp(extensions[i]->name, __DRI_CORE) == 0 && extensions[i]->version >= __DRI_CORE_VERSION) { screen->core = (const __DRIcoreExtension *) extensions[i]; } } if (screen->core == NULL) { LogMessage(X_ERROR, "AIGLX error: %s exports no DRI extension\n", driverName); goto handle_error; } screen->driScreen = (*screen->core->createNewScreen)(pScreen->myNum, screen->fd, sareaHandle, loader_extensions, &driConfigs, screen); if (screen->driScreen == NULL) { LogMessage(X_ERROR, "AIGLX error: Calling driver entry point failed"); goto handle_error; } initializeExtensions(screen); screen->base.fbconfigs = glxConvertConfigs(screen->core, driConfigs); __glXScreenInit(&screen->base, pScreen); buffer_size = __glXGetExtensionString(screen->glx_enable_bits, NULL); if (buffer_size > 0) { if (screen->base.GLXextensions != NULL) { xfree(screen->base.GLXextensions); } screen->base.GLXextensions = xnfalloc(buffer_size); (void) __glXGetExtensionString(screen->glx_enable_bits, screen->base.GLXextensions); } screen->enterVT = pScrn->EnterVT; pScrn->EnterVT = glxDRIEnterVT; screen->leaveVT = pScrn->LeaveVT; pScrn->LeaveVT = glxDRILeaveVT; LogMessage(X_INFO, "AIGLX: Loaded and initialized %s\n", filename); return &screen->base; handle_error: if (screen->driver) dlclose(screen->driver); xfree(screen); LogMessage(X_ERROR, "AIGLX: reverting to software rendering\n"); return NULL; } __GLXprovider __glXDRI2Provider = { __glXDRIscreenProbe, "DRI2", NULL };