EGL: Allow adding EGLDeviceEXT handles

It's possible for new devices to become available while a program is running,
so eglQueryDevicesEXT might need to return a different list.

Change the EGLDeviceEXT hashtable so that each entry is allocated and added
separately, rathat than allocating every entry in a single malloc.

Added a new __eglAddDevice function, which adds an entry to the device
hashtable.

When the application calls eglQueryDevicesEXT, always call through to each
vendor library, and add (or update) each EGLDeviceEXT to the hashtable.

There's still no provision for removing an entry from the device hashtable. The
semantics of hot-unplugging a device aren't defined yet, and there's no way to
ensure that libglvnd's mapping will get updated if a device disappears
asynchonously, so it's up to the vendor library to deal with the old handle.
This commit is contained in:
Kyle Brenneman 2021-02-10 15:39:17 -07:00
parent 2d69d4720c
commit 2747fcd3fc
3 changed files with 107 additions and 118 deletions

View File

@ -955,9 +955,73 @@ PUBLIC const char *EGLAPIENTRY eglQueryString(EGLDisplay dpy, EGLint name)
}
}
static EGLBoolean QueryVendorDevices(__EGLvendorInfo *vendor, EGLint max_devices,
EGLDeviceEXT *devices, EGLint *num_devices)
{
EGLDeviceEXT *vendorDevices = NULL;
EGLint vendorCount = 0;
EGLint i;
if (!vendor->supportsDevice) {
return EGL_TRUE;
}
if (!vendor->staticDispatch.queryDevicesEXT(0, NULL, &vendorCount)) {
// Even if this vendor fails, we can still return the devices from any
// other vendors
return EGL_TRUE;
}
if (vendorCount <= 0) {
return EGL_TRUE;
}
if (devices == NULL) {
// We're only getting the number of devices.
*num_devices += vendorCount;
return EGL_TRUE;
}
vendorDevices = malloc(sizeof(EGLDeviceEXT) * vendorCount);
if (vendorDevices == NULL)
{
__eglReportCritical(EGL_BAD_ALLOC, "eglQueryDevicesEXT",
__eglGetThreadLabel(), "Out of memory allocating device list");
return EGL_FALSE;
}
if (!vendor->staticDispatch.queryDevicesEXT(vendorCount, vendorDevices, &vendorCount)) {
free(vendorDevices);
return EGL_TRUE;
}
// Add or update our mapping for all of the devices, and fill in the
// caller's array.
for (i=0; i<vendorCount; i++)
{
if (!__eglAddDevice(vendorDevices[i], vendor))
{
__eglReportCritical(EGL_BAD_ALLOC, "eglQueryDevicesEXT",
__eglGetThreadLabel(), "Out of memory allocating device/vendor map");
free(vendorDevices);
return EGL_FALSE;
}
if (*num_devices < max_devices)
{
devices[*num_devices] = vendorDevices[i];
(*num_devices)++;
}
}
free(vendorDevices);
return EGL_TRUE;
}
EGLBoolean EGLAPIENTRY eglQueryDevicesEXT(EGLint max_devices,
EGLDeviceEXT *devices, EGLint *num_devices)
{
struct glvnd_list *vendorList;
__EGLvendorInfo *vendor;
__eglEntrypointCommon();
if (num_devices == NULL || (max_devices <= 0 && devices != NULL)) {
@ -966,17 +1030,14 @@ EGLBoolean EGLAPIENTRY eglQueryDevicesEXT(EGLint max_devices,
return EGL_FALSE;
}
__eglInitDeviceList();
vendorList = __eglLoadVendors();
if (devices != NULL) {
EGLint i;
*num_devices = (max_devices < __eglDeviceCount ? max_devices : __eglDeviceCount);
for (i = 0; i < *num_devices; i++) {
devices[i] = __eglDeviceList[i].handle;
// Initialize num_devices. QueryVendorDevices will update it.
*num_devices = 0;
glvnd_list_for_each_entry(vendor, vendorList, entry) {
if (!QueryVendorDevices(vendor, max_devices, devices, num_devices)) {
return EGL_FALSE;
}
} else {
*num_devices = __eglDeviceCount;
}
return EGL_TRUE;
}

View File

@ -41,10 +41,12 @@
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 __EGLdeviceInfoRec {
EGLDeviceEXT handle;
__EGLvendorInfo *vendor;
UT_hash_handle hh;
} __EGLdeviceInfo;
static DEFINE_INITIALIZED_LKDHASH(__EGLdeviceInfo, __eglDeviceHash);
/****************************************************************************/
@ -256,111 +258,52 @@ void __eglMappingTeardown(EGLBoolean doReset)
LKDHASH_TEARDOWN(__EGLdisplayInfoHash,
__eglDisplayInfoHash, NULL, NULL, EGL_FALSE);
LKDHASH_TEARDOWN(__EGLdeviceInfo,
__eglDeviceHash, NULL, NULL, EGL_FALSE);
__glvndWinsysDispatchCleanup();
}
}
static EGLBoolean AddVendorDevices(__EGLvendorInfo *vendor)
EGLBoolean __eglAddDevice(EGLDeviceEXT dev, __EGLvendorInfo *vendor)
{
EGLDeviceEXT *devices = NULL;
EGLint count = 0;
__EGLdeviceInfo *newDevList;
EGLint i, j;
__EGLdeviceInfo *devInfo = NULL;
if (!vendor->supportsDevice) {
if (dev == EGL_NO_DEVICE_EXT) {
// If the handle is NULL, then just silently ignore it.
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++;
LKDHASH_WRLOCK(__eglDeviceHash);
HASH_FIND_PTR(_LH(__eglDeviceHash), &dev, devInfo);
if (devInfo == NULL) {
devInfo = malloc(sizeof(__EGLdeviceInfo));
if (devInfo == NULL) {
LKDHASH_UNLOCK(__eglDeviceHash);
return EGL_FALSE;
}
devInfo->handle = dev;
HASH_ADD_PTR(_LH(__eglDeviceHash), handle, devInfo);
}
free(devices);
devInfo->vendor = vendor;
LKDHASH_UNLOCK(__eglDeviceHash);
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;
__EGLvendorInfo *vendor = NULL;
__eglInitDeviceList();
HASH_FIND_PTR(__eglDeviceHash, &dev, devInfo);
LKDHASH_RDLOCK(__eglDeviceHash);
HASH_FIND_PTR(_LH(__eglDeviceHash), &dev, devInfo);
if (devInfo != NULL) {
return devInfo->vendor;
vendor = devInfo->vendor;
} else {
return NULL;
vendor = NULL;
}
LKDHASH_UNLOCK(__eglDeviceHash);
return vendor;
}

View File

@ -44,16 +44,6 @@ typedef struct __EGLdisplayInfoRec {
__EGLvendorInfo *vendor;
} __EGLdisplayInfo;
typedef struct __EGLdeviceInfoRec {
EGLDeviceEXT handle;
__EGLvendorInfo *vendor;
UT_hash_handle hh;
} __EGLdeviceInfo;
extern __EGLdeviceInfo *__eglDeviceList;
extern __EGLdeviceInfo *__eglDeviceHash;
extern int __eglDeviceCount;
void __eglThreadInitialize(void);
/*!
@ -61,22 +51,12 @@ void __eglThreadInitialize(void);
*/
void __eglMappingInit(void);
/*!
* Initializes the EGLDeviceEXT list and hashtable.
*
* This function must be called before trying to access the \c __eglDeviceList
* array.
*/
void __eglInitDeviceList(void);
/*!
* This handles freeing all mapping state during library teardown
* or resetting locks on fork recovery.
*/
void __eglMappingTeardown(EGLBoolean doReset);
const __EGLdeviceInfo *__eglGetDeviceList(EGLint *deviceCount);
/*!
* Looks up the __EGLdisplayInfo structure for a display. If the display does
* not exist, then this returns NULL.
@ -110,6 +90,11 @@ __eglMustCastToProperFunctionPointerType __eglGetEGLDispatchAddress(const char *
__eglMustCastToProperFunctionPointerType __eglFetchDispatchEntry(__EGLvendorInfo *vendor, int index);
/**
* Adds an EGLDeviceEXT handle to libglvnd's mapping.
*/
EGLBoolean __eglAddDevice(EGLDeviceEXT dev, __EGLvendorInfo *vendor);
__EGLvendorInfo *__eglGetVendorFromDevice(EGLDeviceEXT dev);
void __eglSetError(EGLint errorCode);