/* * Copyright (c) 2013, NVIDIA CORPORATION. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and/or associated documentation files (the * "Materials"), to deal in the Materials without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Materials, and to * permit persons to whom the Materials are furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * unaltered in all copies or substantial portions of the Materials. * Any additions, deletions, or changes to the original source files * must be clearly indicated in accompanying documentation. * * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. */ #include #include #include #include #include #include #include #include "libglxthread.h" #include "libglxabipriv.h" #include "libglxmapping.h" #include "libglxcurrent.h" #include "utils_misc.h" #include "trace.h" #include "GL/glxproto.h" #include "libglxgl.h" #include "glvnd_list.h" #include "app_error_check.h" #include "lkdhash.h" /* current version numbers */ #define GLX_MAJOR_VERSION 1 #define GLX_MINOR_VERSION 4 #define GLX_VERSION_STRING "1.4" /* * Older versions of glxproto.h contained a typo where "Attribs" was misspelled. * The typo was fixed in the xorgproto version of glxproto.h, breaking the API. * Work around that here. */ #if !defined(X_GLXCreateContextAttribsARB) && \ defined(X_GLXCreateContextAtrribsARB) #define X_GLXCreateContextAttribsARB X_GLXCreateContextAtrribsARB #endif static glvnd_mutex_t clientStringLock = GLVND_MUTEX_INITIALIZER; /** * This structure keeps track of a rendering context. * * It's used both to keep track of which vendor owns each context and for * whether a context is current to any thread. */ struct __GLXcontextInfoRec { GLXContext context; __GLXvendorInfo *vendor; int currentCount; Bool deleted; UT_hash_handle hh; }; static __GLXcontextInfo *glxContextHash = NULL; /** * The mutex used to protect the \c glxContextHash hash. Any thread must * take this mutex before it accesses the \c glxContextHash, or before it * modifies any field in a __GLXcontextInfo structure. * * Note that a \c __GLXcontextInfo struct will stay valid for as long as a context * is. That is, it's only freed when the context is deleted and no longer * current to any thread. * * Also note that the \c context and \c vendor values are never modified for * the life of the structure. Thus, it's safe to access them for the current * thread's current context without having to take the \c glxContextHashLock * mutex. */ static glvnd_mutex_t glxContextHashLock; /** * A list of current __GLXThreadState structures. This is used so that we can * clean up at process termination or after a fork. */ static struct glvnd_list currentThreadStateList; static glvnd_mutex_t currentThreadStateListMutex = GLVND_MUTEX_INITIALIZER; static __GLXThreadState *CreateThreadState(__GLXvendorInfo *vendor); static void DestroyThreadState(__GLXThreadState *threadState); /*! * Updates the current context. * * If the old context was flagged for deletion and is no longer current to any * thread, then it will also remove the context from the context hashtable. * * \note glxContextHashLock must be locked before calling this * function. * * \param[in] newCtxInfo The new context to make current, or \c NULL to just * release the current context. * \param[in] oldCtxInfo The previous current context, or \c NULL if no context * was current before. */ static void UpdateCurrentContext(__GLXcontextInfo *newCtxInfo, __GLXcontextInfo *oldCtxInfo); /** * Removes and frees an entry from the glxContextHash table. * * The caller must take the \c glxContextHashLock mutex before calling this * function. * * \param ctx The context to free. */ static void FreeContextInfo(__GLXcontextInfo *ctx); /** * Checks whether a rendering context should be deleted. * * If the context is marked for deletion, and is not current to any thread, * then it will remove and free the __GLXcontextInfo struct. */ static void CheckContextDeleted(__GLXcontextInfo *ctx); static void __glXSendError(Display *dpy, unsigned char errorCode, XID resourceID, unsigned char minorCode, Bool coreX11error); /*! * A common helper for GLX functions that dispatch based on a drawable. * * This function will call __glXThreadInitialize and then look up the vendor * for a drawable. * * If it can't find a vendor for the drawable, then it will call __glXSendError * to generate an error. * * Note that if the server doesn't support the x11glvnd extension, then this * will return the same vendor library whether or not the drawable is valid. * In that case, we'll just rely on the vendor library to report the error if * the drawable is not valid. * * \param dpy The display connection. * \param draw The drawable XID. * \param minorCode The minor opcode of the function being called. * \param errorCode The error code to report if the drawable is invalid. * \param coreX11error True if the error is a core X11 error code, or False if * it's a GLX error code. */ static __GLXvendorInfo *CommonDispatchDrawable(Display *dpy, GLXDrawable draw, unsigned char minorCode, unsigned char errorCode, Bool coreX11error) { __GLXvendorInfo *vendor = NULL; if (draw != None) { __glXThreadInitialize(); vendor = __glXVendorFromDrawable(dpy, draw); } if (vendor == NULL) { __glXSendError(dpy, errorCode, draw, minorCode, coreX11error); } return vendor; } static __GLXvendorInfo *CommonDispatchContext(Display *dpy, GLXContext context, unsigned char minorCode) { __GLXvendorInfo *vendor = NULL; if (context != NULL) { __glXThreadInitialize(); vendor = __glXVendorFromContext(context); } if (vendor == NULL) { __glXSendError(dpy, GLXBadContext, 0, minorCode, False); } return vendor; } static __GLXvendorInfo *CommonDispatchFBConfig(Display *dpy, GLXFBConfig config, unsigned char minorCode) { __GLXvendorInfo *vendor = NULL; if (config != NULL) { __glXThreadInitialize(); vendor = __glXVendorFromFBConfig(dpy, config); } if (vendor == NULL) { __glXSendError(dpy, GLXBadFBConfig, 0, minorCode, False); } return vendor; } PUBLIC XVisualInfo* glXChooseVisual(Display *dpy, int screen, int *attrib_list) { __GLXvendorInfo *vendor = __glXGetDynDispatch(dpy, screen); if (vendor != NULL) { return vendor->staticDispatch.chooseVisual(dpy, screen, attrib_list); } else { return NULL; } } PUBLIC void glXCopyContext(Display *dpy, GLXContext src, GLXContext dst, unsigned long mask) { /* * GLX requires that src and dst are on the same X screen, but the * application may have passed invalid input. Pick the screen * from one of the contexts, and then let that vendor's * implementation validate that both contexts are on the same * screen. */ __GLXvendorInfo *vendor = CommonDispatchContext(dpy, src, X_GLXCopyContext); if (vendor != NULL) { vendor->staticDispatch.copyContext(dpy, src, dst, mask); } } PUBLIC GLXContext glXCreateContext(Display *dpy, XVisualInfo *vis, GLXContext share_list, Bool direct) { __GLXvendorInfo *vendor = __glXGetDynDispatch(dpy, vis->screen); if (vendor != NULL) { GLXContext context = vendor->staticDispatch.createContext(dpy, vis, share_list, direct); if (__glXAddVendorContextMapping(dpy, context, vendor) != 0) { vendor->staticDispatch.destroyContext(dpy, context); context = NULL; } return context; } else { return NULL; } } PUBLIC GLXContext glXCreateNewContext(Display *dpy, GLXFBConfig config, int render_type, GLXContext share_list, Bool direct) { GLXContext context = NULL; __GLXvendorInfo *vendor = CommonDispatchFBConfig(dpy, config, X_GLXCreateNewContext); if (vendor != NULL) { context = vendor->staticDispatch.createNewContext(dpy, config, render_type, share_list, direct); if (__glXAddVendorContextMapping(dpy, context, vendor) != 0) { vendor->staticDispatch.destroyContext(dpy, context); context = NULL; } } return context; } static GLXContext glXCreateContextAttribsARB(Display *dpy, GLXFBConfig config, GLXContext share_list, Bool direct, const int *attrib_list) { GLXContext context = NULL; __GLXvendorInfo *vendor = NULL; if (attrib_list != NULL) { // See if the caller passed in a GLX_SCREEN attribute, and if so, use // that to select a vendor library. This is needed for // GLX_EXT_no_config_context, where we won't have a GLXFBConfig handle. int i; for (i=0; attrib_list[i] != None; i += 2) { if (attrib_list[i] == GLX_SCREEN) { int screen = attrib_list[i + 1]; vendor = __glXGetDynDispatch(dpy, screen); if (vendor == NULL) { __glXSendError(dpy, BadValue, 0, X_GLXCreateContextAttribsARB, True); return None; } } } } if (vendor == NULL) { // We didn't get a GLX_SCREEN attribute, so look at the config instead. vendor = CommonDispatchFBConfig(dpy, config, X_GLXCreateContextAttribsARB); } if (vendor != NULL && vendor->staticDispatch.createContextAttribsARB != NULL) { context = vendor->staticDispatch.createContextAttribsARB(dpy, config, share_list, direct, attrib_list); if (context != NULL) { if (__glXAddVendorContextMapping(dpy, context, vendor) != 0) { vendor->staticDispatch.destroyContext(dpy, context); context = NULL; } } } return context; } PUBLIC void glXDestroyContext(Display *dpy, GLXContext context) { __GLXvendorInfo *vendor; if (context == NULL) { // Some drivers will just return without generating an error if the app // passes NULL for a context, and unfortunately there are some broken // applications that depend on that behavior. glvndAppErrorCheckReportError("glXDestroyContext called with NULL for context\n"); return; } vendor = CommonDispatchContext(dpy, context, X_GLXDestroyContext); if (vendor != NULL) { __glXRemoveVendorContextMapping(dpy, context); vendor->staticDispatch.destroyContext(dpy, context); } } static Bool __glXIsDirect(Display *dpy, __GLXdisplayInfo *dpyInfo, GLXContextID context) { xGLXIsDirectReq *req; xGLXIsDirectReply reply; assert(dpyInfo->glxSupported); LockDisplay(dpy); GetReq(GLXIsDirect, req); req->reqType = dpyInfo->glxMajorOpcode; req->glxCode = X_GLXIsDirect; req->context = context; _XReply(dpy, (xReply *) &reply, 0, False); UnlockDisplay(dpy); SyncHandle(); return reply.isDirect; } /** * Finds the screen number for a context, using the context's XID. This * function sends the request directly, so it doesn't rely on any vendor * library. * * Adapted from Mesa's glXImportContextEXT implementation. */ static int __glXGetScreenForContextID(Display *dpy, __GLXdisplayInfo *dpyInfo, GLXContextID contextID) { xGLXQueryContextReply reply; int *propList; int majorVersion, minorVersion; int screen = -1; int i; assert(dpyInfo->glxSupported); // Check the version number so that we know which request to send. if (!glXQueryVersion(dpy, &majorVersion, &minorVersion)) { return -1; } /* Send the glXQueryContextInfoEXT request */ LockDisplay(dpy); if (majorVersion > 1 || minorVersion >= 3) { xGLXQueryContextReq *req; GetReq(GLXQueryContext, req); req->reqType = dpyInfo->glxMajorOpcode; req->glxCode = X_GLXQueryContext; req->context = contextID; } else { xGLXVendorPrivateReq *vpreq; xGLXQueryContextInfoEXTReq *req; GetReqExtra(GLXVendorPrivate, sz_xGLXQueryContextInfoEXTReq - sz_xGLXVendorPrivateReq, vpreq); req = (xGLXQueryContextInfoEXTReq *) vpreq; req->reqType = dpyInfo->glxMajorOpcode; req->glxCode = X_GLXVendorPrivateWithReply; req->vendorCode = X_GLXvop_QueryContextInfoEXT; req->context = contextID; } _XReply(dpy, (xReply *) &reply, 0, False); if (reply.n <= 0) { UnlockDisplay(dpy); SyncHandle(); return -1; } propList = malloc(reply.n * 8); if (propList == NULL) { UnlockDisplay(dpy); SyncHandle(); return -1; } _XRead(dpy, (char *) propList, reply.n * 8); UnlockDisplay(dpy); SyncHandle(); for (i=0; iglxSupported) { return NULL; } /* The GLX_EXT_import_context spec says: * * "If does not refer to a valid context, then a BadContext * error is generated; if refers to direct rendering * context then no error is generated but glXImportContextEXT returns * NULL." * * If contextID is None, generate BadContext on the client-side. Other * sorts of invalid contexts will be detected by the server in the * __glXIsDirect call. */ if (contextID == None) { __glXSendError(dpy, GLXBadContext, contextID, X_GLXIsDirect, False); return NULL; } if (__glXIsDirect(dpy, dpyInfo, contextID)) { return NULL; } // Find the screen number for the context. We can't rely on a vendor // library yet, so send the request manually. screen = __glXGetScreenForContextID(dpy, dpyInfo, contextID); if (screen < 0) { return NULL; } vendor = __glXLookupVendorByScreen(dpy, screen); if (vendor != NULL && vendor->staticDispatch.importContextEXT != NULL && vendor->staticDispatch.freeContextEXT) { GLXContext context = vendor->staticDispatch.importContextEXT(dpy, contextID); if (__glXAddVendorContextMapping(dpy, context, vendor) != 0) { vendor->staticDispatch.freeContextEXT(dpy, context); context = NULL; } return context; } else { return NULL; } } static void glXFreeContextEXT(Display *dpy, GLXContext context) { __GLXvendorInfo *vendor = NULL; __glXThreadInitialize(); vendor = __glXVendorFromContext(context); if (vendor != NULL && vendor->staticDispatch.freeContextEXT != NULL) { __glXRemoveVendorContextMapping(dpy, context); vendor->staticDispatch.freeContextEXT(dpy, context); } } PUBLIC GLXPixmap glXCreateGLXPixmap(Display *dpy, XVisualInfo *vis, Pixmap pixmap) { __GLXvendorInfo *vendor = __glXGetDynDispatch(dpy, vis->screen); if (vendor != NULL) { GLXPixmap pmap = vendor->staticDispatch.createGLXPixmap(dpy, vis, pixmap); if (__glXAddVendorDrawableMapping(dpy, pmap, vendor) != 0) { vendor->staticDispatch.destroyGLXPixmap(dpy, pmap); pmap = None; } return pmap; } else { return None; } } PUBLIC void glXDestroyGLXPixmap(Display *dpy, GLXPixmap pix) { __GLXvendorInfo *vendor = CommonDispatchDrawable(dpy, pix, X_GLXDestroyGLXPixmap, GLXBadPixmap, False); if (vendor != NULL) { __glXRemoveVendorDrawableMapping(dpy, pix); vendor->staticDispatch.destroyGLXPixmap(dpy, pix); } } PUBLIC int glXGetConfig(Display *dpy, XVisualInfo *vis, int attrib, int *value) { __GLXvendorInfo *vendor; __glXThreadInitialize(); if (!dpy || !vis || !value) { return GLX_BAD_VALUE; } vendor = __glXLookupVendorByScreen(dpy, vis->screen); if (vendor != NULL) { return vendor->staticDispatch.getConfig(dpy, vis, attrib, value); } else { return GLX_BAD_VALUE; } } PUBLIC GLXContext glXGetCurrentContext(void) { __glXThreadInitialize(); __GLXThreadState *threadState = __glXGetCurrentThreadState(); if (threadState != NULL) { // The current thread has a thread state pointer if and only if it has a // current context, and the currentContext pointer is assigned before // the threadState pointer is put into TLS, so it will never be NULL. assert(threadState->currentContext != NULL); return threadState->currentContext->context; } else { return NULL; } } PUBLIC GLXDrawable glXGetCurrentDrawable(void) { __glXThreadInitialize(); __GLXThreadState *threadState = __glXGetCurrentThreadState(); if (threadState != NULL) { return threadState->currentDraw; } else { return None; } } PUBLIC GLXDrawable glXGetCurrentReadDrawable(void) { __glXThreadInitialize(); __GLXThreadState *threadState = __glXGetCurrentThreadState(); if (threadState != NULL) { return threadState->currentRead; } else { return None; } } PUBLIC Display *glXGetCurrentDisplay(void) { __glXThreadInitialize(); __GLXThreadState *threadState = __glXGetCurrentThreadState(); if (threadState != NULL) { return threadState->currentDisplay; } else { return NULL; } } __GLXvendorInfo *__glXGetCurrentDynDispatch(void) { __glXThreadInitialize(); __GLXThreadState *threadState = __glXGetCurrentThreadState(); if (threadState != NULL) { return threadState->currentVendor; } else { return NULL; } } PUBLIC Bool glXIsDirect(Display *dpy, GLXContext context) { __GLXvendorInfo *vendor = CommonDispatchContext(dpy, context, X_GLXIsDirect); if (vendor != NULL) { return vendor->staticDispatch.isDirect(dpy, context); } else { return False; } } void __glXDisplayClosed(__GLXdisplayInfo *dpyInfo) { __GLXThreadState *threadState; threadState = __glXGetCurrentThreadState(); if (threadState != NULL && threadState->currentDisplay == dpyInfo->dpy) { // Clear out the current context, but don't call into the vendor // library or do anything that might require a valid display. __glDispatchLoseCurrent(); __glvndPthreadFuncs.mutex_lock(&glxContextHashLock); UpdateCurrentContext(NULL, threadState->currentContext); __glvndPthreadFuncs.mutex_unlock(&glxContextHashLock); DestroyThreadState(threadState); } __glvndPthreadFuncs.mutex_lock(¤tThreadStateListMutex); glvnd_list_for_each_entry(threadState, ¤tThreadStateList, entry) { /* * Stub out any references to this display in any other thread states. */ if (threadState->currentDisplay == dpyInfo->dpy) { threadState->currentDisplay = NULL; } } __glvndPthreadFuncs.mutex_unlock(¤tThreadStateListMutex); } static void ThreadDestroyed(__GLdispatchThreadState *threadState) { __GLXThreadState *glxState = (__GLXThreadState *) threadState; // Clear out the current context. __glvndPthreadFuncs.mutex_lock(&glxContextHashLock); UpdateCurrentContext(NULL, glxState->currentContext); __glvndPthreadFuncs.mutex_unlock(&glxContextHashLock); // Free the thread state struct. DestroyThreadState(glxState); } static __GLXThreadState *CreateThreadState(__GLXvendorInfo *vendor) { __GLXThreadState *threadState = calloc(1, sizeof(*threadState)); assert(threadState); threadState->glas.tag = GLDISPATCH_API_GLX; threadState->glas.threadDestroyedCallback = ThreadDestroyed; threadState->currentVendor = vendor; __glvndPthreadFuncs.mutex_lock(¤tThreadStateListMutex); glvnd_list_add(&threadState->entry, ¤tThreadStateList); __glvndPthreadFuncs.mutex_unlock(¤tThreadStateListMutex); return threadState; } static void DestroyThreadState(__GLXThreadState *threadState) { // Free the thread state struct. __glvndPthreadFuncs.mutex_lock(¤tThreadStateListMutex); glvnd_list_del(&threadState->entry); __glvndPthreadFuncs.mutex_unlock(¤tThreadStateListMutex); free(threadState); } /* * Notifies libglvnd that the given context has been marked for destruction * by glXDestroyContext(), and removes any context -> screen mappings if * necessary. */ void __glXRemoveVendorContextMapping(Display *dpy, GLXContext context) { __GLXcontextInfo *ctxInfo; __glvndPthreadFuncs.mutex_lock(&glxContextHashLock); HASH_FIND_PTR(glxContextHash, &context, ctxInfo); if (ctxInfo != NULL) { ctxInfo->deleted = True; CheckContextDeleted(ctxInfo); } __glvndPthreadFuncs.mutex_unlock(&glxContextHashLock); } int __glXAddVendorContextMapping(Display *dpy, GLXContext context, __GLXvendorInfo *vendor) { __GLXcontextInfo *ctxInfo; __glvndPthreadFuncs.mutex_lock(&glxContextHashLock); HASH_FIND_PTR(glxContextHash, &context, ctxInfo); if (ctxInfo == NULL) { ctxInfo = (__GLXcontextInfo *) malloc(sizeof(__GLXcontextInfo)); if (ctxInfo == NULL) { __glvndPthreadFuncs.mutex_unlock(&glxContextHashLock); return -1; } ctxInfo->context = context; ctxInfo->vendor = vendor; ctxInfo->currentCount = 0; ctxInfo->deleted = False; HASH_ADD_PTR(glxContextHash, context, ctxInfo); } else { if (ctxInfo->vendor != vendor) { __glvndPthreadFuncs.mutex_unlock(&glxContextHashLock); return -1; } } __glvndPthreadFuncs.mutex_unlock(&glxContextHashLock); return 0; } __GLXvendorInfo *__glXVendorFromContext(GLXContext context) { __GLXcontextInfo *ctxInfo; __GLXvendorInfo *vendor = NULL; __glvndPthreadFuncs.mutex_lock(&glxContextHashLock); HASH_FIND_PTR(glxContextHash, &context, ctxInfo); if (ctxInfo != NULL) { vendor = ctxInfo->vendor; } __glvndPthreadFuncs.mutex_unlock(&glxContextHashLock); return vendor; } static void FreeContextInfo(__GLXcontextInfo *ctx) { if (ctx != NULL) { HASH_DELETE(hh, glxContextHash, ctx); free(ctx); } } static void UpdateCurrentContext(__GLXcontextInfo *newCtxInfo, __GLXcontextInfo *oldCtxInfo) { if (newCtxInfo == oldCtxInfo) { return; } if (newCtxInfo != NULL) { newCtxInfo->currentCount++; } if (oldCtxInfo != NULL) { assert(oldCtxInfo->currentCount > 0); oldCtxInfo->currentCount--; CheckContextDeleted(oldCtxInfo); } } static void CheckContextDeleted(__GLXcontextInfo *ctx) { if (ctx->deleted && ctx->currentCount == 0) { FreeContextInfo(ctx); } } static void __glXSendError(Display *dpy, unsigned char errorCode, XID resourceID, unsigned char minorCode, Bool coreX11error) { __GLXdisplayInfo *dpyInfo = NULL; xError error; if (dpy == NULL) { return; } dpyInfo = __glXLookupDisplay(dpy); if (dpyInfo == NULL || !dpyInfo->glxSupported) { return; } LockDisplay(dpy); error.type = X_Error; error.errorCode = errorCode; error.sequenceNumber = dpy->request; error.resourceID = resourceID; error.minorCode = minorCode; error.majorCode = dpyInfo->glxMajorOpcode; if (!coreX11error) { error.errorCode += dpyInfo->glxFirstError; } _XError(dpy, &error); UnlockDisplay(dpy); } static void NotifyXError(Display *dpy, unsigned char errorCode, XID resourceID, unsigned char minorCode, Bool coreX11error, __GLXvendorInfo *vendor) { if (vendor != NULL && vendor->glxvc->notifyError != NULL) { Bool ret = vendor->glxvc->notifyError(dpy, errorCode, resourceID, minorCode, coreX11error); if (!ret) { return; } } __glXSendError(dpy, errorCode, resourceID, minorCode, coreX11error); } static Bool InternalLoseCurrent(void) { __GLXThreadState *threadState = __glXGetCurrentThreadState(); Bool ret; if (threadState == NULL) { return True; } ret = threadState->currentVendor->staticDispatch.makeCurrent(threadState->currentDisplay, None, NULL); if (!ret) { return False; } __glDispatchLoseCurrent(); // Remove the context from the current context map. UpdateCurrentContext(NULL, threadState->currentContext); DestroyThreadState(threadState); return True; } /** * Calls into the vendor library to set the current context, and then updates * the thread state fields to match. * * This function does *not* call into libGLdispatch, so it can only switch * to another context with the same vendor. * * If this function succeeds, then it will update the current display, context, * and drawables in \p threadState. * * If it fails, then it will leave \p threadState unmodified. It's up to the * vendor library to ensure that the old context is still current in that case. */ static Bool InternalMakeCurrentVendor( Display *dpy, GLXDrawable draw, GLXDrawable read, __GLXcontextInfo *ctxInfo, char callerOpcode, __GLXThreadState *threadState, __GLXvendorInfo *vendor) { Bool ret; assert(threadState->currentVendor == vendor); if (callerOpcode == X_GLXMakeCurrent && draw == read) { ret = vendor->staticDispatch.makeCurrent(dpy, draw, ctxInfo->context); } else { ret = vendor->staticDispatch.makeContextCurrent(dpy, draw, read, ctxInfo->context); } if (ret) { threadState->currentDisplay = dpy; threadState->currentDraw = draw; threadState->currentRead = read; threadState->currentContext = ctxInfo; } return ret; } /** * Makes a context current. This function handles both the vendor library and * libGLdispatch. * * There must not be a current thread state in libGLdispatch when this function * is called. * * If this function fails, then it will release the context and dispatch state * before returning. */ static Bool InternalMakeCurrentDispatch( Display *dpy, GLXDrawable draw, GLXDrawable read, __GLXcontextInfo *ctxInfo, char callerOpcode, __GLXvendorInfo *vendor) { __GLXThreadState *threadState; Bool ret; assert(__glXGetCurrentThreadState() == NULL); UpdateCurrentContext(ctxInfo, NULL); threadState = CreateThreadState(vendor); if (threadState == NULL) { UpdateCurrentContext(NULL, ctxInfo); return False; } ret = __glDispatchMakeCurrent( &threadState->glas, vendor->glDispatch, vendor->vendorID, vendor->patchCallbacks ); if (ret) { // Call into the vendor library. ret = InternalMakeCurrentVendor(dpy, draw, read, ctxInfo, callerOpcode, threadState, vendor); if (!ret) { __glDispatchLoseCurrent(); } } if (!ret) { DestroyThreadState(threadState); UpdateCurrentContext(NULL, ctxInfo); } return ret; } /** * A common function to handle glXMakeCurrent and glXMakeContextCurrent. */ static Bool CommonMakeCurrent(Display *dpy, GLXDrawable draw, GLXDrawable read, GLXContext context, char callerOpcode) { __GLXThreadState *threadState; __GLXvendorInfo *oldVendor, *newVendor; Display *oldDpy; GLXDrawable oldDraw, oldRead; __GLXcontextInfo *oldCtxInfo; __GLXcontextInfo *newCtxInfo; Bool ret; __glXThreadInitialize(); threadState = __glXGetCurrentThreadState(); if (threadState != NULL) { oldVendor = threadState->currentVendor; oldDpy = threadState->currentDisplay; oldDraw = threadState->currentDraw; oldRead = threadState->currentRead; oldCtxInfo = threadState->currentContext; assert(oldCtxInfo != NULL); if (dpy == oldDpy && context == oldCtxInfo->context && draw == oldDraw && read == oldRead) { // The current display, context, and drawables are the same, so just // return. return True; } } else { // We might have a non-GLX context current... __GLdispatchThreadState *glas = __glDispatchGetCurrentThreadState(); if (glas != NULL && glas->tag != GLDISPATCH_API_GLX) { NotifyXError(dpy, BadAccess, 0, callerOpcode, True, NULL); return False; } // We don't have a current context already. oldVendor = NULL; oldDpy = NULL; oldDraw = oldRead = None; oldCtxInfo = NULL; } /* * If is NULL and and are not None, or if or * are set to None and is not NULL, then a BadMatch error will * be generated. GLX 1.4 section 3.3.7 (p. 27). * * However, GLX_ARB_create_context specifies that GL 3.0+ contexts may be * made current without a default framebuffer, so the "or if..." part above * is ignored here. */ if (!context && (draw != None || read != None)) { // Notify the vendor library and send the X error. Since we don't have // a new context, instead notify the vendor library that owns the // current context (if there is one). NotifyXError(dpy, BadMatch, 0, callerOpcode, True, oldVendor); return False; } if (oldCtxInfo == NULL && context == NULL) { // If both the old and new contexts are NULL, then there's nothing to // do. Just return early. return True; } __glvndPthreadFuncs.mutex_lock(&glxContextHashLock); if (context != NULL) { // Look up the new display. This will ensure that we keep track of it // and get a callback when it's closed. if (__glXLookupDisplay(dpy) == NULL) { __glvndPthreadFuncs.mutex_unlock(&glxContextHashLock); return False; } HASH_FIND_PTR(glxContextHash, &context, newCtxInfo); if (newCtxInfo == NULL) { __glvndPthreadFuncs.mutex_unlock(&glxContextHashLock); /* * We can run into this corner case if a GLX client calls * glXDestroyContext() on a current context, loses current to this * context (causing it to be freed), then tries to make current to the * context again. This is incorrect application behavior, but we should * attempt to handle this failure gracefully. */ NotifyXError(dpy, GLXBadContext, 0, callerOpcode, False, oldVendor); return False; } newVendor = newCtxInfo->vendor; assert(newVendor != NULL); } else { newCtxInfo = NULL; newVendor = NULL; } if (oldVendor == newVendor) { assert(threadState != NULL); /* * We're switching between two contexts that use the same vendor. That * means the dispatch table is also the same, which is the only thing * that libGLdispatch cares about. Call into the vendor library to * switch contexts, but don't call into libGLdispatch. */ ret = InternalMakeCurrentVendor(dpy, draw, read, newCtxInfo, callerOpcode, threadState, newVendor); if (ret) { UpdateCurrentContext(newCtxInfo, oldCtxInfo); } } else if (newVendor == NULL) { /* * We have a current context and we're releasing it. */ assert(context == NULL); ret = InternalLoseCurrent(); } else if (oldVendor == NULL) { /* * We don't have a current context, so we only need to make the new one * current. */ ret = InternalMakeCurrentDispatch(dpy, draw, read, newCtxInfo, callerOpcode, newVendor); } else { /* * We're switching between contexts with different vendors. * * This gets tricky because we have to call into both vendor libraries * and libGLdispatch. Any of those can fail, and if it does, then we * have to make sure libGLX, libGLdispatch, and the vendor libraries * all agree on what the current context is. * * To do that, we'll first release the current context, and then make * the new context current. */ // First, check to see if calling InternalLoseCurrent is going to // destroy the old context. Bool canRestoreOldContext = True; if (oldCtxInfo->deleted && oldCtxInfo->currentCount == 1) { canRestoreOldContext = False; } ret = InternalLoseCurrent(); if (ret) { ret = InternalMakeCurrentDispatch(dpy, draw, read, newCtxInfo, callerOpcode, newVendor); if (!ret && canRestoreOldContext) { /* * Try to restore the old context. Note that this can fail if * the old context was marked for deletion. If that happens, * then we'll end up with no current context instead, but we * should at least still be in a consistent state. */ InternalMakeCurrentDispatch(oldDpy, oldDraw, oldRead, oldCtxInfo, callerOpcode, oldVendor); } } } __glvndPthreadFuncs.mutex_unlock(&glxContextHashLock); return ret; } PUBLIC Bool glXMakeCurrent(Display *dpy, GLXDrawable drawable, GLXContext context) { return CommonMakeCurrent(dpy, drawable, drawable, context, X_GLXMakeCurrent); } PUBLIC Bool glXMakeContextCurrent(Display *dpy, GLXDrawable draw, GLXDrawable read, GLXContext context) { return CommonMakeCurrent(dpy, draw, read, context, X_GLXMakeContextCurrent); } PUBLIC Bool glXQueryExtension(Display *dpy, int *error_base, int *event_base) { __glXThreadInitialize(); /* * There isn't enough information to dispatch to a vendor's * implementation, so handle the request here. */ int major, event, error; Bool ret = XQueryExtension(dpy, "GLX", &major, &event, &error); if (ret) { if (error_base) { *error_base = error; } if (event_base) { *event_base = event; } } return ret; } PUBLIC Bool glXQueryVersion(Display *dpy, int *major, int *minor) { __glXThreadInitialize(); /* * There isn't enough information to dispatch to a vendor's * implementation, so handle the request here. * * Adapted from mesa's * * gallium/state_trackers/egl/x11/glxinit.c:QueryVersion() * * TODO: Mesa's GLX state tracker uses xcb-glx rather than Xlib to perform * the query. Should we do the same here? */ xGLXQueryVersionReq *req; xGLXQueryVersionReply reply; __GLXdisplayInfo *dpyInfo = NULL; Bool ret; dpyInfo = __glXLookupDisplay(dpy); if (dpyInfo == NULL || !dpyInfo->glxSupported) { return False; } LockDisplay(dpy); GetReq(GLXQueryVersion, req); req->reqType = dpyInfo->glxMajorOpcode; req->glxCode = X_GLXQueryVersion; req->majorVersion = GLX_MAJOR_VERSION; req->minorVersion = GLX_MINOR_VERSION; ret = _XReply(dpy, (xReply *)&reply, 0, False); UnlockDisplay(dpy); SyncHandle(); if (!ret) { return False; } if (reply.majorVersion != GLX_MAJOR_VERSION) { /* Server does not support same major as client */ return False; } if (major) { *major = reply.majorVersion; } if (minor) { *minor = reply.minorVersion; } return True; } PUBLIC void glXSwapBuffers(Display *dpy, GLXDrawable drawable) { __GLXvendorInfo *vendor = CommonDispatchDrawable(dpy, drawable, X_GLXSwapBuffers, GLXBadDrawable, False); if (vendor != NULL) { vendor->staticDispatch.swapBuffers(dpy, drawable); } } PUBLIC void glXUseXFont(Font font, int first, int count, int list_base) { __GLXvendorInfo *vendor = __glXGetCurrentDynDispatch(); if (vendor != NULL) { vendor->staticDispatch.useXFont(font, first, count, list_base); } } PUBLIC void glXWaitGL(void) { __GLXvendorInfo *vendor = __glXGetCurrentDynDispatch(); if (vendor != NULL) { vendor->staticDispatch.waitGL(); } } PUBLIC void glXWaitX(void) { __GLXvendorInfo *vendor = __glXGetCurrentDynDispatch(); if (vendor != NULL) { vendor->staticDispatch.waitX(); } } /** * Queries a client string for each screen in a display. * * The returned array will have one element for each screen. The caller must * free the array by calling free. * * \param dpy The display connection. * \param name The string to query (GLX_VENDOR, GLX_VERSION, or GLX_EXTENSION). * \return An array of strings, or NULL on error. */ static const char **GetVendorClientStrings(Display *dpy, int name) { int num_screens = XScreenCount(dpy); const char **result = malloc(num_screens * sizeof(const char *)); int screen; if (result == NULL) { return NULL; } for (screen = 0; screen < num_screens; screen++) { __GLXvendorInfo *vendor = __glXLookupVendorByScreen(dpy, screen); if (vendor != NULL) { result[screen] = vendor->staticDispatch.getClientString(dpy, name); } else { result[screen] = NULL; } if (result[screen] == NULL) { free(result); return NULL; } } return result; } /*! * Parses the version string that you'd get from calling glXGetClientString * with GLX_VERSION. * * \param version The version string. * \param[out] major The major version number. * \param[out] minor The minor version number. * \param[out] vendor Returns a pointer to the vendor-specific part of the * string, or \c NULL if it there isn't any vendor-specific string. * \return Zero on success, or -1 if \c version doesn't match the correct * format. */ static int ParseClientVersionString(const char *version, int *major, int *minor, const char **vendor) { int count; const char *ptr; count = sscanf(version, "%d.%d", major, minor); if (count != 2) { return -1; } // The vendor-specific info should be after the first space character. *vendor = NULL; ptr = strchr(version, ' '); if (ptr != NULL) { while (*ptr == ' ') { ptr++; } if (*ptr != '\0') { *vendor = ptr; } } return 0; } /** * Merges two GLX_VERSION strings. * * The merged string will specify the higher version number of \p currentString * and \p newString, up to the version specified by \c GLX_MAJOR_VERSION and * \c GLX_MINOR_VERSION. * * \param currentString The current string, which must have been allocated with malloc. * \param newString The version string to merge. * \return A new version string. */ static char *MergeVersionStrings(char *currentString, const char *newString) { int major, minor; const char *vendorInfo; int newMajor, newMinor; const char *newVendorInfo; char *buf; int ret; if (ParseClientVersionString(currentString, &major, &minor, &vendorInfo) != 0) { return currentString; } if (ParseClientVersionString(newString, &newMajor, &newMinor, &newVendorInfo) != 0) { return currentString; } // Report the highest version number of any vendor library, but no higher // than what this version of libglvnd supports. if (newMajor > major || (newMajor == major && newMinor > minor)) { major = newMajor; minor = newMinor; } if (major > GLX_MAJOR_VERSION || (major == GLX_MAJOR_VERSION && minor > GLX_MINOR_VERSION)) { major = GLX_MAJOR_VERSION; minor = GLX_MINOR_VERSION; } if (vendorInfo != NULL && newVendorInfo != NULL) { ret = glvnd_asprintf(&buf, "%d.%d %s, %s", major, minor, vendorInfo, newVendorInfo); } else if (vendorInfo != NULL || newVendorInfo != NULL) { const char *info = (vendorInfo != NULL ? vendorInfo : newVendorInfo); ret = glvnd_asprintf(&buf, "%d.%d %s", major, minor, info); } else { ret = glvnd_asprintf(&buf, "%d.%d", major, minor); } free(currentString); if (ret >= 0) { return buf; } else { return NULL; } } static const char *GetClientStringNoVendor(int name) { switch (name) { case GLX_VENDOR: return "libglvnd (no display specified)"; case GLX_VERSION: return GLX_VERSION_STRING " (no display specified)"; case GLX_EXTENSIONS: return ""; default: return NULL; } } PUBLIC const char *glXGetClientString(Display *dpy, int name) { __glXThreadInitialize(); __GLXdisplayInfo *dpyInfo = NULL; int num_screens; int screen; int index = name - 1; const char **vendorStrings = NULL; if (dpy == NULL) { return GetClientStringNoVendor(name); } num_screens = XScreenCount(dpy); if (num_screens == 1) { // There's only one screen, so we don't have to mess around with // merging the strings from multiple vendors. __GLXvendorInfo *vendor = __glXLookupVendorByScreen(dpy, 0); if (vendor != NULL) { return vendor->staticDispatch.getClientString(dpy, name); } else { return NULL; } } if (name != GLX_VENDOR && name != GLX_VERSION && name != GLX_EXTENSIONS) { return NULL; } dpyInfo = __glXLookupDisplay(dpy); if (dpyInfo == NULL) { return NULL; } __glvndPthreadFuncs.mutex_lock(&clientStringLock); if (dpyInfo->clientStrings[index] != NULL) { goto done; } vendorStrings = GetVendorClientStrings(dpy, name); if (vendorStrings == NULL) { goto done; } dpyInfo->clientStrings[index] = strdup(vendorStrings[0]); if (dpyInfo->clientStrings[index] == NULL) { goto done; } for (screen = 1; screen < num_screens; screen++) { if (name == GLX_VENDOR) { char *newBuf; if (glvnd_asprintf(&newBuf, "%s, %s", dpyInfo->clientStrings[index], vendorStrings[screen]) < 0) { newBuf = NULL; } free(dpyInfo->clientStrings[index]); dpyInfo->clientStrings[index] = newBuf; } else if (name == GLX_VERSION) { dpyInfo->clientStrings[index] = MergeVersionStrings(dpyInfo->clientStrings[index], vendorStrings[screen]); } else if (name == GLX_EXTENSIONS) { dpyInfo->clientStrings[index] = UnionExtensionStrings(dpyInfo->clientStrings[index], vendorStrings[screen]); } else { assert(!"Can't happen: Invalid string name"); free(dpyInfo->clientStrings[index]); dpyInfo->clientStrings[index] = NULL; } if (dpyInfo->clientStrings[index] == NULL) { goto done; } } done: __glvndPthreadFuncs.mutex_unlock(&clientStringLock); if (vendorStrings != NULL) { free(vendorStrings); } return dpyInfo->clientStrings[index]; } PUBLIC const char *glXQueryServerString(Display *dpy, int screen, int name) { __GLXvendorInfo *vendor = __glXGetDynDispatch(dpy, screen); if (vendor != NULL) { return vendor->staticDispatch.queryServerString(dpy, screen, name); } else { return NULL; } } PUBLIC const char *glXQueryExtensionsString(Display *dpy, int screen) { __GLXvendorInfo *vendor = __glXGetDynDispatch(dpy, screen); if (vendor != NULL) { return vendor->staticDispatch.queryExtensionsString(dpy, screen); } else { return NULL; } } PUBLIC GLXFBConfig *glXChooseFBConfig(Display *dpy, int screen, const int *attrib_list, int *nelements) { __GLXvendorInfo *vendor = __glXGetDynDispatch(dpy, screen); if (vendor != NULL) { GLXFBConfig *fbconfigs = vendor->staticDispatch.chooseFBConfig(dpy, screen, attrib_list, nelements); if (fbconfigs != NULL) { int i; Bool success = True; for (i = 0; i < *nelements; i++) { if (__glXAddVendorFBConfigMapping(dpy, fbconfigs[i], vendor) != 0) { success = False; break; } } if (!success) { XFree(fbconfigs); fbconfigs = NULL; *nelements = 0; } } return fbconfigs; } else { return NULL; } } PUBLIC GLXPbuffer glXCreatePbuffer(Display *dpy, GLXFBConfig config, const int *attrib_list) { GLXPbuffer pbuffer = None; __GLXvendorInfo *vendor = CommonDispatchFBConfig(dpy, config, X_GLXCreatePbuffer); if (vendor != NULL) { pbuffer = vendor->staticDispatch.createPbuffer(dpy, config, attrib_list); if (__glXAddVendorDrawableMapping(dpy, pbuffer, vendor) != 0) { vendor->staticDispatch.destroyPbuffer(dpy, pbuffer); pbuffer = None; } } return pbuffer; } PUBLIC GLXPixmap glXCreatePixmap(Display *dpy, GLXFBConfig config, Pixmap pixmap, const int *attrib_list) { GLXPixmap glxPixmap = None; __GLXvendorInfo *vendor = CommonDispatchFBConfig(dpy, config, X_GLXCreatePixmap); if (vendor != NULL) { glxPixmap = vendor->staticDispatch.createPixmap(dpy, config, pixmap, attrib_list); if (__glXAddVendorDrawableMapping(dpy, glxPixmap, vendor) != 0) { vendor->staticDispatch.destroyGLXPixmap(dpy, glxPixmap); glxPixmap = None; } } return glxPixmap; } PUBLIC GLXWindow glXCreateWindow(Display *dpy, GLXFBConfig config, Window win, const int *attrib_list) { GLXWindow glxWindow = None; __GLXvendorInfo *vendor = CommonDispatchFBConfig(dpy, config, X_GLXCreateWindow); if (vendor != NULL) { glxWindow = vendor->staticDispatch.createWindow(dpy, config, win, attrib_list); if (__glXAddVendorDrawableMapping(dpy, glxWindow, vendor) != 0) { vendor->staticDispatch.destroyWindow(dpy, glxWindow); glxWindow = None; } } return glxWindow; } PUBLIC void glXDestroyPbuffer(Display *dpy, GLXPbuffer pbuf) { __GLXvendorInfo *vendor = CommonDispatchDrawable(dpy, pbuf, X_GLXDestroyPbuffer, GLXBadPbuffer, False); if (vendor != NULL) { __glXRemoveVendorDrawableMapping(dpy, pbuf); vendor->staticDispatch.destroyPbuffer(dpy, pbuf); } } PUBLIC void glXDestroyPixmap(Display *dpy, GLXPixmap pixmap) { __GLXvendorInfo *vendor = CommonDispatchDrawable(dpy, pixmap, X_GLXDestroyPixmap, GLXBadPixmap, False); if (vendor != NULL) { __glXRemoveVendorDrawableMapping(dpy, pixmap); vendor->staticDispatch.destroyPixmap(dpy, pixmap); } } PUBLIC void glXDestroyWindow(Display *dpy, GLXWindow win) { __GLXvendorInfo *vendor = CommonDispatchDrawable(dpy, win, X_GLXDestroyWindow, GLXBadWindow, False); if (vendor != NULL) { __glXRemoveVendorDrawableMapping(dpy, win); vendor->staticDispatch.destroyWindow(dpy, win); } } PUBLIC int glXGetFBConfigAttrib(Display *dpy, GLXFBConfig config, int attribute, int *value) { __GLXvendorInfo *vendor = CommonDispatchFBConfig(dpy, config, X_GLXGetFBConfigs); if (vendor != NULL) { return vendor->staticDispatch.getFBConfigAttrib(dpy, config, attribute, value); } else { return GLX_BAD_VISUAL; } } PUBLIC GLXFBConfig *glXGetFBConfigs(Display *dpy, int screen, int *nelements) { __GLXvendorInfo *vendor = __glXGetDynDispatch(dpy, screen); if (vendor != NULL) { GLXFBConfig *fbconfigs = vendor->staticDispatch.getFBConfigs(dpy, screen, nelements); if (fbconfigs != NULL) { int i; Bool success = True; for (i = 0; i < *nelements; i++) { if (__glXAddVendorFBConfigMapping(dpy, fbconfigs[i], vendor) != 0) { success = False; break; } } if (!success) { XFree(fbconfigs); fbconfigs = NULL; *nelements = 0; } } return fbconfigs; } else { return NULL; } } PUBLIC void glXGetSelectedEvent(Display *dpy, GLXDrawable draw, unsigned long *event_mask) { // glXGetSelectedEvent uses the glXGetDrawableAttributes protocol. __GLXvendorInfo *vendor = CommonDispatchDrawable(dpy, draw, X_GLXGetDrawableAttributes, GLXBadDrawable, False); if (vendor != NULL) { vendor->staticDispatch.getSelectedEvent(dpy, draw, event_mask); } } PUBLIC XVisualInfo *glXGetVisualFromFBConfig(Display *dpy, GLXFBConfig config) { __GLXvendorInfo *vendor = CommonDispatchFBConfig(dpy, config, X_GLXGetFBConfigs); if (vendor != NULL) { return vendor->staticDispatch.getVisualFromFBConfig(dpy, config); } else { return NULL; } } PUBLIC int glXQueryContext(Display *dpy, GLXContext context, int attribute, int *value) { __GLXvendorInfo *vendor = CommonDispatchContext(dpy, context, X_GLXQueryContext); if (vendor != NULL) { return vendor->staticDispatch.queryContext(dpy, context, attribute, value); } else { return GLX_BAD_CONTEXT; } } PUBLIC void glXQueryDrawable(Display *dpy, GLXDrawable draw, int attribute, unsigned int *value) { __GLXvendorInfo *vendor = CommonDispatchDrawable(dpy, draw, X_GLXGetDrawableAttributes, GLXBadDrawable, False); if (vendor != NULL) { vendor->staticDispatch.queryDrawable(dpy, draw, attribute, value); } } PUBLIC void glXSelectEvent(Display *dpy, GLXDrawable draw, unsigned long event_mask) { __GLXvendorInfo *vendor = CommonDispatchDrawable(dpy, draw, X_GLXChangeDrawableAttributes, GLXBadDrawable, False); if (vendor != NULL) { vendor->staticDispatch.selectEvent(dpy, draw, event_mask); } } const __GLXlocalDispatchFunction LOCAL_GLX_DISPATCH_FUNCTIONS[] = { #define LOCAL_FUNC_TABLE_ENTRY(func) \ { #func, (__GLXextFuncPtr)(func) }, LOCAL_FUNC_TABLE_ENTRY(glXChooseFBConfig) LOCAL_FUNC_TABLE_ENTRY(glXChooseVisual) LOCAL_FUNC_TABLE_ENTRY(glXCopyContext) LOCAL_FUNC_TABLE_ENTRY(glXCreateContext) LOCAL_FUNC_TABLE_ENTRY(glXCreateGLXPixmap) LOCAL_FUNC_TABLE_ENTRY(glXCreateNewContext) LOCAL_FUNC_TABLE_ENTRY(glXCreatePbuffer) LOCAL_FUNC_TABLE_ENTRY(glXCreatePixmap) LOCAL_FUNC_TABLE_ENTRY(glXCreateWindow) LOCAL_FUNC_TABLE_ENTRY(glXDestroyContext) LOCAL_FUNC_TABLE_ENTRY(glXDestroyGLXPixmap) LOCAL_FUNC_TABLE_ENTRY(glXDestroyPbuffer) LOCAL_FUNC_TABLE_ENTRY(glXDestroyPixmap) LOCAL_FUNC_TABLE_ENTRY(glXDestroyWindow) LOCAL_FUNC_TABLE_ENTRY(glXGetClientString) LOCAL_FUNC_TABLE_ENTRY(glXGetConfig) LOCAL_FUNC_TABLE_ENTRY(glXGetCurrentContext) LOCAL_FUNC_TABLE_ENTRY(glXGetCurrentDisplay) LOCAL_FUNC_TABLE_ENTRY(glXGetCurrentDrawable) LOCAL_FUNC_TABLE_ENTRY(glXGetCurrentReadDrawable) LOCAL_FUNC_TABLE_ENTRY(glXGetFBConfigAttrib) LOCAL_FUNC_TABLE_ENTRY(glXGetFBConfigs) LOCAL_FUNC_TABLE_ENTRY(glXGetProcAddress) LOCAL_FUNC_TABLE_ENTRY(glXGetProcAddressARB) LOCAL_FUNC_TABLE_ENTRY(glXGetSelectedEvent) LOCAL_FUNC_TABLE_ENTRY(glXGetVisualFromFBConfig) LOCAL_FUNC_TABLE_ENTRY(glXIsDirect) LOCAL_FUNC_TABLE_ENTRY(glXMakeContextCurrent) LOCAL_FUNC_TABLE_ENTRY(glXMakeCurrent) LOCAL_FUNC_TABLE_ENTRY(glXQueryContext) LOCAL_FUNC_TABLE_ENTRY(glXQueryDrawable) LOCAL_FUNC_TABLE_ENTRY(glXQueryExtension) LOCAL_FUNC_TABLE_ENTRY(glXQueryExtensionsString) LOCAL_FUNC_TABLE_ENTRY(glXQueryServerString) LOCAL_FUNC_TABLE_ENTRY(glXQueryVersion) LOCAL_FUNC_TABLE_ENTRY(glXSelectEvent) LOCAL_FUNC_TABLE_ENTRY(glXSwapBuffers) LOCAL_FUNC_TABLE_ENTRY(glXUseXFont) LOCAL_FUNC_TABLE_ENTRY(glXWaitGL) LOCAL_FUNC_TABLE_ENTRY(glXWaitX) LOCAL_FUNC_TABLE_ENTRY(glXImportContextEXT) LOCAL_FUNC_TABLE_ENTRY(glXFreeContextEXT) LOCAL_FUNC_TABLE_ENTRY(glXCreateContextAttribsARB) #undef LOCAL_FUNC_TABLE_ENTRY { NULL, NULL } }; typedef struct { GLubyte *procName; __GLXextFuncPtr addr; UT_hash_handle hh; } __GLXprocAddressHash; static DEFINE_INITIALIZED_LKDHASH(__GLXprocAddressHash, __glXProcAddressHash); /* * This function is called externally by the libGL wrapper library to * retrieve libGLX entrypoints. */ static __GLXextFuncPtr __glXGetCachedProcAddress(const GLubyte *procName) { /* * If this is the first time GetProcAddress has been called, * initialize the hash table with locally-exported functions. */ __GLXprocAddressHash *pEntry = NULL; LKDHASH_RDLOCK(__glXProcAddressHash); HASH_FIND(hh, _LH(__glXProcAddressHash), procName, strlen((const char *)procName), pEntry); LKDHASH_UNLOCK(__glXProcAddressHash); if (pEntry) { return pEntry->addr; } return NULL; } static void cacheProcAddress(const GLubyte *procName, __GLXextFuncPtr addr) { size_t nameLen = strlen((const char *) procName); __GLXprocAddressHash *pEntry; LKDHASH_WRLOCK(__glXProcAddressHash); HASH_FIND(hh, _LH(__glXProcAddressHash), procName, nameLen, pEntry); if (pEntry == NULL) { pEntry = malloc(sizeof(*pEntry) + nameLen + 1); if (pEntry != NULL) { pEntry->procName = (GLubyte *) (pEntry + 1); memcpy(pEntry->procName, procName, nameLen + 1); pEntry->addr = addr; HASH_ADD_KEYPTR(hh, _LH(__glXProcAddressHash), pEntry->procName, nameLen, pEntry); } } else { assert(pEntry->addr == addr); } LKDHASH_UNLOCK(__glXProcAddressHash); } PUBLIC __GLXextFuncPtr glXGetProcAddressARB(const GLubyte *procName) { __glXThreadInitialize(); return glXGetProcAddress(procName); } PUBLIC __GLXextFuncPtr glXGetProcAddress(const GLubyte *procName) { __GLXextFuncPtr addr = NULL; __glXThreadInitialize(); /* * Easy case: First check if we already know this address from * a previous GetProcAddress() call or by virtue of being a function * exported by libGLX. */ addr = __glXGetCachedProcAddress(procName); if (addr) { return addr; } if (procName[0] == 'g' && procName[1] == 'l' && procName[2] == 'X') { // This looks like a GLX function, so try to find a GLX dispatch stub. addr = __glXGetGLXDispatchAddress(procName); } else { addr = __glDispatchGetProcAddress((const char *) procName); } /* Store the resulting proc address. */ if (addr) { cacheProcAddress(procName, addr); } return addr; } PUBLIC __GLXextFuncPtr __glXGLLoadGLXFunction(const char *name, __GLXextFuncPtr *ptr, glvnd_mutex_t *mutex) { __GLXextFuncPtr func; __glvndPthreadFuncs.mutex_lock(mutex); func = *ptr; if (func == NULL) { func = glXGetProcAddress((const GLubyte *) name); *ptr = func; } __glvndPthreadFuncs.mutex_unlock(mutex); return func; } int AtomicIncrement(int volatile *val) { #if defined(HAVE_SYNC_INTRINSICS) return __sync_add_and_fetch(val, 1); #elif defined(USE_X86_ASM) || defined(USE_X86_64_ASM) int result; int delta = 1; __asm __volatile__ ("lock; xaddl %0, %1" : "=r" (result), "=m" (*val) : "0" (delta), "m" (*val)); return result + delta; #else #error "Not implemented" #endif } int AtomicSwap(int volatile *val, int newVal) { #if defined(HAVE_SYNC_INTRINSICS) return __sync_lock_test_and_set(val, newVal); #elif defined(USE_X86_ASM) || defined(USE_X86_64_ASM) int result; __asm __volatile__ ("xchgl %0, %1" : "=r" (result), "=m" (*val) : "0" (newVal), "m" (*val)); return result; #else #error "Not implemented" #endif } int AtomicCompareAndSwap(int volatile *val, int oldVal, int newVal) { #if defined(HAVE_SYNC_INTRINSICS) return __sync_val_compare_and_swap(val, oldVal, newVal); #elif defined(USE_X86_ASM) || defined(USE_X86_64_ASM) int result; __asm __volatile__ ("lock; cmpxchgl %2, %1" : "=a" (result), "=m" (*val) : "r" (newVal), "m" (*val), "0" (oldVal)); return result; #else #error "Not implemented" #endif } int AtomicDecrementClampAtZero(int volatile *val) { int oldVal, newVal; oldVal = *val; newVal = oldVal; do { if (oldVal <= 0) { assert(oldVal == 0); } else { newVal = oldVal - 1; oldVal = AtomicCompareAndSwap(val, oldVal, newVal); } } while ((oldVal > 0) && (newVal != oldVal - 1)); return newVal; } static void __glXResetOnFork(void); /*! * Checks to see if a fork occurred since the last GLX entrypoint was called, * and performs recovery if needed. */ static void CheckFork(void) { volatile static int g_threadsInCheck = 0; volatile static int g_lastPid = -1; int lastPid; int pid = getpid(); AtomicIncrement(&g_threadsInCheck); lastPid = AtomicSwap(&g_lastPid, pid); if ((lastPid != -1) && (lastPid != pid)) { DBG_PRINTF(0, "Fork detected\n"); __glXResetOnFork(); // Force g_threadsInCheck to 0 to unblock other threads waiting here. g_threadsInCheck = 0; } else { AtomicDecrementClampAtZero(&g_threadsInCheck); while (g_threadsInCheck > 0) { // Wait for other threads to finish checking for a fork. // // If a fork happens while g_threadsInCheck > 0 the _first_ thread // to enter __glXThreadInitialize() will see the fork, handle it, and force // g_threadsInCheck to 0, unblocking any other threads stuck here. sched_yield(); } } } /*! * Handles any common tasks that need to occur at the beginning of any GLX * entrypoint. */ void __glXThreadInitialize(void) { CheckFork(); __glDispatchCheckMultithreaded(); } static void __glXAPITeardown(Bool doReset) { __GLXThreadState *threadState, *threadStateTemp; __GLXcontextInfo *currContext, *currContextTemp; glvnd_list_for_each_entry_safe(threadState, threadStateTemp, ¤tThreadStateList, entry) { glvnd_list_del(&threadState->entry); free(threadState); } if (doReset) { /* * XXX: We should be able to get away with just resetting the proc address * hash lock, and not throwing away cached addresses. */ __glvndPthreadFuncs.rwlock_init(&__glXProcAddressHash.lock, NULL); __glvndPthreadFuncs.mutex_init(¤tThreadStateListMutex, NULL); HASH_ITER(hh, glxContextHash, currContext, currContextTemp) { currContext->currentCount = 0; CheckContextDeleted(currContext); } } else { LKDHASH_TEARDOWN(__GLXprocAddressHash, __glXProcAddressHash, NULL, NULL, False); /* * It's possible that another thread could be blocked in a * glXMakeCurrent call here, especially if an Xlib I/O error occurred. * In that case, the other thead will be holding the context hash lock, * so we'd deadlock if we tried to wait for it here. Instead, clean up * if the lock is available, but don't try to wait if it isn't. */ if (__glvndPthreadFuncs.mutex_trylock(&glxContextHashLock) == 0) { HASH_ITER(hh, glxContextHash, currContext, currContextTemp) { FreeContextInfo(currContext); } assert(glxContextHash == NULL); __glvndPthreadFuncs.mutex_unlock(&glxContextHashLock); } } } static void __glXResetOnFork(void) { /* Reset GLdispatch */ __glDispatchReset(); /* Reset all GLX API state */ __glXAPITeardown(True); /* Reset all mapping state */ __glXMappingTeardown(True); } PUBLIC const __glXGLCoreFunctions __GLXGL_CORE_FUNCTIONS = { glXChooseFBConfig, glXChooseVisual, glXCopyContext, glXCreateContext, glXCreateGLXPixmap, glXCreateNewContext, glXCreatePbuffer, glXCreatePixmap, glXCreateWindow, glXDestroyContext, glXDestroyGLXPixmap, glXDestroyPbuffer, glXDestroyPixmap, glXDestroyWindow, glXGetClientString, glXGetConfig, glXGetCurrentContext, glXGetCurrentDrawable, glXGetCurrentReadDrawable, glXGetFBConfigAttrib, glXGetFBConfigs, glXGetProcAddress, glXGetProcAddressARB, glXGetSelectedEvent, glXGetVisualFromFBConfig, glXIsDirect, glXMakeContextCurrent, glXMakeCurrent, glXQueryContext, glXQueryDrawable, glXQueryExtension, glXQueryExtensionsString, glXQueryServerString, glXQueryVersion, glXSelectEvent, glXSwapBuffers, glXUseXFont, glXWaitGL, glXWaitX, }; #if defined(USE_ATTRIBUTE_CONSTRUCTOR) void __attribute__ ((constructor)) __glXInit(void) #else void _init(void) #endif { glvnd_mutexattr_t mutexAttribs; if (__glDispatchGetABIVersion() != GLDISPATCH_ABI_VERSION) { fprintf(stderr, "libGLdispatch ABI version is incompatible with libGLX.\n"); abort(); } /* Initialize GLdispatch; this will also initialize our pthreads imports */ __glDispatchInit(); glvndSetupPthreads(); glvndAppErrorCheckInit(); glvnd_list_init(¤tThreadStateList); /* * glxContextHashLock must be a recursive mutex, because we'll have it * locked when we call into the vendor library's glXMakeCurrent * implementation. If the vendor library generates an X error, then that * will often result in a call to exit. In that case, the teardown code * will try to lock the mutex again so that it can clean up the current * context list. */ __glvndPthreadFuncs.mutexattr_init(&mutexAttribs); __glvndPthreadFuncs.mutexattr_settype(&mutexAttribs, PTHREAD_MUTEX_RECURSIVE); __glvndPthreadFuncs.mutex_init(&glxContextHashLock, &mutexAttribs); __glvndPthreadFuncs.mutexattr_destroy(&mutexAttribs); __glXMappingInit(); { /* * Check if we need to pre-load any vendors specified via environment * variable. */ const char *preloadedVendor = getenv("__GLX_VENDOR_LIBRARY_NAME"); if (preloadedVendor) { __glXLookupVendorByName(preloadedVendor); } } /* TODO install fork handlers using __register_atfork */ DBG_PRINTF(0, "Loading GLX...\n"); } #if defined(USE_ATTRIBUTE_CONSTRUCTOR) void __attribute__ ((destructor)) __glXFini(void) #else void _fini(void) #endif { /* * Note that the dynamic linker may have already called the destructors for * the vendor libraries. As a result, we can't do anything here that would * try to call into any vendor library. */ /* Check for a fork before going further. */ CheckFork(); /* * If libGLX owns the current thread state, lose current * in GLdispatch before going further. */ __GLdispatchThreadState *glas = __glDispatchGetCurrentThreadState(); if (glas && glas->tag == GLDISPATCH_API_GLX) { __glDispatchLoseCurrent(); } /* Tear down all GLX API state */ __glXAPITeardown(False); /* Tear down all mapping state */ __glXMappingTeardown(False); /* Tear down GLdispatch if necessary */ __glDispatchFini(); }