libglvnd/src/GLX/libglxmapping.c

1110 lines
36 KiB
C
Raw Normal View History

2013-08-23 01:06:53 +02:00
/*
* 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.
2013-08-23 01:06:53 +02:00
*/
#include <X11/Xlibint.h>
#include <pthread.h>
#include <dlfcn.h>
#include <string.h>
#if defined(HASH_DEBUG)
# include <stdio.h>
#endif
Simplify vendor-library ABI for libglx - Pull GL dispatch callback typedefs into GLdispatchABI.h. This is a common header which will be shared between the libGLX and libEGL ABIs. - Add extern "C" blocks to libglxabi.h and GLdispatchABI.h so these headers may be used with vendor libraries that are built C++. - Remove support for auxiliary dispatch table management (libglxgldispatch.[ch] and __GLdispatchExports). This can be added back in later on if it proves useful. - In libglxmappings.c, replace static initialization of glxExportsTable with one-time runtime initialization of the table. - In libglxmappings.c, use __glDispatchCreateTable() directly to create the initial dispatch table. - In libglxnoopdefs.h, remove unnecessary includes of libglxabipriv.h and libglxnoop.h. - Add screen mapping functions to the GLX exports table. Some extension functions not implemented by libglvnd, e.g. glXCreateContextAttribsARB() from GLX_ARB_create_context, may need to update the mappings from context or drawable to screen that are managed by libglvnd in order to work properly. Additionally, dispatch functions implemented by vendor libraries for GLX extension functions may want to query these mappings in order to properly dispatch the call. - Remove the getDispatchProto() vendor callback. This is not needed by libglvnd since libglvnd does not support mapping more than one entrypoint to a single dispatch stub. See GLdispatch.c for the rationale behind this decision. - Remove redundant typedefs to prevent build errors. - Delete the vendorData parameter from the getProcAddress() vendor callback, which isn't useful if there is no support for more than one libglvnd dispatch table per context. Add the isClientAPI parameter, which can be used to distinguish between getProcAddress() requests for GLX functions versus getProcAddress() requests for client API functions. - Remove the "screen" argument from Remove.*Mapping() functions. This is unnecessary, as the hash key used to lookup the mapping object in libglvnd's hashtables doesn't depend on the screen (which is the hash value). It's problematic since in some cases we don't actually know the value of the screen mapped to by a key. Signed-off-by: Brian Nguyen <brnguyen@nvidia.com>
2013-11-26 00:21:41 +01:00
#include "libglxcurrent.h"
2013-08-23 01:06:53 +02:00
#include "libglxmapping.h"
#include "libglxthread.h"
#include "libglxproto.h"
#include "utils_misc.h"
#include "glvnd_genentry.h"
#include "trace.h"
#include "winsys_dispatch.h"
2013-08-23 01:06:53 +02:00
#include "lkdhash.h"
#define _GNU_SOURCE 1
2013-08-23 01:06:53 +02:00
#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.
2013-08-23 01:06:53 +02:00
*/
__GLXextFuncPtr __glXGetGLXDispatchAddress(const GLubyte *procName)
2013-08-23 01:06:53 +02:00
{
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.
Simplify vendor-library ABI for libglx - Pull GL dispatch callback typedefs into GLdispatchABI.h. This is a common header which will be shared between the libGLX and libEGL ABIs. - Add extern "C" blocks to libglxabi.h and GLdispatchABI.h so these headers may be used with vendor libraries that are built C++. - Remove support for auxiliary dispatch table management (libglxgldispatch.[ch] and __GLdispatchExports). This can be added back in later on if it proves useful. - In libglxmappings.c, replace static initialization of glxExportsTable with one-time runtime initialization of the table. - In libglxmappings.c, use __glDispatchCreateTable() directly to create the initial dispatch table. - In libglxnoopdefs.h, remove unnecessary includes of libglxabipriv.h and libglxnoop.h. - Add screen mapping functions to the GLX exports table. Some extension functions not implemented by libglvnd, e.g. glXCreateContextAttribsARB() from GLX_ARB_create_context, may need to update the mappings from context or drawable to screen that are managed by libglvnd in order to work properly. Additionally, dispatch functions implemented by vendor libraries for GLX extension functions may want to query these mappings in order to properly dispatch the call. - Remove the getDispatchProto() vendor callback. This is not needed by libglvnd since libglvnd does not support mapping more than one entrypoint to a single dispatch stub. See GLdispatch.c for the rationale behind this decision. - Remove redundant typedefs to prevent build errors. - Delete the vendorData parameter from the getProcAddress() vendor callback, which isn't useful if there is no support for more than one libglvnd dispatch table per context. Add the isClientAPI parameter, which can be used to distinguish between getProcAddress() requests for GLX functions versus getProcAddress() requests for client API functions. - Remove the "screen" argument from Remove.*Mapping() functions. This is unnecessary, as the hash key used to lookup the mapping object in libglvnd's hashtables doesn't depend on the screen (which is the hash value). It's problematic since in some cases we don't actually know the value of the screen mapped to by a key. Signed-off-by: Brian Nguyen <brnguyen@nvidia.com>
2013-11-26 00:21:41 +01:00
*
* 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;
2013-08-23 01:06:53 +02:00
}
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)
2013-08-23 01:06:53 +02:00
{
__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 *)
Simplify vendor-library ABI for libglx - Pull GL dispatch callback typedefs into GLdispatchABI.h. This is a common header which will be shared between the libGLX and libEGL ABIs. - Add extern "C" blocks to libglxabi.h and GLdispatchABI.h so these headers may be used with vendor libraries that are built C++. - Remove support for auxiliary dispatch table management (libglxgldispatch.[ch] and __GLdispatchExports). This can be added back in later on if it proves useful. - In libglxmappings.c, replace static initialization of glxExportsTable with one-time runtime initialization of the table. - In libglxmappings.c, use __glDispatchCreateTable() directly to create the initial dispatch table. - In libglxnoopdefs.h, remove unnecessary includes of libglxabipriv.h and libglxnoop.h. - Add screen mapping functions to the GLX exports table. Some extension functions not implemented by libglvnd, e.g. glXCreateContextAttribsARB() from GLX_ARB_create_context, may need to update the mappings from context or drawable to screen that are managed by libglvnd in order to work properly. Additionally, dispatch functions implemented by vendor libraries for GLX extension functions may want to query these mappings in order to properly dispatch the call. - Remove the getDispatchProto() vendor callback. This is not needed by libglvnd since libglvnd does not support mapping more than one entrypoint to a single dispatch stub. See GLdispatch.c for the rationale behind this decision. - Remove redundant typedefs to prevent build errors. - Delete the vendorData parameter from the getProcAddress() vendor callback, which isn't useful if there is no support for more than one libglvnd dispatch table per context. Add the isClientAPI parameter, which can be used to distinguish between getProcAddress() requests for GLX functions versus getProcAddress() requests for client API functions. - Remove the "screen" argument from Remove.*Mapping() functions. This is unnecessary, as the hash key used to lookup the mapping object in libglvnd's hashtables doesn't depend on the screen (which is the hash value). It's problematic since in some cases we don't actually know the value of the screen mapped to by a key. Signed-off-by: Brian Nguyen <brnguyen@nvidia.com>
2013-11-26 00:21:41 +01:00
__glDispatchCreateTable(
VendorGetProcAddressCallback,
vendor
Simplify vendor-library ABI for libglx - Pull GL dispatch callback typedefs into GLdispatchABI.h. This is a common header which will be shared between the libGLX and libEGL ABIs. - Add extern "C" blocks to libglxabi.h and GLdispatchABI.h so these headers may be used with vendor libraries that are built C++. - Remove support for auxiliary dispatch table management (libglxgldispatch.[ch] and __GLdispatchExports). This can be added back in later on if it proves useful. - In libglxmappings.c, replace static initialization of glxExportsTable with one-time runtime initialization of the table. - In libglxmappings.c, use __glDispatchCreateTable() directly to create the initial dispatch table. - In libglxnoopdefs.h, remove unnecessary includes of libglxabipriv.h and libglxnoop.h. - Add screen mapping functions to the GLX exports table. Some extension functions not implemented by libglvnd, e.g. glXCreateContextAttribsARB() from GLX_ARB_create_context, may need to update the mappings from context or drawable to screen that are managed by libglvnd in order to work properly. Additionally, dispatch functions implemented by vendor libraries for GLX extension functions may want to query these mappings in order to properly dispatch the call. - Remove the getDispatchProto() vendor callback. This is not needed by libglvnd since libglvnd does not support mapping more than one entrypoint to a single dispatch stub. See GLdispatch.c for the rationale behind this decision. - Remove redundant typedefs to prevent build errors. - Delete the vendorData parameter from the getProcAddress() vendor callback, which isn't useful if there is no support for more than one libglvnd dispatch table per context. Add the isClientAPI parameter, which can be used to distinguish between getProcAddress() requests for GLX functions versus getProcAddress() requests for client API functions. - Remove the "screen" argument from Remove.*Mapping() functions. This is unnecessary, as the hash key used to lookup the mapping object in libglvnd's hashtables doesn't depend on the screen (which is the hash value). It's problematic since in some cases we don't actually know the value of the screen mapped to by a key. Signed-off-by: Brian Nguyen <brnguyen@nvidia.com>
2013-11-26 00:21:41 +01:00
);
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;
2013-08-23 01:06:53 +02:00
}
__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;
}
2013-08-23 01:06:53 +02:00
__GLXvendorInfo *__glXGetDynDispatch(Display *dpy, const int screen)
{
__glXThreadInitialize();
__GLXvendorInfo *vendor = __glXLookupVendorByScreen(dpy, screen);
return vendor;
}
2013-08-23 01:06:53 +02:00
/**
* 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;
2013-08-23 01:06:53 +02:00
}
void __glXRemoveVendorFBConfigMapping(Display *dpy, GLXFBConfig config)
2013-08-23 01:06:53 +02:00
{
__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);
2013-08-23 01:06:53 +02:00
}
__GLXvendorInfo *__glXVendorFromFBConfig(Display *dpy, GLXFBConfig config)
2013-08-23 01:06:53 +02:00
{
__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);
2013-08-23 01:06:53 +02:00
return vendor;
}
2013-08-23 01:06:53 +02:00
/****************************************************************************/
/*
* __GLXvendorXIDMappingHash is a hash table which maps XIDs to vendors.
*/
static int AddVendorXIDMapping(Display *dpy, __GLXdisplayInfo *dpyInfo, XID xid, __GLXvendorInfo *vendor)
2013-08-23 01:06:53 +02:00
{
__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;
2013-08-23 01:06:53 +02:00
}
static void RemoveVendorXIDMapping(Display *dpy, __GLXdisplayInfo *dpyInfo, XID xid)
2013-08-23 01:06:53 +02:00
{
__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);
2013-08-23 01:06:53 +02:00
}
static void VendorFromXID(Display *dpy, __GLXdisplayInfo *dpyInfo, XID xid,
__GLXvendorInfo **retVendor)
2013-08-23 01:06:53 +02:00
{
__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;
}
2013-08-23 01:06:53 +02:00
}
int __glXAddVendorDrawableMapping(Display *dpy, GLXDrawable drawable, __GLXvendorInfo *vendor)
2013-08-23 01:06:53 +02:00
{
__GLXdisplayInfo *dpyInfo = __glXLookupDisplay(dpy);
if (dpyInfo != NULL) {
return AddVendorXIDMapping(dpy, dpyInfo, drawable, vendor);
} else {
return -1;
}
2013-08-23 01:06:53 +02:00
}
void __glXRemoveVendorDrawableMapping(Display *dpy, GLXDrawable drawable)
2013-08-23 01:06:53 +02:00
{
__GLXdisplayInfo *dpyInfo = __glXLookupDisplay(dpy);
if (dpyInfo != NULL) {
RemoveVendorXIDMapping(dpy, dpyInfo, drawable);
}
2013-08-23 01:06:53 +02:00
}
__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();
}
}