1110 lines
36 KiB
C
1110 lines
36 KiB
C
/*
|
|
* 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 <X11/Xlibint.h>
|
|
|
|
#include <pthread.h>
|
|
#include <dlfcn.h>
|
|
#include <string.h>
|
|
|
|
#if defined(HASH_DEBUG)
|
|
# include <stdio.h>
|
|
#endif
|
|
|
|
#include "libglxcurrent.h"
|
|
#include "libglxmapping.h"
|
|
#include "libglxthread.h"
|
|
#include "libglxproto.h"
|
|
#include "utils_misc.h"
|
|
#include "glvnd_genentry.h"
|
|
#include "trace.h"
|
|
#include "winsys_dispatch.h"
|
|
|
|
#include "lkdhash.h"
|
|
|
|
#define _GNU_SOURCE 1
|
|
|
|
#if !defined(FALLBACK_VENDOR_NAME)
|
|
/*!
|
|
* This is the vendor name that we'll use as a fallback if we can't otherwise
|
|
* find one.
|
|
*
|
|
* The only place where this should happen is if the display connection is to a
|
|
* remote X server, which might not support the x11glvnd extension, or might
|
|
* specify a vendor library that's not available to the client. In that case,
|
|
* only indirect rendering will be possible.
|
|
*
|
|
* Eventually, libglvnd should have a dedicated vendor library for indirect
|
|
* rendering, independent of any hardware vendor. Until then, this will
|
|
* typically be a symlink to an existing vendor library.
|
|
*/
|
|
#define FALLBACK_VENDOR_NAME "indirect"
|
|
#endif
|
|
|
|
#define GLX_EXTENSION_NAME "GLX"
|
|
|
|
/****************************************************************************/
|
|
|
|
/**
|
|
* __glXVendorNameHash is a hash table mapping a vendor name to vendor info.
|
|
*
|
|
* Note that the lock for the vendor name hashtable is also used to control
|
|
* access to the GLX dispatch index list and the generated GLX dispatch stubs.
|
|
*/
|
|
typedef struct __GLXvendorNameHashRec {
|
|
__GLXvendorInfo vendor;
|
|
|
|
/**
|
|
* The imports table for this vendor. This is allocated and zeroed by
|
|
* libGLX.so, so that we can add functions to the end without breaking
|
|
* backward compatibility.
|
|
*/
|
|
__GLXapiImports imports;
|
|
__GLdispatchPatchCallbacks patchCallbacks;
|
|
|
|
UT_hash_handle hh;
|
|
} __GLXvendorNameHash;
|
|
|
|
static DEFINE_INITIALIZED_LKDHASH(__GLXvendorNameHash, __glXVendorNameHash);
|
|
|
|
typedef struct __GLXdisplayInfoHashRec {
|
|
__GLXdisplayInfo info;
|
|
Bool inTeardown;
|
|
XExtCodes *extCodes;
|
|
UT_hash_handle hh;
|
|
} __GLXdisplayInfoHash;
|
|
|
|
static DEFINE_INITIALIZED_LKDHASH(__GLXdisplayInfoHash, __glXDisplayInfoHash);
|
|
|
|
struct __GLXvendorXIDMappingHashRec {
|
|
XID xid;
|
|
__GLXvendorInfo *vendor;
|
|
UT_hash_handle hh;
|
|
};
|
|
|
|
static __GLXextFuncPtr __glXFetchDispatchEntry(__GLXvendorInfo *vendor, int index);
|
|
|
|
static const __GLXapiExports glxExportsTable = {
|
|
.getDynDispatch = __glXGetDynDispatch,
|
|
.getCurrentDynDispatch = __glXGetCurrentDynDispatch,
|
|
.fetchDispatchEntry = __glXFetchDispatchEntry,
|
|
|
|
/* We use the real function since __glXGetCurrentContext is inline */
|
|
.getCurrentContext = glXGetCurrentContext,
|
|
|
|
.addVendorContextMapping = __glXAddVendorContextMapping,
|
|
.removeVendorContextMapping = __glXRemoveVendorContextMapping,
|
|
.vendorFromContext = __glXVendorFromContext,
|
|
|
|
.addVendorFBConfigMapping = __glXAddVendorFBConfigMapping,
|
|
.removeVendorFBConfigMapping = __glXRemoveVendorFBConfigMapping,
|
|
.vendorFromFBConfig = __glXVendorFromFBConfig,
|
|
|
|
.addVendorDrawableMapping = __glXAddVendorDrawableMapping,
|
|
.removeVendorDrawableMapping = __glXRemoveVendorDrawableMapping,
|
|
.vendorFromDrawable = __glXVendorFromDrawable,
|
|
};
|
|
|
|
/*!
|
|
* Looks for a GLX dispatch function.
|
|
*
|
|
* This function queries each loaded vendor to determine if there is
|
|
* a vendor-implemented dispatch function. The dispatch function
|
|
* uses the vendor <-> API library ABI to determine the screen given
|
|
* the parameters of the function and dispatch to the correct vendor's
|
|
* implementation.
|
|
*
|
|
* If no vendor provides a dispatch function, then instead we'll generate a
|
|
* dispatch stub. We'll plug a real dispatch function into that stub later.
|
|
*/
|
|
__GLXextFuncPtr __glXGetGLXDispatchAddress(const GLubyte *procName)
|
|
{
|
|
int index;
|
|
__GLXextFuncPtr addr = NULL;
|
|
Bool isGLX;
|
|
__GLXvendorNameHash *pEntry, *tmp;
|
|
|
|
/*
|
|
* Note that if a GLX extension function doesn't depend on calling any
|
|
* other GLX functions first, then the app could call it before loading any
|
|
* vendor libraries. If that happens, then the entrypoint would go to a
|
|
* no-op stub instead of the correct dispatch stub.
|
|
*
|
|
* Running into that case would be an application bug, since it means that
|
|
* the application is calling an extension function without checking the
|
|
* extension string -- calling glXGetClientString would have loaded the
|
|
* vendor libraries for every screen.
|
|
*
|
|
* In order to work with a buggy app like that, we might have to find and
|
|
* load all available vendor libraries until we find one that supports the
|
|
* function. Lacking that, a user could work around the issue by setting
|
|
* __GLX_VENDOR_LIBRARY_NAME.
|
|
*/
|
|
|
|
// First, check if we've already found a dispatch stub. Note that this
|
|
// generally shouldn't happen, because we cache the results of
|
|
// glXGetProcAddress.
|
|
|
|
// The vendor name hashtable's lock is also used for the dispatch index
|
|
// list and the generated GLX entrypoints.
|
|
LKDHASH_WRLOCK(__glXVendorNameHash);
|
|
index = __glvndWinsysDispatchFindIndex((const char *) procName);
|
|
if (index >= 0) {
|
|
addr = (__GLXextFuncPtr) __glvndWinsysDispatchGetDispatch(index);
|
|
|
|
LKDHASH_UNLOCK(__glXVendorNameHash);
|
|
return addr;
|
|
}
|
|
|
|
// We haven't seen this function before, so we need to find or generate a
|
|
// dispatch stub.
|
|
|
|
// First, look for a GLX dispatch function from any vendor.
|
|
HASH_ITER(hh, _LH(__glXVendorNameHash), pEntry, tmp) {
|
|
addr = pEntry->vendor.glxvc->getDispatchAddress((const GLubyte *) procName);
|
|
if (addr != NULL) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (addr != NULL) {
|
|
isGLX = True;
|
|
} else {
|
|
// Look to see if any vendor provides an implementation function. If
|
|
// it does, then that means this is really a GL function that happens
|
|
// to start with "glX".
|
|
HASH_ITER(hh, _LH(__glXVendorNameHash), pEntry, tmp) {
|
|
addr = pEntry->vendor.glxvc->getProcAddress((const GLubyte *) procName);
|
|
if (addr != NULL) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (addr != NULL) {
|
|
// This is a GL function, so get a dispatch stub from
|
|
// libGLdispatch.
|
|
addr = __glDispatchGetProcAddress((const char *) procName);
|
|
isGLX = False;
|
|
} else {
|
|
// None of the vendor libraries provide the function in either
|
|
// form. That probably means it's a GLX extension function from a
|
|
// vendor that hasn't been loaded yet. Generate a GLX entrypoint
|
|
// stub. We'll plug in the real GLX dispatch function if and when
|
|
// we load a vendor library that supports it.
|
|
addr = (__GLXextFuncPtr) glvndGenerateEntrypoint((const char *) procName);
|
|
isGLX = True;
|
|
}
|
|
}
|
|
|
|
if (addr != NULL && isGLX) {
|
|
index = __glvndWinsysDispatchAllocIndex((const char *) procName, addr);
|
|
if (index >= 0) {
|
|
HASH_ITER(hh, _LH(__glXVendorNameHash), pEntry, tmp) {
|
|
pEntry->vendor.glxvc->setDispatchIndex(procName, index);
|
|
}
|
|
} else {
|
|
addr = NULL;
|
|
}
|
|
}
|
|
|
|
LKDHASH_UNLOCK(__glXVendorNameHash);
|
|
|
|
return addr;
|
|
}
|
|
|
|
static GLVNDentrypointStub GLXEntrypointUpdateCallback(const char *procName, void *param)
|
|
{
|
|
__GLXvendorInfo *vendor = (__GLXvendorInfo *) param;
|
|
__GLXextFuncPtr addr = NULL;
|
|
|
|
addr = vendor->glxvc->getDispatchAddress((const GLubyte *) procName);
|
|
if (addr == NULL) {
|
|
// If we didn't find a GLX dispatch function, then check for a normal
|
|
// OpenGL function. This should handle any case where a GL extension
|
|
// function starts with "glX".
|
|
addr = vendor->glxvc->getProcAddress((const GLubyte *) procName);
|
|
if (addr != NULL) {
|
|
addr = __glDispatchGetProcAddress(procName);
|
|
}
|
|
}
|
|
return (GLVNDentrypointStub) addr;
|
|
}
|
|
|
|
__GLXextFuncPtr __glXFetchDispatchEntry(__GLXvendorInfo *vendor,
|
|
int index)
|
|
{
|
|
__GLXextFuncPtr addr = NULL;
|
|
const GLubyte *procName = NULL;
|
|
|
|
addr = (__GLXextFuncPtr) __glvndWinsysVendorDispatchLookupFunc(vendor->dynDispatch, index);
|
|
if (addr != NULL) {
|
|
return addr;
|
|
}
|
|
|
|
// Not seen before by this vendor: query the vendor for the right
|
|
// address to use.
|
|
|
|
LKDHASH_RDLOCK(__glXVendorNameHash);
|
|
procName = (const GLubyte *) __glvndWinsysDispatchGetName(index);
|
|
LKDHASH_UNLOCK(__glXVendorNameHash);
|
|
|
|
// This should have a valid entry point associated with it.
|
|
if (procName == NULL) {
|
|
assert(procName);
|
|
return NULL;
|
|
}
|
|
|
|
// Get the real address.
|
|
addr = vendor->glxvc->getProcAddress(procName);
|
|
if (addr != NULL) {
|
|
// Record the address in the vendor's hashtable. Note that if this
|
|
// fails, it's not fatal. It just means we'll have to call
|
|
// getProcAddress again the next time we need this function.
|
|
__glvndWinsysVendorDispatchAddFunc(vendor->dynDispatch, index, addr);
|
|
}
|
|
return addr;
|
|
}
|
|
|
|
static char *ConstructVendorLibraryFilename(const char *vendorName)
|
|
{
|
|
char *filename;
|
|
int ret;
|
|
|
|
ret = glvnd_asprintf(&filename, "libGLX_%s.so.0", vendorName);
|
|
|
|
if (ret < 0) {
|
|
return NULL;
|
|
}
|
|
|
|
return filename;
|
|
}
|
|
|
|
static void CleanupVendorNameEntry(void *unused,
|
|
__GLXvendorNameHash *pEntry)
|
|
{
|
|
__GLXvendorInfo *vendor = &pEntry->vendor;
|
|
if (vendor->glDispatch != NULL) {
|
|
__glDispatchDestroyTable(vendor->glDispatch);
|
|
vendor->glDispatch = NULL;
|
|
}
|
|
|
|
if (vendor->dynDispatch != NULL) {
|
|
__glvndWinsysVendorDispatchDestroy(vendor->dynDispatch);
|
|
vendor->dynDispatch = NULL;
|
|
}
|
|
|
|
if (vendor->dlhandle != NULL) {
|
|
dlclose(vendor->dlhandle);
|
|
vendor->dlhandle = NULL;
|
|
}
|
|
}
|
|
|
|
static GLboolean LookupVendorEntrypoints(__GLXvendorInfo *vendor)
|
|
{
|
|
#define LOADENTRYPOINT(ptr, name) do { \
|
|
vendor->staticDispatch.ptr = vendor->glxvc->getProcAddress((const GLubyte *) name); \
|
|
if (vendor->staticDispatch.ptr == NULL) { return GL_FALSE; } \
|
|
} while(0)
|
|
|
|
LOADENTRYPOINT(chooseVisual, "glXChooseVisual" );
|
|
LOADENTRYPOINT(copyContext, "glXCopyContext" );
|
|
LOADENTRYPOINT(createContext, "glXCreateContext" );
|
|
LOADENTRYPOINT(createGLXPixmap, "glXCreateGLXPixmap" );
|
|
LOADENTRYPOINT(destroyContext, "glXDestroyContext" );
|
|
LOADENTRYPOINT(destroyGLXPixmap, "glXDestroyGLXPixmap" );
|
|
LOADENTRYPOINT(getConfig, "glXGetConfig" );
|
|
LOADENTRYPOINT(isDirect, "glXIsDirect" );
|
|
LOADENTRYPOINT(makeCurrent, "glXMakeCurrent" );
|
|
LOADENTRYPOINT(swapBuffers, "glXSwapBuffers" );
|
|
LOADENTRYPOINT(useXFont, "glXUseXFont" );
|
|
LOADENTRYPOINT(waitGL, "glXWaitGL" );
|
|
LOADENTRYPOINT(waitX, "glXWaitX" );
|
|
LOADENTRYPOINT(queryServerString, "glXQueryServerString" );
|
|
LOADENTRYPOINT(getClientString, "glXGetClientString" );
|
|
LOADENTRYPOINT(queryExtensionsString, "glXQueryExtensionsString");
|
|
LOADENTRYPOINT(chooseFBConfig, "glXChooseFBConfig" );
|
|
LOADENTRYPOINT(createNewContext, "glXCreateNewContext" );
|
|
LOADENTRYPOINT(createPbuffer, "glXCreatePbuffer" );
|
|
LOADENTRYPOINT(createPixmap, "glXCreatePixmap" );
|
|
LOADENTRYPOINT(createWindow, "glXCreateWindow" );
|
|
LOADENTRYPOINT(destroyPbuffer, "glXDestroyPbuffer" );
|
|
LOADENTRYPOINT(destroyPixmap, "glXDestroyPixmap" );
|
|
LOADENTRYPOINT(destroyWindow, "glXDestroyWindow" );
|
|
LOADENTRYPOINT(getFBConfigAttrib, "glXGetFBConfigAttrib" );
|
|
LOADENTRYPOINT(getFBConfigs, "glXGetFBConfigs" );
|
|
LOADENTRYPOINT(getSelectedEvent, "glXGetSelectedEvent" );
|
|
LOADENTRYPOINT(getVisualFromFBConfig, "glXGetVisualFromFBConfig");
|
|
LOADENTRYPOINT(makeContextCurrent, "glXMakeContextCurrent" );
|
|
LOADENTRYPOINT(queryContext, "glXQueryContext" );
|
|
LOADENTRYPOINT(queryDrawable, "glXQueryDrawable" );
|
|
LOADENTRYPOINT(selectEvent, "glXSelectEvent" );
|
|
#undef LOADENTRYPOINT
|
|
|
|
// These functions are optional.
|
|
#define LOADENTRYPOINT(ptr, name) do { \
|
|
vendor->staticDispatch.ptr = vendor->glxvc->getProcAddress((const GLubyte *) name); \
|
|
} while(0)
|
|
LOADENTRYPOINT(importContextEXT, "glXImportContextEXT" );
|
|
LOADENTRYPOINT(freeContextEXT, "glXFreeContextEXT" );
|
|
LOADENTRYPOINT(createContextAttribsARB, "glXCreateContextAttribsARB" );
|
|
#undef LOADENTRYPOINT
|
|
|
|
return GL_TRUE;
|
|
}
|
|
|
|
static void *VendorGetProcAddressCallback(const char *procName, void *param)
|
|
{
|
|
__GLXvendorInfo *vendor = (__GLXvendorInfo *) param;
|
|
return vendor->glxvc->getProcAddress((const GLubyte *) procName);
|
|
}
|
|
|
|
__GLXvendorInfo *__glXLookupVendorByName(const char *vendorName)
|
|
{
|
|
__GLXvendorNameHash *pEntry = NULL;
|
|
Bool locked = False;
|
|
size_t vendorNameLen;
|
|
|
|
// We'll use the vendor name to construct a DSO name, so make sure it
|
|
// doesn't contain any '/' characters.
|
|
if (strchr(vendorName, '/') != NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
vendorNameLen = strlen(vendorName);
|
|
|
|
LKDHASH_RDLOCK(__glXVendorNameHash);
|
|
HASH_FIND(hh, _LH(__glXVendorNameHash), vendorName, vendorNameLen, pEntry);
|
|
|
|
LKDHASH_UNLOCK(__glXVendorNameHash);
|
|
|
|
if (!pEntry) {
|
|
LKDHASH_WRLOCK(__glXVendorNameHash);
|
|
locked = True;
|
|
// Do another lookup to check uniqueness
|
|
HASH_FIND(hh, _LH(__glXVendorNameHash), vendorName, vendorNameLen, pEntry);
|
|
if (!pEntry) {
|
|
__GLXvendorInfo *vendor;
|
|
__PFNGLXMAINPROC glxMainProc;
|
|
char *filename, *nix_path;
|
|
int i, count;
|
|
Bool success;
|
|
|
|
// Previously unseen vendor. dlopen() the new vendor and add it to the
|
|
// hash table.
|
|
pEntry = calloc(1, sizeof(*pEntry) + vendorNameLen + 1);
|
|
if (!pEntry) {
|
|
goto fail;
|
|
}
|
|
vendor = &pEntry->vendor;
|
|
|
|
vendor->glxvc = &pEntry->imports;
|
|
vendor->name = (char *) (pEntry + 1);
|
|
memcpy(vendor->name, vendorName, vendorNameLen + 1);
|
|
|
|
filename = ConstructVendorLibraryFilename(vendorName);
|
|
if (filename) {
|
|
nix_path = secure_getenv("NIX_GLVND_GLX_PATH");
|
|
if(nix_path) {
|
|
size_t nix_path_len;
|
|
char* path_sep, *full_path;
|
|
nix_path_len = strlen(nix_path);
|
|
// Let's make sure we don't end up with a double
|
|
// slash in the path.
|
|
if (nix_path > 0 && nix_path[nix_path_len - 1] != '/') {
|
|
path_sep = "/";
|
|
} else {
|
|
path_sep = "";
|
|
}
|
|
glvnd_asprintf(&full_path, "%s%s%s", nix_path, path_sep, filename);
|
|
vendor->dlhandle = dlopen(full_path, RTLD_LAZY);
|
|
free(full_path);
|
|
} else {
|
|
vendor->dlhandle = dlopen(filename, RTLD_LAZY);
|
|
}
|
|
}
|
|
free(filename);
|
|
if (vendor->dlhandle == NULL) {
|
|
goto fail;
|
|
}
|
|
|
|
glxMainProc = dlsym(vendor->dlhandle, __GLX_MAIN_PROTO_NAME);
|
|
if (!glxMainProc) {
|
|
goto fail;
|
|
}
|
|
|
|
vendor->vendorID = __glDispatchNewVendorID();
|
|
assert(vendor->vendorID >= 0);
|
|
|
|
vendor->glDispatch = (__GLdispatchTable *)
|
|
__glDispatchCreateTable(
|
|
VendorGetProcAddressCallback,
|
|
vendor
|
|
);
|
|
if (!vendor->glDispatch) {
|
|
goto fail;
|
|
}
|
|
|
|
/* Initialize the dynamic dispatch table */
|
|
vendor->dynDispatch = __glvndWinsysVendorDispatchCreate();
|
|
if (vendor->dynDispatch == NULL) {
|
|
goto fail;
|
|
}
|
|
|
|
success = (*glxMainProc)(GLX_VENDOR_ABI_VERSION,
|
|
&glxExportsTable,
|
|
vendor, &pEntry->imports);
|
|
if (!success) {
|
|
goto fail;
|
|
}
|
|
|
|
// Make sure all the required functions are there.
|
|
if (pEntry->imports.isScreenSupported == NULL
|
|
|| pEntry->imports.getProcAddress == NULL
|
|
|| pEntry->imports.getDispatchAddress == NULL
|
|
|| pEntry->imports.setDispatchIndex == NULL)
|
|
{
|
|
goto fail;
|
|
}
|
|
|
|
if (!LookupVendorEntrypoints(vendor)) {
|
|
goto fail;
|
|
}
|
|
|
|
// Check to see whether this vendor library can support entrypoint
|
|
// patching.
|
|
if (pEntry->imports.isPatchSupported != NULL
|
|
&& pEntry->imports.initiatePatch != NULL) {
|
|
pEntry->patchCallbacks.isPatchSupported = pEntry->imports.isPatchSupported;
|
|
pEntry->patchCallbacks.initiatePatch = pEntry->imports.initiatePatch;
|
|
pEntry->patchCallbacks.releasePatch = pEntry->imports.releasePatch;
|
|
pEntry->patchCallbacks.threadAttach = pEntry->imports.patchThreadAttach;
|
|
pEntry->vendor.patchCallbacks = &pEntry->patchCallbacks;
|
|
}
|
|
|
|
HASH_ADD_KEYPTR(hh, _LH(__glXVendorNameHash), vendor->name,
|
|
strlen(vendor->name), pEntry);
|
|
|
|
// Look up the dispatch functions for any GLX extensions that we
|
|
// generated entrypoints for.
|
|
glvndUpdateEntrypoints(GLXEntrypointUpdateCallback, vendor);
|
|
|
|
// Tell the vendor the index of all of the GLX dispatch stubs.
|
|
count = __glvndWinsysDispatchGetCount();
|
|
for (i=0; i<count; i++) {
|
|
const char *procName = __glvndWinsysDispatchGetName(i);
|
|
vendor->glxvc->setDispatchIndex((const GLubyte *) procName, i);
|
|
}
|
|
}
|
|
LKDHASH_UNLOCK(__glXVendorNameHash);
|
|
}
|
|
|
|
return &pEntry->vendor;
|
|
|
|
fail:
|
|
if (locked) {
|
|
LKDHASH_UNLOCK(__glXVendorNameHash);
|
|
}
|
|
if (pEntry != NULL) {
|
|
CleanupVendorNameEntry(NULL, pEntry);
|
|
free(pEntry);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
__GLXvendorInfo *__glXLookupVendorByScreen(Display *dpy, const int screen)
|
|
{
|
|
__GLXvendorInfo *vendor = NULL;
|
|
__GLXdisplayInfo *dpyInfo;
|
|
|
|
if (screen < 0 || screen >= ScreenCount(dpy)) {
|
|
return NULL;
|
|
}
|
|
|
|
dpyInfo = __glXLookupDisplay(dpy);
|
|
if (dpyInfo == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
__glvndPthreadFuncs.rwlock_rdlock(&dpyInfo->vendorLock);
|
|
vendor = dpyInfo->vendors[screen];
|
|
__glvndPthreadFuncs.rwlock_unlock(&dpyInfo->vendorLock);
|
|
|
|
if (vendor != NULL) {
|
|
return vendor;
|
|
}
|
|
|
|
__glvndPthreadFuncs.rwlock_wrlock(&dpyInfo->vendorLock);
|
|
vendor = dpyInfo->vendors[screen];
|
|
|
|
if (!vendor) {
|
|
/*
|
|
* If we have specified a vendor library, use that. Otherwise,
|
|
* try to lookup the vendor based on the current screen.
|
|
*/
|
|
char envName[40];
|
|
const char *specifiedVendorName;
|
|
|
|
snprintf(envName, sizeof(envName), "__GLX_FORCE_VENDOR_LIBRARY_%d", screen);
|
|
specifiedVendorName = getenv(envName);
|
|
|
|
if (specifiedVendorName == NULL) {
|
|
specifiedVendorName = getenv("__GLX_VENDOR_LIBRARY_NAME");
|
|
}
|
|
|
|
if (specifiedVendorName) {
|
|
vendor = __glXLookupVendorByName(specifiedVendorName);
|
|
}
|
|
|
|
if (!vendor) {
|
|
if (dpyInfo->libglvndExtensionSupported) {
|
|
char *queriedVendorNames =
|
|
__glXQueryServerString(dpyInfo, screen, GLX_VENDOR_NAMES_EXT);
|
|
if (queriedVendorNames != NULL) {
|
|
char *name, *saveptr;
|
|
for (name = strtok_r(queriedVendorNames, " ", &saveptr);
|
|
name != NULL;
|
|
name = strtok_r(NULL, " ", &saveptr)) {
|
|
vendor = __glXLookupVendorByName(name);
|
|
|
|
// Make sure that the vendor library can support this screen.
|
|
if (vendor != NULL && !vendor->glxvc->isScreenSupported(dpy, screen)) {
|
|
vendor = NULL;
|
|
}
|
|
|
|
if (vendor != NULL) {
|
|
break;
|
|
}
|
|
}
|
|
free(queriedVendorNames);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!vendor) {
|
|
vendor = __glXLookupVendorByName(FALLBACK_VENDOR_NAME);
|
|
}
|
|
|
|
dpyInfo->vendors[screen] = vendor;
|
|
}
|
|
__glvndPthreadFuncs.rwlock_unlock(&dpyInfo->vendorLock);
|
|
|
|
DBG_PRINTF(10, "Found vendor \"%s\" for screen %d\n",
|
|
(vendor != NULL ? vendor->name : "NULL"), screen);
|
|
|
|
return vendor;
|
|
}
|
|
|
|
__GLXvendorInfo *__glXGetDynDispatch(Display *dpy, const int screen)
|
|
{
|
|
__glXThreadInitialize();
|
|
|
|
__GLXvendorInfo *vendor = __glXLookupVendorByScreen(dpy, screen);
|
|
return vendor;
|
|
}
|
|
|
|
/**
|
|
* Allocates and initializes a __GLXdisplayInfoHash structure.
|
|
*
|
|
* The caller is responsible for adding the structure to the hashtable.
|
|
*
|
|
* \param dpy The display connection.
|
|
* \return A newly-allocated __GLXdisplayInfoHash structure, or NULL on error.
|
|
*/
|
|
static __GLXdisplayInfoHash *InitDisplayInfoEntry(Display *dpy)
|
|
{
|
|
__GLXdisplayInfoHash *pEntry;
|
|
size_t size;
|
|
int eventBase;
|
|
|
|
size = sizeof(*pEntry) + ScreenCount(dpy) * sizeof(__GLXvendorInfo *);
|
|
pEntry = (__GLXdisplayInfoHash *) malloc(size);
|
|
if (pEntry == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(pEntry, 0, size);
|
|
pEntry->info.dpy = dpy;
|
|
pEntry->info.vendors = (__GLXvendorInfo **) (pEntry + 1);
|
|
|
|
LKDHASH_INIT(pEntry->info.xidVendorHash);
|
|
__glvndPthreadFuncs.rwlock_init(&pEntry->info.vendorLock, NULL);
|
|
|
|
// Check whether the server supports the GLX extension, and record the
|
|
// major opcode if it does.
|
|
pEntry->info.glxSupported = XQueryExtension(dpy, GLX_EXTENSION_NAME,
|
|
&pEntry->info.glxMajorOpcode, &eventBase,
|
|
&pEntry->info.glxFirstError);
|
|
|
|
if (pEntry->info.glxSupported) {
|
|
int screen;
|
|
|
|
// Check to see if the server supports the GLX_EXT_libglvnd extension.
|
|
// Note that it has to be supported on every screen to use it.
|
|
pEntry->info.libglvndExtensionSupported = True;
|
|
for (screen = 0;
|
|
screen < ScreenCount(dpy) && pEntry->info.libglvndExtensionSupported;
|
|
screen++) {
|
|
char *extensions = __glXQueryServerString(&pEntry->info, screen, GLX_EXTENSIONS);
|
|
if (extensions != NULL) {
|
|
if (!IsTokenInString(extensions, GLX_EXT_LIBGLVND_NAME,
|
|
strlen(GLX_EXT_LIBGLVND_NAME), " ")) {
|
|
pEntry->info.libglvndExtensionSupported = False;
|
|
}
|
|
free(extensions);
|
|
} else {
|
|
pEntry->info.libglvndExtensionSupported = False;
|
|
}
|
|
}
|
|
}
|
|
|
|
return pEntry;
|
|
}
|
|
|
|
/**
|
|
* Frees a __GLXdisplayInfoHash structure.
|
|
*
|
|
* The caller is responsible for removing the structure from the hashtable.
|
|
*
|
|
* \param unused Ingored. Needed for the uthash teardown function.
|
|
* \param pEntry The structure to free.
|
|
*/
|
|
static void CleanupDisplayInfoEntry(void *unused, __GLXdisplayInfoHash *pEntry)
|
|
{
|
|
int i;
|
|
|
|
if (pEntry == NULL) {
|
|
return;
|
|
}
|
|
|
|
for (i=0; i<GLX_CLIENT_STRING_LAST_ATTRIB; i++) {
|
|
free(pEntry->info.clientStrings[i]);
|
|
}
|
|
|
|
if (pEntry->extCodes != NULL) {
|
|
XESetCloseDisplay(pEntry->info.dpy, pEntry->extCodes->extension, NULL);
|
|
}
|
|
|
|
LKDHASH_TEARDOWN(__GLXvendorXIDMappingHash,
|
|
pEntry->info.xidVendorHash, NULL, NULL, False);
|
|
}
|
|
|
|
static int OnDisplayClosed(Display *dpy, XExtCodes *codes)
|
|
{
|
|
__GLXdisplayInfoHash *pEntry = NULL;
|
|
|
|
LKDHASH_WRLOCK(__glXDisplayInfoHash);
|
|
|
|
// Set the inTeardown flag for this display, but don't remove it from the
|
|
// hashtable yet. We shouldn't try to look up this display after this, but
|
|
// just in case something does, we don't want to try to set up a new
|
|
// __GLXdisplayInfoHash struct for it.
|
|
HASH_FIND_PTR(_LH(__glXDisplayInfoHash), &dpy, pEntry);
|
|
if (pEntry != NULL) {
|
|
assert(!pEntry->inTeardown);
|
|
pEntry->inTeardown = True;
|
|
}
|
|
LKDHASH_UNLOCK(__glXDisplayInfoHash);
|
|
|
|
if (pEntry != NULL) {
|
|
__glXDisplayClosed(&pEntry->info);
|
|
|
|
// Now, we can remove the display from the hashtable and free it.
|
|
LKDHASH_WRLOCK(__glXDisplayInfoHash);
|
|
HASH_DEL(_LH(__glXDisplayInfoHash), pEntry);
|
|
LKDHASH_UNLOCK(__glXDisplayInfoHash);
|
|
|
|
pEntry->extCodes = NULL;
|
|
CleanupDisplayInfoEntry(NULL, pEntry);
|
|
free(pEntry);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
__GLXdisplayInfo *__glXLookupDisplay(Display *dpy)
|
|
{
|
|
__GLXdisplayInfoHash *pEntry = NULL;
|
|
__GLXdisplayInfoHash *foundEntry = NULL;
|
|
|
|
if (dpy == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
LKDHASH_RDLOCK(__glXDisplayInfoHash);
|
|
HASH_FIND_PTR(_LH(__glXDisplayInfoHash), &dpy, pEntry);
|
|
LKDHASH_UNLOCK(__glXDisplayInfoHash);
|
|
|
|
if (pEntry != NULL) {
|
|
if (pEntry->inTeardown) {
|
|
// This shouldn't happen: If we're in the process of tearing down a
|
|
// display, then we shouldn't be trying to look up the display
|
|
// again.
|
|
return NULL;
|
|
}
|
|
return &pEntry->info;
|
|
}
|
|
|
|
// Create the new __GLXdisplayInfoHash structure without holding the lock.
|
|
// If we run into an X error, we may wind up in __glXMappingTeardown before
|
|
// we can unlock it again, which would deadlock.
|
|
pEntry = InitDisplayInfoEntry(dpy);
|
|
if (pEntry == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
LKDHASH_WRLOCK(__glXDisplayInfoHash);
|
|
HASH_FIND_PTR(_LH(__glXDisplayInfoHash), &dpy, foundEntry);
|
|
if (foundEntry == NULL) {
|
|
pEntry->extCodes = XAddExtension(dpy);
|
|
if (pEntry->extCodes == NULL) {
|
|
CleanupDisplayInfoEntry(NULL, pEntry);
|
|
free(pEntry);
|
|
LKDHASH_UNLOCK(__glXDisplayInfoHash);
|
|
return NULL;
|
|
}
|
|
|
|
XESetCloseDisplay(dpy, pEntry->extCodes->extension, OnDisplayClosed);
|
|
HASH_ADD_PTR(_LH(__glXDisplayInfoHash), info.dpy, pEntry);
|
|
} else {
|
|
// Another thread already created the hashtable entry.
|
|
CleanupDisplayInfoEntry(NULL, pEntry);
|
|
free(pEntry);
|
|
pEntry = foundEntry;
|
|
}
|
|
LKDHASH_UNLOCK(__glXDisplayInfoHash);
|
|
|
|
return &pEntry->info;
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/*
|
|
* Define two hashtables to store the mappings for GLXFBConfig and GLXContext
|
|
* handles to vendor libraries.
|
|
*
|
|
* The same functions are used to access both tables.
|
|
*/
|
|
|
|
typedef struct {
|
|
GLXFBConfig config;
|
|
__GLXvendorInfo *vendor;
|
|
UT_hash_handle hh;
|
|
} __GLXvendorConfigMappingHash;
|
|
|
|
static DEFINE_LKDHASH(__GLXvendorConfigMappingHash, fbconfigHashtable);
|
|
|
|
int __glXAddVendorFBConfigMapping(Display *dpy, GLXFBConfig config, __GLXvendorInfo *vendor)
|
|
{
|
|
__GLXvendorConfigMappingHash *pEntry;
|
|
|
|
if (config == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
if (vendor == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
LKDHASH_WRLOCK(fbconfigHashtable);
|
|
|
|
HASH_FIND_PTR(_LH(fbconfigHashtable), &config, pEntry);
|
|
|
|
if (pEntry == NULL) {
|
|
pEntry = malloc(sizeof(*pEntry));
|
|
if (pEntry == NULL) {
|
|
LKDHASH_UNLOCK(fbconfigHashtable);
|
|
return -1;
|
|
}
|
|
pEntry->config = config;
|
|
pEntry->vendor = vendor;
|
|
HASH_ADD_PTR(_LH(fbconfigHashtable), config, pEntry);
|
|
} else {
|
|
// Any GLXContext or GLXFBConfig handles must be unique to a single
|
|
// vendor at a time. If we get two different vendors, then there's
|
|
// either a bug in libGLX or in at least one of the vendor libraries.
|
|
if (pEntry->vendor != vendor) {
|
|
LKDHASH_UNLOCK(fbconfigHashtable);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
LKDHASH_UNLOCK(fbconfigHashtable);
|
|
return 0;
|
|
}
|
|
|
|
void __glXRemoveVendorFBConfigMapping(Display *dpy, GLXFBConfig config)
|
|
{
|
|
__GLXvendorConfigMappingHash *pEntry;
|
|
|
|
if (config == NULL) {
|
|
return;
|
|
}
|
|
|
|
LKDHASH_WRLOCK(fbconfigHashtable);
|
|
|
|
HASH_FIND_PTR(_LH(fbconfigHashtable), &config, pEntry);
|
|
|
|
if (pEntry != NULL) {
|
|
HASH_DELETE(hh, _LH(fbconfigHashtable), pEntry);
|
|
free(pEntry);
|
|
}
|
|
|
|
LKDHASH_UNLOCK(fbconfigHashtable);
|
|
}
|
|
|
|
__GLXvendorInfo *__glXVendorFromFBConfig(Display *dpy, GLXFBConfig config)
|
|
{
|
|
__GLXvendorConfigMappingHash *pEntry;
|
|
__GLXvendorInfo *vendor = NULL;
|
|
|
|
__glXThreadInitialize();
|
|
|
|
LKDHASH_RDLOCK(fbconfigHashtable);
|
|
|
|
HASH_FIND_PTR(_LH(fbconfigHashtable), &config, pEntry);
|
|
|
|
if (pEntry != NULL) {
|
|
vendor = pEntry->vendor;
|
|
}
|
|
|
|
LKDHASH_UNLOCK(fbconfigHashtable);
|
|
|
|
return vendor;
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************/
|
|
/*
|
|
* __GLXvendorXIDMappingHash is a hash table which maps XIDs to vendors.
|
|
*/
|
|
|
|
|
|
static int AddVendorXIDMapping(Display *dpy, __GLXdisplayInfo *dpyInfo, XID xid, __GLXvendorInfo *vendor)
|
|
{
|
|
__GLXvendorXIDMappingHash *pEntry = NULL;
|
|
|
|
if (xid == None) {
|
|
return 0;
|
|
}
|
|
|
|
if (vendor == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
LKDHASH_WRLOCK(dpyInfo->xidVendorHash);
|
|
|
|
HASH_FIND(hh, _LH(dpyInfo->xidVendorHash), &xid, sizeof(xid), pEntry);
|
|
|
|
if (pEntry == NULL) {
|
|
pEntry = malloc(sizeof(*pEntry));
|
|
if (pEntry == NULL) {
|
|
LKDHASH_UNLOCK(dpyInfo->xidVendorHash);
|
|
return -1;
|
|
}
|
|
pEntry->xid = xid;
|
|
pEntry->vendor = vendor;
|
|
HASH_ADD(hh, _LH(dpyInfo->xidVendorHash), xid, sizeof(xid), pEntry);
|
|
} else {
|
|
// Like GLXContext and GLXFBConfig handles, any GLXDrawables must map
|
|
// to a single vendor library.
|
|
if (pEntry->vendor != vendor) {
|
|
LKDHASH_UNLOCK(dpyInfo->xidVendorHash);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
LKDHASH_UNLOCK(dpyInfo->xidVendorHash);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void RemoveVendorXIDMapping(Display *dpy, __GLXdisplayInfo *dpyInfo, XID xid)
|
|
{
|
|
__GLXvendorXIDMappingHash *pEntry;
|
|
|
|
if (xid == None) {
|
|
return;
|
|
}
|
|
|
|
LKDHASH_WRLOCK(dpyInfo->xidVendorHash);
|
|
|
|
HASH_FIND(hh, _LH(dpyInfo->xidVendorHash), &xid, sizeof(xid), pEntry);
|
|
|
|
if (pEntry != NULL) {
|
|
HASH_DELETE(hh, _LH(dpyInfo->xidVendorHash), pEntry);
|
|
free(pEntry);
|
|
}
|
|
|
|
LKDHASH_UNLOCK(dpyInfo->xidVendorHash);
|
|
}
|
|
|
|
|
|
static void VendorFromXID(Display *dpy, __GLXdisplayInfo *dpyInfo, XID xid,
|
|
__GLXvendorInfo **retVendor)
|
|
{
|
|
__GLXvendorXIDMappingHash *pEntry;
|
|
__GLXvendorInfo *vendor = NULL;
|
|
|
|
LKDHASH_RDLOCK(dpyInfo->xidVendorHash);
|
|
|
|
HASH_FIND(hh, _LH(dpyInfo->xidVendorHash), &xid, sizeof(xid), pEntry);
|
|
|
|
if (pEntry) {
|
|
vendor = pEntry->vendor;
|
|
LKDHASH_UNLOCK(dpyInfo->xidVendorHash);
|
|
} else {
|
|
LKDHASH_UNLOCK(dpyInfo->xidVendorHash);
|
|
|
|
if (dpyInfo->libglvndExtensionSupported) {
|
|
int screen = __glXGetDrawableScreen(dpyInfo, xid);
|
|
if (screen >= 0 && screen < ScreenCount(dpy)) {
|
|
vendor = __glXLookupVendorByScreen(dpy, screen);
|
|
if (vendor != NULL) {
|
|
// Note that if this fails, it's not necessarily a problem.
|
|
// We can just query it again next time.
|
|
AddVendorXIDMapping(dpy, dpyInfo, xid, vendor);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (retVendor != NULL) {
|
|
*retVendor = vendor;
|
|
}
|
|
}
|
|
|
|
|
|
int __glXAddVendorDrawableMapping(Display *dpy, GLXDrawable drawable, __GLXvendorInfo *vendor)
|
|
{
|
|
__GLXdisplayInfo *dpyInfo = __glXLookupDisplay(dpy);
|
|
if (dpyInfo != NULL) {
|
|
return AddVendorXIDMapping(dpy, dpyInfo, drawable, vendor);
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
|
|
void __glXRemoveVendorDrawableMapping(Display *dpy, GLXDrawable drawable)
|
|
{
|
|
__GLXdisplayInfo *dpyInfo = __glXLookupDisplay(dpy);
|
|
if (dpyInfo != NULL) {
|
|
RemoveVendorXIDMapping(dpy, dpyInfo, drawable);
|
|
}
|
|
}
|
|
|
|
|
|
__GLXvendorInfo *__glXVendorFromDrawable(Display *dpy, GLXDrawable drawable)
|
|
{
|
|
__glXThreadInitialize();
|
|
|
|
__GLXdisplayInfo *dpyInfo = __glXLookupDisplay(dpy);
|
|
__GLXvendorInfo *vendor = NULL;
|
|
if (dpyInfo != NULL) {
|
|
if (dpyInfo->libglvndExtensionSupported) {
|
|
VendorFromXID(dpy, dpyInfo, drawable, &vendor);
|
|
} else {
|
|
// We'll use the same vendor for every screen in this case.
|
|
vendor = __glXLookupVendorByScreen(dpy, 0);
|
|
}
|
|
}
|
|
|
|
return vendor;
|
|
}
|
|
|
|
void __glXMappingInit(void)
|
|
{
|
|
int i;
|
|
|
|
__glvndWinsysDispatchInit();
|
|
|
|
// Add all of the GLX dispatch stubs that are defined in libGLX itself.
|
|
for (i=0; LOCAL_GLX_DISPATCH_FUNCTIONS[i].name != NULL; i++) {
|
|
// TODO: Is there any way to recover from a malloc failure here?
|
|
__glvndWinsysDispatchAllocIndex(
|
|
LOCAL_GLX_DISPATCH_FUNCTIONS[i].name,
|
|
LOCAL_GLX_DISPATCH_FUNCTIONS[i].addr);
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* This handles freeing all mapping state during library teardown
|
|
* or resetting locks on fork recovery.
|
|
*/
|
|
void __glXMappingTeardown(Bool doReset)
|
|
{
|
|
|
|
if (doReset) {
|
|
__GLXdisplayInfoHash *dpyInfoEntry, *dpyInfoTmp;
|
|
|
|
/*
|
|
* If we're just doing fork recovery, we don't actually want to unload
|
|
* any currently loaded vendors _or_ remove any mappings (they should
|
|
* still be valid in the new process, and may be needed if the child
|
|
* tries using pointers/XIDs that were created in the parent). Just
|
|
* reset the corresponding locks.
|
|
*/
|
|
__glvndPthreadFuncs.rwlock_init(&fbconfigHashtable.lock, NULL);
|
|
__glvndPthreadFuncs.rwlock_init(&__glXVendorNameHash.lock, NULL);
|
|
__glvndPthreadFuncs.rwlock_init(&__glXDisplayInfoHash.lock, NULL);
|
|
|
|
HASH_ITER(hh, _LH(__glXDisplayInfoHash), dpyInfoEntry, dpyInfoTmp) {
|
|
__glvndPthreadFuncs.rwlock_init(&dpyInfoEntry->info.xidVendorHash.lock, NULL);
|
|
__glvndPthreadFuncs.rwlock_init(&dpyInfoEntry->info.vendorLock, NULL);
|
|
}
|
|
} else {
|
|
__GLXvendorNameHash *pEntry, *tmp;
|
|
|
|
/* Tear down all hashtables used in this file */
|
|
__glvndWinsysDispatchCleanup();
|
|
|
|
// If a GLX vendor library has patched the OpenGL entrypoints, then
|
|
// unpatch them before we unload the vendors.
|
|
LKDHASH_RDLOCK(__glXVendorNameHash);
|
|
HASH_ITER(hh, _LH(__glXVendorNameHash), pEntry, tmp) {
|
|
__glDispatchForceUnpatch(pEntry->vendor.vendorID);
|
|
}
|
|
LKDHASH_UNLOCK(__glXVendorNameHash);
|
|
|
|
LKDHASH_TEARDOWN(__GLXvendorConfigMappingHash,
|
|
fbconfigHashtable, NULL, NULL, False);
|
|
|
|
LKDHASH_TEARDOWN(__GLXdisplayInfoHash,
|
|
__glXDisplayInfoHash, CleanupDisplayInfoEntry,
|
|
NULL, False);
|
|
/*
|
|
* This implicitly unloads vendor libraries that were loaded when
|
|
* they were added to this hashtable.
|
|
*/
|
|
LKDHASH_TEARDOWN(__GLXvendorNameHash,
|
|
__glXVendorNameHash, CleanupVendorNameEntry,
|
|
NULL, False);
|
|
|
|
/* Free any generated entrypoints */
|
|
glvndFreeEntrypoints();
|
|
}
|
|
|
|
}
|