From 2747fcd3fce8ad062e329e765d44db23c850588d Mon Sep 17 00:00:00 2001 From: Kyle Brenneman Date: Wed, 10 Feb 2021 15:39:17 -0700 Subject: [PATCH 1/8] 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. --- src/EGL/libegl.c | 79 +++++++++++++++++++++++--- src/EGL/libeglmapping.c | 121 +++++++++++----------------------------- src/EGL/libeglmapping.h | 25 ++------- 3 files changed, 107 insertions(+), 118 deletions(-) diff --git a/src/EGL/libegl.c b/src/EGL/libegl.c index 4e9f615..30468b1 100644 --- a/src/EGL/libegl.c +++ b/src/EGL/libegl.c @@ -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; isupportsDevice) { + 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; ihandle = 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; } diff --git a/src/EGL/libeglmapping.h b/src/EGL/libeglmapping.h index 26747e4..d1018d9 100644 --- a/src/EGL/libeglmapping.h +++ b/src/EGL/libeglmapping.h @@ -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); From 26d15a9f13c9178fe7f07585a6dac46661937062 Mon Sep 17 00:00:00 2001 From: Kyle Brenneman Date: Wed, 10 Feb 2021 15:52:11 -0700 Subject: [PATCH 2/8] EGL: Allow vendor's dispatch stubs to update the EGL device map. Export a new __EGLapiExports::setVendorForDevice function, which allows dispatch stubs to update the device hashtable when something returns an EGLDeviceEXT handle. Bumped the ABI version number to 1.2. --- include/glvnd/libeglabi.h | 10 +++++++++- src/EGL/libeglvendor.c | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/include/glvnd/libeglabi.h b/include/glvnd/libeglabi.h index d8857b9..b2f19c0 100644 --- a/include/glvnd/libeglabi.h +++ b/include/glvnd/libeglabi.h @@ -82,7 +82,7 @@ extern "C" { * will still work. */ #define EGL_VENDOR_ABI_MAJOR_VERSION ((uint32_t) 0) -#define EGL_VENDOR_ABI_MINOR_VERSION ((uint32_t) 1) +#define EGL_VENDOR_ABI_MINOR_VERSION ((uint32_t) 2) #define EGL_VENDOR_ABI_VERSION ((EGL_VENDOR_ABI_MAJOR_VERSION << 16) | EGL_VENDOR_ABI_MINOR_VERSION) static inline uint32_t EGL_VENDOR_ABI_GET_MAJOR_VERSION(uint32_t version) { @@ -191,6 +191,14 @@ typedef struct __EGLapiExportsRec { * Returns the EGL vendor for an EGLDeviceEXT handle. */ __EGLvendorInfo *(*getVendorFromDevice)(EGLDeviceEXT dev); + + /** + * Sets the EGL vendor for an EGLDeviceEXT handle. The dispatch stub for + * any function that returns an EGLDeviceEXT handle should call this. + * + * Supported since ABI version 1.2. + */ + EGLBoolean (* setVendorForDevice)(EGLDeviceEXT dev, __EGLvendorInfo *vendor); } __EGLapiExports; /***************************************************************************** diff --git a/src/EGL/libeglvendor.c b/src/EGL/libeglvendor.c index 8cb4ff5..05fc665 100644 --- a/src/EGL/libeglvendor.c +++ b/src/EGL/libeglvendor.c @@ -164,6 +164,7 @@ const __EGLapiExports __eglExportsTable = { __eglSetLastVendor, // setLastVendor __eglGetVendorFromDisplay, // getVendorFromDisplay __eglGetVendorFromDevice, // getVendorFromDevice + __eglAddDevice, // setVendorForDevice }; void TeardownVendor(__EGLvendorInfo *vendor) From eaffa7bc12aec49e029e30767cd3b5cfa90a073b Mon Sep 17 00:00:00 2001 From: Kyle Brenneman Date: Wed, 10 Feb 2021 16:06:48 -0700 Subject: [PATCH 3/8] EGL: Add a dispatch stub for eglQueryDisplayAttribEXT. Add a dispatch stub for eglQueryDisplayAttribEXT, which updates the device hashtable if the application queries EGL_DEVICE_EXT. It's possible that eglQueryDisplayAttribEXT could return an EGLDeviceEXT handle that wasn't returned by eglQueryDevicesEXT, so the dispatch stub needs to add the handle to the device hashtable. --- src/EGL/libegl.c | 40 +++++++++++++++++++++++++++++++++ src/EGL/libeglabipriv.h | 1 + src/EGL/libeglvendor.c | 1 + src/generate/eglFunctionList.py | 3 +++ 4 files changed, 45 insertions(+) diff --git a/src/EGL/libegl.c b/src/EGL/libegl.c index 30468b1..0cfb1d8 100644 --- a/src/EGL/libegl.c +++ b/src/EGL/libegl.c @@ -1042,6 +1042,46 @@ EGLBoolean EGLAPIENTRY eglQueryDevicesEXT(EGLint max_devices, return EGL_TRUE; } + +EGLBoolean eglQueryDisplayAttribEXT(EGLDisplay dpy, EGLint attribute, EGLAttrib *value) +{ + __EGLvendorInfo *vendor; + + if (value == NULL) { + __eglReportError(EGL_BAD_PARAMETER, "eglQueryDisplayAttribEXT", NULL, + "Missing value pointer"); + return EGL_FALSE; + } + + vendor = __eglGetVendorFromDisplay(dpy); + if (vendor == NULL) { + __eglReportError(EGL_BAD_DISPLAY, "eglQueryDisplayAttribEXT", NULL, + "Invalid EGLDisplay handle"); + return EGL_FALSE; + } + + if (vendor->staticDispatch.queryDisplayAttribEXT == NULL) { + __eglReportError(EGL_BAD_DISPLAY, "eglQueryDisplayAttribEXT", NULL, + "Driver does not support eglQueryDisplayAttribEXT"); + return EGL_FALSE; + } + + __eglSetLastVendor(vendor); + if (!vendor->staticDispatch.queryDisplayAttribEXT(dpy, attribute, value)) { + return EGL_FALSE; + } + + if (attribute == EGL_DEVICE_EXT && (EGLDeviceEXT) *value != EGL_NO_DEVICE_EXT) { + if (!__eglAddDevice((EGLDeviceEXT) *value, vendor)) { + __eglReportCritical(EGL_BAD_ALLOC, "eglQueryDevicesEXT", + __eglGetThreadLabel(), "Out of memory allocating device/vendor map"); + return EGL_FALSE; + } + } + + return EGL_TRUE; +} + // TODO: The function hash is the same as in GLX. It should go into a common // file. typedef struct { diff --git a/src/EGL/libeglabipriv.h b/src/EGL/libeglabipriv.h index ac9a5dc..315464c 100644 --- a/src/EGL/libeglabipriv.h +++ b/src/EGL/libeglabipriv.h @@ -91,6 +91,7 @@ typedef struct __EGLdispatchTableStaticRec { // Extension functions that libEGL cares about. EGLBoolean (* queryDevicesEXT) (EGLint max_devices, EGLDeviceEXT *devices, EGLint *num_devices); + EGLBoolean (* queryDisplayAttribEXT) (EGLDisplay dpy, EGLint attribute, EGLAttrib *value); EGLint (* debugMessageControlKHR) (EGLDEBUGPROCKHR callback, const EGLAttrib *attrib_list); diff --git a/src/EGL/libeglvendor.c b/src/EGL/libeglvendor.c index 05fc665..414f956 100644 --- a/src/EGL/libeglvendor.c +++ b/src/EGL/libeglvendor.c @@ -242,6 +242,7 @@ static GLboolean LookupVendorEntrypoints(__EGLvendorInfo *vendor) LOADENTRYPOINT(createPlatformPixmapSurface, "eglCreatePlatformPixmapSurface" ); LOADENTRYPOINT(waitSync, "eglWaitSync" ); LOADENTRYPOINT(queryDevicesEXT, "eglQueryDevicesEXT" ); + LOADENTRYPOINT(queryDisplayAttribEXT, "eglQueryDisplayAttribEXT" ); LOADENTRYPOINT(debugMessageControlKHR, "eglDebugMessageControlKHR" ); LOADENTRYPOINT(queryDebugKHR, "eglQueryDebugKHR" ); diff --git a/src/generate/eglFunctionList.py b/src/generate/eglFunctionList.py index afe14c6..2be8d9c 100644 --- a/src/generate/eglFunctionList.py +++ b/src/generate/eglFunctionList.py @@ -139,6 +139,9 @@ EGL_FUNCTIONS = ( # EGL_EXT_device_enumeration _eglExt("eglQueryDevicesEXT", "custom"), + # EGL_EXT_device_query + _eglExt("eglQueryDisplayAttribEXT", "custom"), + # EGL_KHR_debug _eglExt("eglDebugMessageControlKHR", "custom"), _eglExt("eglQueryDebugKHR", "custom"), From 6d43e9bac5c5b93cddc9e31a520525cb833d652f Mon Sep 17 00:00:00 2001 From: Kyle Brenneman Date: Thu, 11 Feb 2021 11:39:57 -0700 Subject: [PATCH 4/8] tests: Add initial support for adding new devices to EGL_dummy. Added a new function to the EGL dummy vendor called DummySetDeviceCount, which will change the number of devices that it hands back for eglQueryDevicesEXT. Also added a new dummy extension function, eglTestReturnDevice, which will dispatch based on an EGLDisplay and return an EGLDeviceEXT handle. This is used to test the new __EGLapiExports::setVendorForDevice function. --- tests/dummy/EGL_dummy.c | 52 ++++++++++++++++++++++++++++++++++------- tests/dummy/EGL_dummy.h | 28 ++++++++++++++++++++-- 2 files changed, 69 insertions(+), 11 deletions(-) diff --git a/tests/dummy/EGL_dummy.c b/tests/dummy/EGL_dummy.c index 501fae4..602fe20 100644 --- a/tests/dummy/EGL_dummy.c +++ b/tests/dummy/EGL_dummy.c @@ -38,6 +38,7 @@ enum DI_eglTestDispatchDisplay, DI_eglTestDispatchDevice, DI_eglTestDispatchCurrent, + DI_eglTestReturnDevice, DI_COUNT, }; @@ -82,6 +83,11 @@ static EGLint failNextMakeCurrentError = EGL_NONE; static EGLDEBUGPROCKHR debugCallbackFunc = NULL; static EGLBoolean debugCallbackEnabled = EGL_TRUE; +// The EGLDeviceEXT handle values have to be pointers, so just use the +// address of an array element for each EGLDeviceEXT handle. +static const char EGL_DEVICE_HANDLES[DUMMY_EGL_MAX_DEVICE_COUNT]; +static EGLint deviceCount = DUMMY_EGL_DEVICE_COUNT; + static DummyThreadState *GetThreadState(void) { DummyThreadState *thr = (DummyThreadState *) @@ -144,18 +150,14 @@ static DummyEGLDisplay *LookupEGLDisplay(EGLDisplay dpy) static EGLDeviceEXT GetEGLDevice(EGLint index) { - // The EGLDeviceEXT handle values have to be pointers, so just use the - // address of an array element for each EGLDeviceEXT handle. - static const char EGL_DEVICE_HANDLES[DUMMY_EGL_DEVICE_COUNT]; - - assert(index >= 0 && index < DUMMY_EGL_DEVICE_COUNT); + assert(index >= 0 && index < DUMMY_EGL_MAX_DEVICE_COUNT); return (EGLDeviceEXT) (EGL_DEVICE_HANDLES + index); } static EGLBoolean IsEGLDeviceValid(EGLDeviceEXT dev) { int i; - for (i=0; i= 0 && index < deviceCount); + return GetEGLDevice(index); +} + static void *dispatch_eglTestDispatchDisplay(EGLDisplay dpy, EGLint command, EGLAttrib param); static void *dispatch_eglTestDispatchDevice(EGLDeviceEXT dpy, EGLint command, EGLAttrib param); static void *dispatch_eglTestDispatchCurrent(EGLint command, EGLAttrib param); +static EGLDeviceEXT dispatch_eglTestReturnDevice(EGLDisplay dpy, EGLint index); static struct { const char *name; @@ -626,6 +635,7 @@ static struct { PROC_ENTRY(eglTestDispatchDisplay), PROC_ENTRY(eglTestDispatchDevice), PROC_ENTRY(eglTestDispatchCurrent), + PROC_ENTRY(eglTestReturnDevice), #undef PROC_ENTRY }; @@ -697,6 +707,24 @@ static void *dispatch_eglTestDispatchCurrent(EGLint command, EGLAttrib param) } } +static EGLDeviceEXT dispatch_eglTestReturnDevice(EGLDisplay dpy, EGLint index) +{ + __EGLvendorInfo *vendor; + pfn_eglTestReturnDevice func; + EGLDeviceEXT ret; + + apiExports->threadInit(); + vendor = apiExports->getVendorFromDisplay(dpy); + func = (pfn_eglTestReturnDevice) FetchVendorFunc(vendor, DI_eglTestReturnDevice, EGL_BAD_DISPLAY); + if (func != NULL) { + ret = func(dpy, index); + } else { + ret = NULL; + } + apiExports->setVendorForDevice(ret, vendor); + return ret; +} + static const struct { const char *name; @@ -791,6 +819,12 @@ static EGLBoolean dummyGetSupportsAPI(EGLenum api) } } +PUBLIC void DummySetDeviceCount(EGLint count) +{ + assert(count >= 0 && count <= DUMMY_EGL_MAX_DEVICE_COUNT); + deviceCount = count; +} + PUBLIC EGLBoolean __egl_Main(uint32_t version, const __EGLapiExports *exports, __EGLvendorInfo *vendor, __EGLapiImports *imports) diff --git a/tests/dummy/EGL_dummy.h b/tests/dummy/EGL_dummy.h index 59a74f3..e478ef3 100644 --- a/tests/dummy/EGL_dummy.h +++ b/tests/dummy/EGL_dummy.h @@ -56,11 +56,20 @@ #define DUMMY_VENDOR_NAME_1 "dummy1" /** - * The number of devices that each dummy vendor library exposes. This is used - * to figure out which vendor library should be behind each device. + * The number of devices that each dummy vendor library exposes by default. + * This is used to figure out which vendor library should be behind each + * device. */ #define DUMMY_EGL_DEVICE_COUNT 2 +/** + * The maximum number of devices that each dummy vendor library can expose. + * + * This is used to test adding a device after the initial eglQueryDevicesEXT + * call. + */ +#define DUMMY_EGL_MAX_DEVICE_COUNT 3 + /** * A platform enum to select a vendor library by name. * The native display should be a pointer to a string with the vendor name. @@ -111,4 +120,19 @@ typedef void * (* pfn_eglTestDispatchDevice) (EGLDeviceEXT dev, EGLint command, */ typedef void * (* pfn_eglTestDispatchCurrent) (EGLint command, EGLAttrib param); +/** + * Returns an EGLDeviceEXT handle from the vendor library. + * + * This is used to test returning a device that wasn't listed in a call to + * eglQueryDevicesEXT. + */ +typedef EGLDeviceEXT (* pfn_eglTestReturnDevice) (EGLDisplay dpy, EGLint index); + +/** + * Changes the number of EGLDeviceEXT handles that the dummy library exposes. + * + * This function has to be looked up using dlsym, not eglGetProcAddress. + */ +typedef void (* pfn_DummySetDeviceCount) (EGLint count); + #endif // EGL_DUMMY_H From 01316ea91dd982189a189c904c4c5a9ddec03c42 Mon Sep 17 00:00:00 2001 From: Kyle Brenneman Date: Thu, 11 Feb 2021 11:48:49 -0700 Subject: [PATCH 5/8] tests: Update EGL test helpers to match the new EGL dummy vendor functions. Updated egl_test_utils.c/h to include the new eglTestReturnDevice function. Added functions to egl_test_utils.c/h to load each vendor's DummySetDeviceCount function using dlopen+dlsym. --- tests/Makefile.am | 12 +++++----- tests/egl_test_utils.c | 51 ++++++++++++++++++++++++++++++++++++++++++ tests/egl_test_utils.h | 22 ++++++++++++++++++ tests/meson.build | 2 +- 4 files changed, 80 insertions(+), 7 deletions(-) diff --git a/tests/Makefile.am b/tests/Makefile.am index 1c3101b..0f417f7 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -185,32 +185,32 @@ check_PROGRAMS += testegldisplay testegldisplay_SOURCES = \ testegldisplay.c \ egl_test_utils.c -testegldisplay_LDADD = $(top_builddir)/src/EGL/libEGL.la +testegldisplay_LDADD = $(top_builddir)/src/EGL/libEGL.la @LIB_DL@ check_PROGRAMS += testegldevice testegldevice_SOURCES = \ testegldevice.c \ egl_test_utils.c -testegldevice_LDADD = $(top_builddir)/src/EGL/libEGL.la +testegldevice_LDADD = $(top_builddir)/src/EGL/libEGL.la @LIB_DL@ check_PROGRAMS += testeglgetprocaddress testeglgetprocaddress_SOURCES = \ testeglgetprocaddress.c \ egl_test_utils.c -testeglgetprocaddress_LDADD = $(top_builddir)/src/EGL/libEGL.la +testeglgetprocaddress_LDADD = $(top_builddir)/src/EGL/libEGL.la @LIB_DL@ check_PROGRAMS += testeglmakecurrent testeglmakecurrent_SOURCES = \ testeglmakecurrent.c \ egl_test_utils.c -testeglmakecurrent_LDADD = $(top_builddir)/src/EGL/libEGL.la +testeglmakecurrent_LDADD = $(top_builddir)/src/EGL/libEGL.la @LIB_DL@ testeglmakecurrent_LDADD += $(top_builddir)/src/OpenGL/libOpenGL.la check_PROGRAMS += testeglerror testeglerror_SOURCES = \ testeglerror.c \ egl_test_utils.c -testeglerror_LDADD = $(top_builddir)/src/EGL/libEGL.la +testeglerror_LDADD = $(top_builddir)/src/EGL/libEGL.la @LIB_DL@ testeglerror_LDADD += $(top_builddir)/src/OpenGL/libOpenGL.la @@ -218,7 +218,7 @@ check_PROGRAMS += testegldebug testegldebug_SOURCES = \ testegldebug.c \ egl_test_utils.c -testegldebug_LDADD = $(top_builddir)/src/EGL/libEGL.la +testegldebug_LDADD = $(top_builddir)/src/EGL/libEGL.la @LIB_DL@ endif # ENABLE_EGL diff --git a/tests/egl_test_utils.c b/tests/egl_test_utils.c index cfc32e1..bcac2b5 100644 --- a/tests/egl_test_utils.c +++ b/tests/egl_test_utils.c @@ -2,6 +2,8 @@ #include #include +#include +#include const char *DUMMY_VENDOR_NAMES[DUMMY_VENDOR_COUNT] = { DUMMY_VENDOR_NAME_0, @@ -16,6 +18,10 @@ PFNEGLLABELOBJECTKHRPROC ptr_eglLabelObjectKHR; pfn_eglTestDispatchDisplay ptr_eglTestDispatchDisplay; pfn_eglTestDispatchDevice ptr_eglTestDispatchDevice; pfn_eglTestDispatchCurrent ptr_eglTestDispatchCurrent; +pfn_eglTestReturnDevice ptr_eglTestReturnDevice; + +static void *dummyVendorHandles[DUMMY_VENDOR_COUNT] = {}; +DummyVendorFunctions dummyFuncs[DUMMY_VENDOR_COUNT] = {}; __eglMustCastToProperFunctionPointerType loadEGLFunction(const char *name) { @@ -44,5 +50,50 @@ void loadEGLExtensions(void) loadEGLFunction("eglTestDispatchDevice"); ptr_eglTestDispatchCurrent = (pfn_eglTestDispatchCurrent) loadEGLFunction("eglTestDispatchCurrent"); + ptr_eglTestReturnDevice = (pfn_eglTestReturnDevice) + loadEGLFunction("eglTestReturnDevice"); +} + +void loadDummyVendorExtensions(void) +{ + int i; + + for (i=0; i Date: Thu, 11 Feb 2021 11:55:34 -0700 Subject: [PATCH 6/8] tests: Added unit tests for adding new EGLDisplayEXT handles. Added a new test program, testegldeviceadd, which tests a vendor library adding a new EGLDeviceEXT handle after the application calls eglQueryDevicesEXT. It's got two subtests right now. "querydevices" will test returning the new device through a second eglQueryDevicesEXT call. "returndevice" tests returning the new device through a vendor-provided dispatch stub. --- tests/Makefile.am | 8 + tests/meson.build | 17 ++ tests/testegldeviceadd.c | 244 +++++++++++++++++++++++++ tests/testegldeviceadd_querydevices.sh | 5 + tests/testegldeviceadd_returndevice.sh | 5 + 5 files changed, 279 insertions(+) create mode 100644 tests/testegldeviceadd.c create mode 100755 tests/testegldeviceadd_querydevices.sh create mode 100755 tests/testegldeviceadd_returndevice.sh diff --git a/tests/Makefile.am b/tests/Makefile.am index 0f417f7..7df10d0 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -172,6 +172,8 @@ endif # ENABLE_GLX TESTS_EGL = TESTS_EGL += testegldisplay.sh TESTS_EGL += testegldevice.sh +TESTS_EGL += testegldeviceadd_querydevices.sh +TESTS_EGL += testegldeviceadd_returndevice.sh TESTS_EGL += testeglgetprocaddress.sh TESTS_EGL += testeglmakecurrent.sh TESTS_EGL += testeglerror.sh @@ -193,6 +195,12 @@ testegldevice_SOURCES = \ egl_test_utils.c testegldevice_LDADD = $(top_builddir)/src/EGL/libEGL.la @LIB_DL@ +check_PROGRAMS += testegldeviceadd +testegldeviceadd_SOURCES = \ + testegldeviceadd.c \ + egl_test_utils.c +testegldeviceadd_LDADD = $(top_builddir)/src/EGL/libEGL.la @LIB_DL@ + check_PROGRAMS += testeglgetprocaddress testeglgetprocaddress_SOURCES = \ testeglgetprocaddress.c \ diff --git a/tests/meson.build b/tests/meson.build index 2173b48..8be6cf3 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -215,5 +215,22 @@ if get_option('egl') depends : libEGL_dummy, ) endforeach + + exe_egldeviceadd = executable( + 'egldeviceadd', + ['testegldeviceadd.c', 'egl_test_utils.c'], + include_directories : [inc_include], + link_with : [libEGL], + dependencies : [dep_dl], + ) + foreach t : [ 'querydevices', 'returndevice' ] + test( + 'egldeviceadd (@0@)'.format(t), + exe_egldeviceadd, + args : [t], + env : env_egl, + suite : ['egl'], + ) + endforeach endif diff --git a/tests/testegldeviceadd.c b/tests/testegldeviceadd.c new file mode 100644 index 0000000..3c10e31 --- /dev/null +++ b/tests/testegldeviceadd.c @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2016, 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. + */ + +#include +#include +#include +#include +#include + +#include "dummy/EGL_dummy.h" +#include "egl_test_utils.h" + +#define DEVICE_ARRAY_SIZE (DUMMY_EGL_MAX_DEVICE_COUNT * DUMMY_VENDOR_COUNT) + +EGLBoolean CheckDeviceHandles(EGLDeviceEXT *devices, EGLint count) +{ + EGLint i, j; + + for (i=0; i Date: Thu, 11 Feb 2021 12:10:41 -0700 Subject: [PATCH 7/8] tests: Implement EGL_EXT_device_query in the dummy vendor. Added implementations for eglQueryDisplayAttribEXT, eglQueryDeviceAttribEXT, and eglQueryDeviceStringEXT. Defined a new EGL_DEVICE_INDEX attribute for eglGetPlatformDisplay that uses a device based on its index. This will allow us to test the case where eglQueryDisplayAttribEXT returns an EGLDeviceEXT handle that libglvnd hasn't seen before. --- tests/dummy/EGL_dummy.c | 93 ++++++++++++++++++++++++++++++++++++++++- tests/dummy/EGL_dummy.h | 8 ++++ tests/egl_test_utils.c | 9 ++++ tests/egl_test_utils.h | 3 ++ 4 files changed, 112 insertions(+), 1 deletion(-) diff --git a/tests/dummy/EGL_dummy.c b/tests/dummy/EGL_dummy.c index 602fe20..e21dfee 100644 --- a/tests/dummy/EGL_dummy.c +++ b/tests/dummy/EGL_dummy.c @@ -39,13 +39,17 @@ enum DI_eglTestDispatchDevice, DI_eglTestDispatchCurrent, DI_eglTestReturnDevice, + DI_eglQueryDeviceAttribEXT, + DI_eglQueryDeviceStringEXT, DI_COUNT, }; static const char *CLIENT_EXTENSIONS = "EGL_KHR_client_get_all_proc_addresses" " EGL_EXT_client_extensions" + " EGL_EXT_device_base" " EGL_EXT_device_enumeration" + " EGL_EXT_device_query" ; static const char *PLATFORM_EXTENSIONS = @@ -58,6 +62,7 @@ typedef struct DummyEGLDisplayRec { EGLenum platform; void *native_display; EGLLabelKHR label; + EGLDeviceEXT device; struct glvnd_list entry; } DummyEGLDisplay; @@ -179,6 +184,7 @@ static EGLDisplay dummyGetPlatformDisplay(EGLenum platform, void *native_display { CommonEntrypoint(); DummyEGLDisplay *disp = NULL; + EGLDeviceEXT device = EGL_NO_DEVICE_EXT; if (platform == EGL_NONE) { if (native_display != EGL_DEFAULT_DISPLAY) { @@ -200,6 +206,20 @@ static EGLDisplay dummyGetPlatformDisplay(EGLenum platform, void *native_display // Set the native_display pointer to NULL. This makes it simpler to // find the same dispaly below. native_display = NULL; + + if (attrib_list != NULL) { + int i; + for (i=0; attrib_list[i] != EGL_NONE; i += 2) { + if (attrib_list[i] == EGL_DEVICE_INDEX) { + EGLint index = (EGLint) attrib_list[i + 1]; + assert(index >= 0 && index < deviceCount); + device = GetEGLDevice(index); + } else { + printf("Invalid attribute 0x%04llx\n", (unsigned long long) attrib_list[i]); + abort(); + } + } + } } } else if (platform == EGL_PLATFORM_DEVICE_EXT) { if (native_display == EGL_DEFAULT_DISPLAY) { @@ -209,6 +229,7 @@ static EGLDisplay dummyGetPlatformDisplay(EGLenum platform, void *native_display return EGL_NO_DISPLAY; } } + device = (EGLDeviceEXT) native_display; } else { // We don't support this platform. SetLastError("eglGetPlatformDisplay", NULL, EGL_BAD_PARAMETER); @@ -217,7 +238,7 @@ static EGLDisplay dummyGetPlatformDisplay(EGLenum platform, void *native_display __glvndPthreadFuncs.mutex_lock(&displayListLock); glvnd_list_for_each_entry(disp, &displayList, entry) { - if (disp->platform == platform && disp->native_display == native_display) { + if (disp->platform == platform && disp->native_display == native_display && disp->device == device) { __glvndPthreadFuncs.mutex_unlock(&displayListLock); return disp; } @@ -227,6 +248,7 @@ static EGLDisplay dummyGetPlatformDisplay(EGLenum platform, void *native_display disp = (DummyEGLDisplay *) calloc(1, sizeof(DummyEGLDisplay)); disp->platform = platform; disp->native_display = native_display; + disp->device = device; glvnd_list_append(&disp->entry, &displayList); __glvndPthreadFuncs.mutex_unlock(&displayListLock); return disp; @@ -521,6 +543,36 @@ static EGLBoolean EGLAPIENTRY dummy_eglQueryDevicesEXT(EGLint max_devices, EGLDe return EGL_TRUE; } +static EGLBoolean EGLAPIENTRY dummy_eglQueryDisplayAttribEXT(EGLDisplay dpy, EGLint attribute, EGLAttrib *value) +{ + DummyEGLDisplay *disp = LookupEGLDisplay(dpy); + + if (attribute == EGL_DEVICE_EXT) { + *value = (EGLAttrib) disp->device; + return EGL_TRUE; + } else { + SetLastError("eglQueryDisplayAttribEXT", disp->label, EGL_BAD_ATTRIBUTE); + return EGL_FALSE; + } +} + +static EGLBoolean EGLAPIENTRY dummy_eglQueryDeviceAttribEXT(EGLDeviceEXT device, EGLint attribute, EGLAttrib *value) +{ + // No device attributes are defined here. + SetLastError("eglQueryDeviceAttribEXT", NULL, EGL_BAD_ATTRIBUTE); + return EGL_FALSE; +} + +static const char *EGLAPIENTRY dummy_eglQueryDeviceStringEXT(EGLDeviceEXT device, EGLint name) +{ + if (name == EGL_EXTENSIONS) { + return ""; + } else { + SetLastError("eglQueryDeviceStringEXT", NULL, EGL_BAD_ATTRIBUTE); + return NULL; + } +} + static EGLint EGLAPIENTRY dummy_eglDebugMessageControlKHR(EGLDEBUGPROCKHR callback, const EGLAttrib *attrib_list) { CommonEntrypoint(); @@ -624,6 +676,8 @@ static void *dispatch_eglTestDispatchDisplay(EGLDisplay dpy, EGLint command, EGL static void *dispatch_eglTestDispatchDevice(EGLDeviceEXT dpy, EGLint command, EGLAttrib param); static void *dispatch_eglTestDispatchCurrent(EGLint command, EGLAttrib param); static EGLDeviceEXT dispatch_eglTestReturnDevice(EGLDisplay dpy, EGLint index); +static EGLBoolean EGLAPIENTRY dispatch_eglQueryDeviceAttribEXT(EGLDeviceEXT device, EGLint attribute, EGLAttrib *value); +static const char *EGLAPIENTRY dispatch_eglQueryDeviceStringEXT(EGLDeviceEXT device, EGLint name); static struct { const char *name; @@ -636,6 +690,8 @@ static struct { PROC_ENTRY(eglTestDispatchDevice), PROC_ENTRY(eglTestDispatchCurrent), PROC_ENTRY(eglTestReturnDevice), + PROC_ENTRY(eglQueryDeviceAttribEXT), + PROC_ENTRY(eglQueryDeviceStringEXT), #undef PROC_ENTRY }; @@ -662,6 +718,17 @@ static __eglMustCastToProperFunctionPointerType FetchVendorFunc(__EGLvendorInfo return func; } +static __eglMustCastToProperFunctionPointerType FetchByDevice(EGLDeviceEXT dev, int index) +{ + __EGLvendorInfo *vendor; + __eglMustCastToProperFunctionPointerType func; + + apiExports->threadInit(); + vendor = apiExports->getVendorFromDevice(dev); + func = FetchVendorFunc(vendor, index, EGL_BAD_DEVICE_EXT); + return func; +} + static void *dispatch_eglTestDispatchDisplay(EGLDisplay dpy, EGLint command, EGLAttrib param) { __EGLvendorInfo *vendor; @@ -725,6 +792,27 @@ static EGLDeviceEXT dispatch_eglTestReturnDevice(EGLDisplay dpy, EGLint index) return ret; } +static EGLBoolean EGLAPIENTRY dispatch_eglQueryDeviceAttribEXT(EGLDeviceEXT device, EGLint attribute, EGLAttrib *value) +{ + PFNEGLQUERYDEVICEATTRIBEXTPROC func = (PFNEGLQUERYDEVICEATTRIBEXTPROC) + FetchByDevice(device, DI_eglQueryDeviceAttribEXT); + if (func != NULL) { + return func(device, attribute, value); + } else { + return EGL_FALSE; + } +} + +static const char *EGLAPIENTRY dispatch_eglQueryDeviceStringEXT(EGLDeviceEXT device, EGLint name) +{ + PFNEGLQUERYDEVICESTRINGEXTPROC func = (PFNEGLQUERYDEVICESTRINGEXTPROC) + FetchByDevice(device, DI_eglQueryDeviceStringEXT); + if (func != NULL) { + return func(device, name); + } else { + return EGL_FALSE; + } +} static const struct { const char *name; @@ -763,6 +851,9 @@ static const struct { PROC_ENTRY(eglGetError), PROC_ENTRY(eglQueryDevicesEXT), + PROC_ENTRY(eglQueryDisplayAttribEXT), + PROC_ENTRY(eglQueryDeviceAttribEXT), + PROC_ENTRY(eglQueryDeviceStringEXT), PROC_ENTRY(eglDebugMessageControlKHR), PROC_ENTRY(eglQueryDebugKHR), PROC_ENTRY(eglLabelObjectKHR), diff --git a/tests/dummy/EGL_dummy.h b/tests/dummy/EGL_dummy.h index e478ef3..01b4a1d 100644 --- a/tests/dummy/EGL_dummy.h +++ b/tests/dummy/EGL_dummy.h @@ -85,6 +85,14 @@ */ #define EGL_CREATE_CONTEXT_FAIL 0x010001 +/** + * This attribute is for eglCreatePlatformDisplay. The attribute is the index + * of the EGLDeviceEXT handle to associate with the display. + * + * This is used to test eglQueryDisplayEXT. + */ +#define EGL_DEVICE_INDEX 0x010002 + enum { DUMMY_COMMAND_GET_VENDOR_NAME, diff --git a/tests/egl_test_utils.c b/tests/egl_test_utils.c index bcac2b5..ac03714 100644 --- a/tests/egl_test_utils.c +++ b/tests/egl_test_utils.c @@ -14,6 +14,9 @@ PFNEGLQUERYDEVICESEXTPROC ptr_eglQueryDevicesEXT; PFNEGLDEBUGMESSAGECONTROLKHRPROC ptr_eglDebugMessageControlKHR; PFNEGLQUERYDEBUGKHRPROC ptr_eglQueryDebugKHR; PFNEGLLABELOBJECTKHRPROC ptr_eglLabelObjectKHR; +PFNEGLQUERYDEVICEATTRIBEXTPROC ptr_eglQueryDeviceAttribEXT; +PFNEGLQUERYDEVICESTRINGEXTPROC ptr_eglQueryDeviceStringEXT; +PFNEGLQUERYDISPLAYATTRIBEXTPROC ptr_eglQueryDisplayAttribEXT; pfn_eglTestDispatchDisplay ptr_eglTestDispatchDisplay; pfn_eglTestDispatchDevice ptr_eglTestDispatchDevice; @@ -37,6 +40,12 @@ void loadEGLExtensions(void) { ptr_eglQueryDevicesEXT = (PFNEGLQUERYDEVICESEXTPROC) loadEGLFunction("eglQueryDevicesEXT"); + ptr_eglQueryDeviceAttribEXT = (PFNEGLQUERYDEVICEATTRIBEXTPROC) + loadEGLFunction("eglQueryDeviceAttribEXT"); + ptr_eglQueryDeviceStringEXT = (PFNEGLQUERYDEVICESTRINGEXTPROC) + loadEGLFunction("eglQueryDeviceStringEXT"); + ptr_eglQueryDisplayAttribEXT = (PFNEGLQUERYDISPLAYATTRIBEXTPROC) + loadEGLFunction("eglQueryDisplayAttribEXT"); ptr_eglDebugMessageControlKHR = (PFNEGLDEBUGMESSAGECONTROLKHRPROC) loadEGLFunction("eglDebugMessageControlKHR"); ptr_eglQueryDebugKHR = (PFNEGLQUERYDEBUGKHRPROC) diff --git a/tests/egl_test_utils.h b/tests/egl_test_utils.h index ce06671..8cb63e0 100644 --- a/tests/egl_test_utils.h +++ b/tests/egl_test_utils.h @@ -53,6 +53,9 @@ typedef struct extern const char *DUMMY_VENDOR_NAMES[DUMMY_VENDOR_COUNT]; extern PFNEGLQUERYDEVICESEXTPROC ptr_eglQueryDevicesEXT; +extern PFNEGLQUERYDEVICEATTRIBEXTPROC ptr_eglQueryDeviceAttribEXT; +extern PFNEGLQUERYDEVICESTRINGEXTPROC ptr_eglQueryDeviceStringEXT; +extern PFNEGLQUERYDISPLAYATTRIBEXTPROC ptr_eglQueryDisplayAttribEXT; extern PFNEGLDEBUGMESSAGECONTROLKHRPROC ptr_eglDebugMessageControlKHR; extern PFNEGLQUERYDEBUGKHRPROC ptr_eglQueryDebugKHR; extern PFNEGLLABELOBJECTKHRPROC ptr_eglLabelObjectKHR; From 288eb48c043f86c784eff5c6d0c5a885d67a6d9f Mon Sep 17 00:00:00 2001 From: Kyle Brenneman Date: Thu, 11 Feb 2021 12:43:52 -0700 Subject: [PATCH 8/8] tests: Add unit test for eglQueryDisplayAttribEXT. Extended testegldeviceadd to be able to test calling eglQueryDisplayAttribEXT to return a new EGLDeviceEXT handle. --- tests/Makefile.am | 1 + tests/meson.build | 2 +- tests/testegldeviceadd.c | 34 ++++++++++++++++++++++++++ tests/testegldeviceadd_querydisplay.sh | 5 ++++ 4 files changed, 41 insertions(+), 1 deletion(-) create mode 100755 tests/testegldeviceadd_querydisplay.sh diff --git a/tests/Makefile.am b/tests/Makefile.am index 7df10d0..54c46e6 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -174,6 +174,7 @@ TESTS_EGL += testegldisplay.sh TESTS_EGL += testegldevice.sh TESTS_EGL += testegldeviceadd_querydevices.sh TESTS_EGL += testegldeviceadd_returndevice.sh +TESTS_EGL += testegldeviceadd_querydisplay.sh TESTS_EGL += testeglgetprocaddress.sh TESTS_EGL += testeglmakecurrent.sh TESTS_EGL += testeglerror.sh diff --git a/tests/meson.build b/tests/meson.build index 8be6cf3..886ffea 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -223,7 +223,7 @@ if get_option('egl') link_with : [libEGL], dependencies : [dep_dl], ) - foreach t : [ 'querydevices', 'returndevice' ] + foreach t : [ 'querydevices', 'returndevice', 'querydisplay' ] test( 'egldeviceadd (@0@)'.format(t), exe_egldeviceadd, diff --git a/tests/testegldeviceadd.c b/tests/testegldeviceadd.c index 3c10e31..f203e18 100644 --- a/tests/testegldeviceadd.c +++ b/tests/testegldeviceadd.c @@ -200,6 +200,38 @@ EGLBoolean TestReturnDevice(EGLDeviceEXT *oldDevices, EGLint oldDeviceCount) return TestNewDevice(newDevice, oldDevices, oldDeviceCount); } +EGLBoolean TestQueryDisplay(EGLDeviceEXT *oldDevices, EGLint oldDeviceCount) +{ + const EGLAttrib DISPLAY_ATTRIBS[] = + { + EGL_DEVICE_INDEX, DUMMY_EGL_DEVICE_COUNT, EGL_NONE + }; + EGLDisplay dpy; + EGLint major, minor; + EGLAttrib newDevice = -1; + + printf("Testing eglQueryDisplayAttribEXT.\n"); + + dpy = eglGetPlatformDisplay(EGL_DUMMY_PLATFORM, (EGLNativeDisplayType) DUMMY_VENDOR_NAMES[0], DISPLAY_ATTRIBS); + if (dpy == EGL_NO_DISPLAY) { + printf("eglGetPlatformDisplay failed with 0x%04x\n", eglGetError()); + return EGL_FALSE; + } + if (!eglInitialize(dpy, &major, &minor)) { + printf("eglInitialize failed with 0x%04x\n", eglGetError()); + return EGL_FALSE; + } + + if (!ptr_eglQueryDisplayAttribEXT(dpy, EGL_DEVICE_EXT, &newDevice)) { + printf("ptr_eglQueryDisplayAttribEXT failed with 0x%04x\n", eglGetError()); + return EGL_FALSE; + } + eglTerminate(dpy); + + return TestNewDevice((EGLDeviceEXT) newDevice, oldDevices, oldDeviceCount); +} + + int main(int argc, char **argv) { EGLDeviceEXT devices[DEVICE_ARRAY_SIZE] = {}; @@ -230,6 +262,8 @@ int main(int argc, char **argv) success = TestAddQueryDevices(devices, deviceCount); } else if (strcmp(argv[i], "returndevice") == 0) { success = TestReturnDevice(devices, deviceCount); + } else if (strcmp(argv[i], "querydisplay") == 0) { + success = TestQueryDisplay(devices, deviceCount); } else { printf("Invalid test name: %s\n", argv[i]); } diff --git a/tests/testegldeviceadd_querydisplay.sh b/tests/testegldeviceadd_querydisplay.sh new file mode 100755 index 0000000..f61081b --- /dev/null +++ b/tests/testegldeviceadd_querydisplay.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +. $TOP_SRCDIR/tests/eglenv.sh + +./testegldeviceadd querydisplay