libglvnd/src/GLX/libglxmapping.c

455 lines
12 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.
*
* If only executable code is distributed, then the accompanying
* documentation must state that "this software is based in part on the
* work of the Khronos Group."
*
* 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>
#if defined(HASH_DEBUG)
# include <stdio.h>
#endif
2013-08-23 01:06:53 +02:00
#include "libglxmapping.h"
#include "libglxnoop.h"
#include "libglxthread.h"
#include "trace.h"
2013-08-23 01:06:53 +02:00
#include "lkdhash.h"
#define _GNU_SOURCE 1
#include <assert.h>
#include <stdio.h>
2013-08-23 01:06:53 +02:00
/*
* __glXVendorScreenHash is a hash table which maps a Display+screen to a vendor.
* Look up this mapping from the X server once, the first time a unique
* Display+screen pair is seen.
*/
typedef struct {
Display *dpy;
int screen;
} __GLXvendorScreenHashKey;
typedef struct __GLXvendorScreenHashRec {
__GLXvendorScreenHashKey key;
__GLXvendorInfo *vendor;
// XXX for performance reasons we may want to stash the dispatch tables here
// as well
UT_hash_handle hh;
} __GLXvendorScreenHash;
static DEFINE_INITIALIZED_LKDHASH(__GLXvendorScreenHash, __glXVendorScreenHash);
/*
* __glXVendorNameHash is a hash table mapping a vendor name to vendor info.
*/
typedef struct __GLXvendorNameHashRec {
const char *name;
__GLXvendorInfo *vendor;
UT_hash_handle hh;
} __GLXvendorNameHash;
static DEFINE_INITIALIZED_LKDHASH(__GLXvendorNameHash, __glXVendorNameHash);
2013-08-23 01:06:53 +02:00
/*
* 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.
2013-08-23 01:06:53 +02:00
*/
__GLXextFuncPtr __glXGetGLXDispatchAddress(const GLubyte *procName)
2013-08-23 01:06:53 +02:00
{
__GLXextFuncPtr addr = NULL;
__GLXvendorNameHash *pEntry, *tmp;
/*
* XXX for full correctness, we should probably load vendors
* on all screens up-front before doing this. However, that
* might be bad for performance?
*/
LKDHASH_RDLOCK(__glXPthreadFuncs, __glXVendorNameHash);
HASH_ITER(hh, _LH(__glXVendorNameHash), pEntry, tmp) {
// See if the current vendor supports this GLX entry point
addr = pEntry->vendor->staticDispatch->
glxvc.getDispatchAddress(procName);
if (addr) {
// TODO Allocate the new dispatch index.
break;
}
}
LKDHASH_UNLOCK(__glXPthreadFuncs, __glXVendorNameHash);
return addr;
}
static __GLXapiExports glxExportsTable = {
.getDynDispatch = __glXGetDynDispatch,
.fetchDispatchEntry = NULL,
/* We use the real function since __glXGetCurrentContext is inline */
.getCurrentContext = glXGetCurrentContext,
/* GL dispatch management TODO */
.getCurrentGLDispatch = NULL,
.getTopLevelDispatch = NULL,
.createGLDispatch = NULL,
.getGLDispatchOffset = NULL,
.makeGLDispatchCurrent = NULL,
.destroyGLDispatch = NULL
};
static char *ConstructVendorLibraryFilename(const char *vendorName)
{
char *filename;
int ret;
ret = asprintf(&filename, "libGLX_%s.so.0", vendorName);
if (ret < 0) {
return NULL;
}
return filename;
2013-08-23 01:06:53 +02:00
}
__GLXvendorInfo *__glXLookupVendorByName(const char *vendorName)
2013-08-23 01:06:53 +02:00
{
__GLXvendorNameHash *pEntry = NULL;
char *filename;
void *dlhandle = NULL;
__PFNGLXMAINPROC glxMainProc;
const __GLXdispatchTableStatic *dispatch;
__GLXvendorInfo *vendor = NULL;
Bool locked = False;
LKDHASH_RDLOCK(__glXPthreadFuncs, __glXVendorNameHash);
HASH_FIND(hh, _LH(__glXVendorNameHash), vendorName, strlen(vendorName), pEntry);
if (pEntry) {
vendor = pEntry->vendor;
}
LKDHASH_UNLOCK(__glXPthreadFuncs, __glXVendorNameHash);
if (!pEntry) {
LKDHASH_WRLOCK(__glXPthreadFuncs, __glXVendorNameHash);
locked = True;
// Do another lookup to check uniqueness
HASH_FIND(hh, _LH(__glXVendorNameHash), vendorName, strlen(vendorName), pEntry);
if (!pEntry) {
// Previously unseen vendor. dlopen() the new vendor and add it to the
// hash table.
pEntry = calloc(1, sizeof(*pEntry));
if (!pEntry) {
goto fail;
}
filename = ConstructVendorLibraryFilename(vendorName);
dlhandle = dlopen(filename, RTLD_LAZY);
free(filename);
if (!dlhandle) {
goto fail;
}
glxMainProc = dlsym(dlhandle, __GLX_MAIN_PROTO_NAME);
if (!glxMainProc) {
goto fail;
}
dispatch = (*glxMainProc)(GLX_VENDOR_ABI_VERSION,
&glxExportsTable,
vendorName);
if (!dispatch) {
goto fail;
}
vendor = pEntry->vendor
= calloc(1, sizeof(__GLXvendorInfo));
if (!vendor) {
goto fail;
}
pEntry->name = vendor->name = strdup(vendorName);
if (!vendor->name) {
goto fail;
}
vendor->dlhandle = dlhandle;
vendor->staticDispatch = dispatch;
/* TODO: create a core GL dispatch table */
vendor->glDispatch = NULL;
if (!vendor->glDispatch) {
goto fail;
}
/* TODO: create a dynamic GLX dispatch table */
vendor->dynDispatch = NULL;
HASH_ADD_KEYPTR(hh, _LH(__glXVendorNameHash), vendorName,
strlen(vendorName), pEntry);
} else {
/* Some other thread added a vendor */
vendor = pEntry->vendor;
}
LKDHASH_UNLOCK(__glXPthreadFuncs, __glXVendorNameHash);
}
return vendor;
fail:
if (locked) {
LKDHASH_UNLOCK(__glXPthreadFuncs, __glXVendorNameHash);
}
if (dlhandle) {
dlclose(dlhandle);
}
if (vendor) {
free(vendor->name);
if (vendor->glDispatch) {
// TODO: free the table
}
free(vendor->dynDispatch);
}
if (pEntry) {
free(pEntry->vendor);
}
free(pEntry);
return NULL;
2013-08-23 01:06:53 +02:00
}
__GLXvendorInfo *__glXLookupVendorByScreen(Display *dpy, const int screen)
{
__GLXvendorInfo *vendor = NULL;
__GLXvendorScreenHash *pEntry = NULL;
__GLXvendorScreenHashKey key;
if (screen < 0) {
return NULL;
}
memset(&key, 0, sizeof(key));
key.dpy = dpy;
key.screen = screen;
LKDHASH_RDLOCK(__glXPthreadFuncs, __glXVendorScreenHash);
HASH_FIND(hh, _LH(__glXVendorScreenHash), &key,
sizeof(key), pEntry);
if (pEntry) {
vendor = pEntry->vendor;
}
LKDHASH_UNLOCK(__glXPthreadFuncs, __glXVendorScreenHash);
if (!pEntry) {
/*
* If we have specified a vendor library, use that. Otherwise,
* try to lookup the vendor based on the current screen.
*/
const char *preloadedVendorName = getenv("__GLX_VENDOR_LIBRARY_NAME");
char *queriedVendorName;
assert(!vendor);
if (preloadedVendorName) {
vendor = __glXLookupVendorByName(preloadedVendorName);
}
if (!vendor) {
queriedVendorName = NULL; // TODO: query X somehow for the name
vendor = __glXLookupVendorByName(queriedVendorName);
Xfree(queriedVendorName);
}
if (!vendor) {
assert(!"Missing vendor library!");
}
LKDHASH_WRLOCK(__glXPthreadFuncs, __glXVendorScreenHash);
HASH_FIND(hh, _LH(__glXVendorScreenHash), &key, sizeof(key), pEntry);
if (!pEntry) {
pEntry = malloc(sizeof(*pEntry));
if (!pEntry) {
return NULL;
}
pEntry->key.dpy = dpy;
pEntry->key.screen = screen;
pEntry->vendor = vendor;
HASH_ADD(hh, _LH(__glXVendorScreenHash), key,
sizeof(__GLXvendorScreenHashKey), pEntry);
} else {
/* Some other thread already added a vendor */
vendor = pEntry->vendor;
}
LKDHASH_UNLOCK(__glXPthreadFuncs, __glXVendorScreenHash);
}
DBG_PRINTF(10, "Found vendor \"%s\" for screen %d\n",
vendor->name, screen);
return vendor;
}
2013-08-23 01:06:53 +02:00
const __GLXdispatchTableStatic *__glXGetStaticDispatch(Display *dpy, const int screen)
2013-08-23 01:06:53 +02:00
{
__GLXvendorInfo *vendor = __glXLookupVendorByScreen(dpy, screen);
2013-08-23 01:06:53 +02:00
if (vendor) {
assert(vendor->staticDispatch);
return vendor->staticDispatch;
} else {
2013-08-23 01:06:53 +02:00
return __glXDispatchNoopPtr;
}
}
void *__glXGetGLDispatch(Display *dpy, const int screen)
2013-08-23 01:06:53 +02:00
{
__GLXvendorInfo *vendor = __glXLookupVendorByScreen(dpy, screen);
2013-08-23 01:06:53 +02:00
if (vendor) {
assert(vendor->glDispatch);
return vendor->glDispatch;
} else {
return NULL;
2013-08-23 01:06:53 +02:00
}
}
2013-08-23 01:06:53 +02:00
__GLXdispatchTableDynamic *__glXGetDynDispatch(Display *dpy, const int screen)
{
__GLXvendorInfo *vendor = __glXLookupVendorByScreen(dpy, screen);
2013-08-23 01:06:53 +02:00
if (vendor) {
assert(vendor->dynDispatch);
return vendor->dynDispatch;
2013-08-23 01:06:53 +02:00
} else {
return NULL;
2013-08-23 01:06:53 +02:00
}
}
2013-08-23 01:06:53 +02:00
static void AddScreenPointerMapping(void *ptr, int screen)
{
// TODO
2013-08-23 01:06:53 +02:00
}
static void RemoveScreenPointerMapping(void *ptr, int screen)
{
// TODO
2013-08-23 01:06:53 +02:00
}
static int ScreenFromPointer(void *ptr)
{
// TODO
return -1;
2013-08-23 01:06:53 +02:00
}
void __glXAddScreenContextMapping(GLXContext context, int screen)
{
AddScreenPointerMapping(context, screen);
}
void __glXRemoveScreenContextMapping(GLXContext context, int screen)
{
RemoveScreenPointerMapping(context, screen);
}
int __glXScreenFromContext(GLXContext context)
{
return ScreenFromPointer(context);
}
void __glXAddScreenFBConfigMapping(GLXFBConfig config, int screen)
{
AddScreenPointerMapping(config, screen);
}
void __glXRemoveScreenFBConfigMapping(GLXFBConfig config, int screen)
{
RemoveScreenPointerMapping(config, screen);
}
int __glXScreenFromFBConfig(GLXFBConfig config)
{
return ScreenFromPointer(config);
}
/****************************************************************************/
static void AddScreenXIDMapping(XID xid, int screen)
{
// TODO
2013-08-23 01:06:53 +02:00
}
static void RemoveScreenXIDMapping(XID xid, int screen)
{
// TODO
2013-08-23 01:06:53 +02:00
}
static int ScreenFromXID(Display *dpy, XID xid)
2013-08-23 01:06:53 +02:00
{
return -1; // TODO
2013-08-23 01:06:53 +02:00
}
void __glXAddScreenDrawableMapping(GLXDrawable drawable, int screen)
{
AddScreenXIDMapping(drawable, screen);
}
void __glXRemoveScreenDrawableMapping(GLXDrawable drawable, int screen)
{
RemoveScreenXIDMapping(drawable, screen);
}
int __glXScreenFromDrawable(Display *dpy, GLXDrawable drawable)
2013-08-23 01:06:53 +02:00
{
return ScreenFromXID(dpy, drawable);
2013-08-23 01:06:53 +02:00
}