367 lines
10 KiB
C
367 lines
10 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 <pthread.h>
|
|
#include <string.h>
|
|
|
|
#if defined(HASH_DEBUG)
|
|
# include <stdio.h>
|
|
#endif
|
|
|
|
#include "libeglcurrent.h"
|
|
#include "libeglmapping.h"
|
|
#include "glvnd_pthread.h"
|
|
#include "egldispatchstubs.h"
|
|
#include "utils_misc.h"
|
|
#include "trace.h"
|
|
|
|
#include "lkdhash.h"
|
|
|
|
static glvnd_mutex_t dispatchIndexMutex = GLVND_MUTEX_INITIALIZER;
|
|
|
|
__EGLdeviceInfo *__eglDeviceList = NULL;
|
|
__EGLdeviceInfo *__eglDeviceHash = NULL;
|
|
int __eglDeviceCount = 0;
|
|
static glvnd_once_t deviceListInitOnce = GLVND_ONCE_INIT;
|
|
|
|
/****************************************************************************/
|
|
|
|
typedef struct __EGLdisplayInfoHashRec {
|
|
__EGLdisplayInfo info;
|
|
UT_hash_handle hh;
|
|
} __EGLdisplayInfoHash;
|
|
|
|
static DEFINE_INITIALIZED_LKDHASH(__EGLdisplayInfoHash, __eglDisplayInfoHash);
|
|
|
|
__eglMustCastToProperFunctionPointerType __eglGetEGLDispatchAddress(const char *procName)
|
|
{
|
|
struct glvnd_list *vendorList = __eglLoadVendors();
|
|
__EGLvendorInfo *vendor;
|
|
__eglMustCastToProperFunctionPointerType addr = NULL;
|
|
int index;
|
|
|
|
__glvndPthreadFuncs.mutex_lock(&dispatchIndexMutex);
|
|
|
|
index = __glvndWinsysDispatchFindIndex(procName);
|
|
if (index >= 0) {
|
|
addr = (__eglMustCastToProperFunctionPointerType) __glvndWinsysDispatchGetDispatch(index);
|
|
__glvndPthreadFuncs.mutex_unlock(&dispatchIndexMutex);
|
|
return addr;
|
|
}
|
|
|
|
// Check each vendor library for a dispatch stub.
|
|
glvnd_list_for_each_entry(vendor, vendorList, entry) {
|
|
addr = vendor->eglvc.getDispatchAddress(procName);
|
|
if (addr != NULL) {
|
|
break;
|
|
}
|
|
}
|
|
if (addr != NULL) {
|
|
index = __glvndWinsysDispatchAllocIndex(procName, addr);
|
|
if (index >= 0) {
|
|
glvnd_list_for_each_entry(vendor, vendorList, entry) {
|
|
vendor->eglvc.setDispatchIndex(procName, index);
|
|
}
|
|
} else {
|
|
addr = NULL;
|
|
}
|
|
}
|
|
|
|
__glvndPthreadFuncs.mutex_unlock(&dispatchIndexMutex);
|
|
return addr;
|
|
}
|
|
|
|
__eglMustCastToProperFunctionPointerType __eglFetchDispatchEntry(
|
|
__EGLvendorInfo *vendor, int index)
|
|
{
|
|
__eglMustCastToProperFunctionPointerType addr = NULL;
|
|
const char *procName = NULL;
|
|
|
|
addr = (__eglMustCastToProperFunctionPointerType)
|
|
__glvndWinsysVendorDispatchLookupFunc(vendor->dynDispatch, index);
|
|
if (addr != NULL) {
|
|
return addr;
|
|
}
|
|
|
|
// Not seen before by this vendor: query the vendor for the right
|
|
// address to use.
|
|
|
|
__glvndPthreadFuncs.mutex_lock(&dispatchIndexMutex);
|
|
procName = __glvndWinsysDispatchGetName(index);
|
|
__glvndPthreadFuncs.mutex_unlock(&dispatchIndexMutex);
|
|
|
|
if (procName == NULL) {
|
|
// Not a valid function index.
|
|
return NULL;
|
|
}
|
|
|
|
// Get the real address.
|
|
addr = vendor->eglvc.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;
|
|
}
|
|
|
|
__EGLvendorInfo *__eglGetVendorFromDisplay(EGLDisplay dpy)
|
|
{
|
|
__EGLdisplayInfo *dpyInfo = __eglLookupDisplay(dpy);
|
|
if (dpyInfo != NULL) {
|
|
return dpyInfo->vendor;
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Allocates and initializes a __EGLdisplayInfoHash structure.
|
|
*
|
|
* The caller is responsible for adding the structure to the hashtable.
|
|
*
|
|
* \param dpy The display connection.
|
|
* \return A newly-allocated __EGLdisplayInfoHash structure, or NULL on error.
|
|
*/
|
|
static __EGLdisplayInfoHash *InitDisplayInfoEntry(EGLDisplay dpy, __EGLvendorInfo *vendor)
|
|
{
|
|
__EGLdisplayInfoHash *pEntry;
|
|
|
|
pEntry = (__EGLdisplayInfoHash *) calloc(1, sizeof(*pEntry));
|
|
if (pEntry == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
pEntry->info.dpy = dpy;
|
|
pEntry->info.vendor = vendor;
|
|
|
|
return pEntry;
|
|
}
|
|
|
|
__EGLdisplayInfo *__eglLookupDisplay(EGLDisplay dpy)
|
|
{
|
|
__EGLdisplayInfoHash *pEntry = NULL;
|
|
|
|
if (dpy == EGL_NO_DISPLAY) {
|
|
return NULL;
|
|
}
|
|
|
|
LKDHASH_RDLOCK(__eglDisplayInfoHash);
|
|
HASH_FIND_PTR(_LH(__eglDisplayInfoHash), &dpy, pEntry);
|
|
LKDHASH_UNLOCK(__eglDisplayInfoHash);
|
|
|
|
if (pEntry != NULL) {
|
|
return &pEntry->info;
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
__EGLdisplayInfo *__eglAddDisplay(EGLDisplay dpy, __EGLvendorInfo *vendor)
|
|
{
|
|
__EGLdisplayInfoHash *pEntry = NULL;
|
|
|
|
if (dpy == EGL_NO_DISPLAY) {
|
|
return NULL;
|
|
}
|
|
|
|
LKDHASH_WRLOCK(__eglDisplayInfoHash);
|
|
HASH_FIND_PTR(_LH(__eglDisplayInfoHash), &dpy, pEntry);
|
|
if (pEntry == NULL) {
|
|
pEntry = InitDisplayInfoEntry(dpy, vendor);
|
|
if (pEntry != NULL) {
|
|
HASH_ADD_PTR(_LH(__eglDisplayInfoHash), info.dpy, pEntry);
|
|
}
|
|
}
|
|
|
|
LKDHASH_UNLOCK(__eglDisplayInfoHash);
|
|
if (pEntry != NULL && pEntry->info.vendor == vendor) {
|
|
return &pEntry->info;
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
void __eglFreeDisplay(EGLDisplay dpy)
|
|
{
|
|
__EGLdisplayInfoHash *pEntry = NULL;
|
|
|
|
LKDHASH_WRLOCK(__eglDisplayInfoHash);
|
|
HASH_FIND_PTR(_LH(__eglDisplayInfoHash), &dpy, pEntry);
|
|
if (pEntry != NULL) {
|
|
HASH_DEL(_LH(__eglDisplayInfoHash), pEntry);
|
|
}
|
|
LKDHASH_UNLOCK(__eglDisplayInfoHash);
|
|
|
|
if (pEntry != NULL) {
|
|
free(pEntry);
|
|
}
|
|
}
|
|
|
|
void __eglMappingInit(void)
|
|
{
|
|
int i;
|
|
__eglInitDispatchStubs(&__eglExportsTable);
|
|
for (i=0; i<__EGL_DISPATCH_FUNC_COUNT; i++) {
|
|
int index = __glvndWinsysDispatchAllocIndex(
|
|
__EGL_DISPATCH_FUNC_NAMES[i],
|
|
__EGL_DISPATCH_FUNCS[i]);
|
|
if (index < 0) {
|
|
fprintf(stderr, "Could not allocate dispatch index array\n");
|
|
abort();
|
|
}
|
|
__EGL_DISPATCH_FUNC_INDICES[i] = index;
|
|
}
|
|
}
|
|
|
|
void __eglMappingTeardown(EGLBoolean doReset)
|
|
{
|
|
if (doReset) {
|
|
//__EGLdisplayInfoHash *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.mutex_init(&dispatchIndexMutex, NULL);
|
|
__glvndPthreadFuncs.rwlock_init(&__eglDisplayInfoHash.lock, NULL);
|
|
} else {
|
|
/* Tear down all hashtables used in this file */
|
|
LKDHASH_TEARDOWN(__EGLdisplayInfoHash,
|
|
__eglDisplayInfoHash, NULL, NULL, EGL_FALSE);
|
|
|
|
__glvndWinsysDispatchCleanup();
|
|
}
|
|
}
|
|
|
|
static EGLBoolean AddVendorDevices(__EGLvendorInfo *vendor)
|
|
{
|
|
EGLDeviceEXT *devices = NULL;
|
|
EGLint count = 0;
|
|
__EGLdeviceInfo *newDevList;
|
|
EGLint i, j;
|
|
|
|
if (!vendor->supportsDevice) {
|
|
return EGL_TRUE;
|
|
}
|
|
|
|
if (!vendor->staticDispatch.queryDevicesEXT(0, NULL, &count)) {
|
|
// Even if this vendor fails, we can still return the devices from any
|
|
// other vendors
|
|
return EGL_TRUE;
|
|
}
|
|
if (count <= 0) {
|
|
return EGL_TRUE;
|
|
}
|
|
|
|
devices = (EGLDeviceEXT *) malloc(count * sizeof(EGLDeviceEXT));
|
|
if (devices == NULL) {
|
|
return EGL_FALSE;
|
|
}
|
|
|
|
if (!vendor->staticDispatch.queryDevicesEXT(count, devices, &count)) {
|
|
free(devices);
|
|
return EGL_FALSE;
|
|
}
|
|
|
|
newDevList = (__EGLdeviceInfo *) realloc(__eglDeviceList,
|
|
(__eglDeviceCount + count) * sizeof(__EGLdeviceInfo));
|
|
if (newDevList == NULL) {
|
|
free(devices);
|
|
return EGL_FALSE;
|
|
}
|
|
__eglDeviceList = newDevList;
|
|
|
|
for (i=0; i<count; i++) {
|
|
// Make sure we haven't already gotten a device with this handle.
|
|
EGLBoolean found = EGL_FALSE;
|
|
for (j=0; j<__eglDeviceCount; j++) {
|
|
if (__eglDeviceList[j].handle == devices[i]) {
|
|
found = EGL_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
__eglDeviceList[__eglDeviceCount].handle = devices[i];
|
|
__eglDeviceList[__eglDeviceCount].vendor = vendor;
|
|
__eglDeviceCount++;
|
|
}
|
|
}
|
|
free(devices);
|
|
|
|
return EGL_TRUE;
|
|
}
|
|
|
|
void InitDeviceListInternal(void)
|
|
{
|
|
struct glvnd_list *vendorList = __eglLoadVendors();
|
|
__EGLvendorInfo *vendor;
|
|
EGLint i;
|
|
|
|
__eglDeviceList = NULL;
|
|
__eglDeviceHash = NULL;
|
|
__eglDeviceCount = 0;
|
|
|
|
glvnd_list_for_each_entry(vendor, vendorList, entry) {
|
|
if (!AddVendorDevices(vendor)) {
|
|
free(__eglDeviceList);
|
|
__eglDeviceList = NULL;
|
|
__eglDeviceCount = 0;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Build a hashtable for the devices.
|
|
for (i=0; i<__eglDeviceCount; i++) {
|
|
__EGLdeviceInfo *dev = &__eglDeviceList[i];
|
|
HASH_ADD_PTR(__eglDeviceHash, handle, dev);
|
|
}
|
|
}
|
|
|
|
void __eglInitDeviceList(void)
|
|
{
|
|
__glvndPthreadFuncs.once(&deviceListInitOnce, InitDeviceListInternal);
|
|
}
|
|
|
|
__EGLvendorInfo *__eglGetVendorFromDevice(EGLDeviceEXT dev)
|
|
{
|
|
__EGLdeviceInfo *devInfo;
|
|
|
|
__eglInitDeviceList();
|
|
|
|
HASH_FIND_PTR(__eglDeviceHash, &dev, devInfo);
|
|
if (devInfo != NULL) {
|
|
return devInfo->vendor;
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|