diff options
-rw-r--r-- | src/egl/main/eglapi.c | 48 | ||||
-rw-r--r-- | src/egl/main/eglarray.c | 16 | ||||
-rw-r--r-- | src/egl/main/eglarray.h | 4 | ||||
-rw-r--r-- | src/egl/main/egldriver.c | 488 | ||||
-rw-r--r-- | src/egl/main/egldriver.h | 14 | ||||
-rw-r--r-- | src/egl/main/eglglobals.c | 2 | ||||
-rw-r--r-- | src/egl/main/eglglobals.h | 4 |
7 files changed, 365 insertions, 211 deletions
diff --git a/src/egl/main/eglapi.c b/src/egl/main/eglapi.c index d51ffad410d..89a75f9830b 100644 --- a/src/egl/main/eglapi.c +++ b/src/egl/main/eglapi.c @@ -264,46 +264,24 @@ EGLBoolean EGLAPIENTRY eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor) { _EGLDisplay *disp = _eglLockDisplay(dpy); - EGLint major_int = 0, minor_int = 0; if (!disp) RETURN_EGL_ERROR(NULL, EGL_BAD_DISPLAY, EGL_FALSE); if (!disp->Initialized) { - _EGLDriver *drv = disp->Driver; - - if (!drv) { - _eglPreloadDrivers(); - drv = _eglMatchDriver(disp); - /* Initialize the particular display now */ - if (drv && !drv->API.Initialize(drv, disp, &major_int, &minor_int)) - RETURN_EGL_ERROR(disp, EGL_NOT_INITIALIZED, EGL_FALSE); - } - if (!drv) - /* Load and initialize the first default driver that works */ - drv = _eglLoadDefaultDriver(disp, &major_int, &minor_int); - if (!drv) - RETURN_EGL_ERROR(disp, EGL_NOT_INITIALIZED, EGL_FALSE); - - disp->APImajor = major_int; - disp->APIminor = minor_int; - _eglsnprintf(disp->Version, sizeof(disp->Version), - "%d.%d (%s)", major_int, minor_int, drv->Name); + if (!_eglMatchDriver(disp, EGL_FALSE)) + RETURN_EGL_ERROR(disp, EGL_NOT_INITIALIZED, EGL_FALSE); + _eglsnprintf(disp->Version, sizeof(disp->Version), "%d.%d (%s)", + disp->APImajor, disp->APIminor, disp->Driver->Name); /* limit to APIs supported by core */ disp->ClientAPIsMask &= _EGL_API_ALL_BITS; - - disp->Driver = drv; - disp->Initialized = EGL_TRUE; - } else { - major_int = disp->APImajor; - minor_int = disp->APIminor; } /* Update applications version of major and minor if not NULL */ if ((major != NULL) && (minor != NULL)) { - *major = major_int; - *minor = minor_int; + *major = disp->APImajor; + *minor = disp->APIminor; } RETURN_EGL_SUCCESS(disp, EGL_TRUE); @@ -870,18 +848,8 @@ eglGetProcAddress(const char *procname) } } } - if (ret) - RETURN_EGL_SUCCESS(NULL, ret); - - _eglPreloadDrivers(); - - /* now loop over drivers to query their procs */ - for (i = 0; i < _eglGlobal.NumDrivers; i++) { - _EGLDriver *drv = _eglGlobal.Drivers[i]; - ret = drv->API.GetProcAddress(drv, procname); - if (ret) - break; - } + if (!ret) + ret = _eglGetDriverProc(procname); RETURN_EGL_SUCCESS(NULL, ret); } diff --git a/src/egl/main/eglarray.c b/src/egl/main/eglarray.c index 41550bb4bed..781d07fc1ce 100644 --- a/src/egl/main/eglarray.c +++ b/src/egl/main/eglarray.c @@ -84,6 +84,22 @@ _eglAppendArray(_EGLArray *array, void *elem) /** + * Erase an element from an array. + */ +void +_eglEraseArray(_EGLArray *array, EGLint i, void (*free_cb)(void *)) +{ + if (free_cb) + free_cb(array->Elements[i]); + if (i < array->Size - 1) { + memmove(&array->Elements[i], &array->Elements[i + 1], + (array->Size - i - 1) * sizeof(array->Elements[0])); + } + array->Size--; +} + + +/** * Find in an array for the given element. */ void * diff --git a/src/egl/main/eglarray.h b/src/egl/main/eglarray.h index 80bdb0e3eef..fe92efc11ee 100644 --- a/src/egl/main/eglarray.h +++ b/src/egl/main/eglarray.h @@ -29,6 +29,10 @@ extern void _eglAppendArray(_EGLArray *array, void *elem); +extern void +_eglEraseArray(_EGLArray *array, EGLint i, void (*free_cb)(void *)); + + void * _eglFindArray(_EGLArray *array, void *elem); diff --git a/src/egl/main/egldriver.c b/src/egl/main/egldriver.c index d38022c7c6f..d68d3a27ad6 100644 --- a/src/egl/main/egldriver.c +++ b/src/egl/main/egldriver.c @@ -22,6 +22,7 @@ #include "eglstring.h" #include "eglsurface.h" #include "eglimage.h" +#include "eglmutex.h" #if defined(_EGL_OS_UNIX) #include <dlfcn.h> @@ -31,17 +32,22 @@ #endif +typedef struct _egl_module { + char *Path; + void *Handle; + _EGLDriver *Driver; +} _EGLModule; + +static _EGL_DECLARE_MUTEX(_eglModuleMutex); +static _EGLArray *_eglModules; + + /** * Wrappers for dlopen/dlclose() */ #if defined(_EGL_OS_WINDOWS) -/* XXX Need to decide how to do dynamic name lookup on Windows */ -static const char *DefaultDriverNames[] = { - "egl_gallium" -}; - typedef HMODULE lib_handle; static HMODULE @@ -67,12 +73,6 @@ library_suffix(void) #elif defined(_EGL_OS_UNIX) -static const char *DefaultDriverNames[] = { - "egl_gallium", - "egl_dri2", - "egl_glx" -}; - typedef void * lib_handle; static void * @@ -157,87 +157,109 @@ _eglOpenLibrary(const char *driverPath, lib_handle *handle) /** - * Load the named driver. + * Load a module and create the driver object. */ -static _EGLDriver * -_eglLoadDriver(const char *path, const char *args) +static EGLBoolean +_eglLoadModule(_EGLModule *mod) { _EGLMain_t mainFunc; lib_handle lib; - _EGLDriver *drv = NULL; + _EGLDriver *drv; - mainFunc = _eglOpenLibrary(path, &lib); + mainFunc = _eglOpenLibrary(mod->Path, &lib); if (!mainFunc) - return NULL; + return EGL_FALSE; - drv = mainFunc(args); + drv = mainFunc(NULL); if (!drv) { if (lib) close_library(lib); - return NULL; + return EGL_FALSE; } if (!drv->Name) { - _eglLog(_EGL_WARNING, "Driver loaded from %s has no name", path); + _eglLog(_EGL_WARNING, "Driver loaded from %s has no name", mod->Path); drv->Name = "UNNAMED"; } - drv->Path = _eglstrdup(path); - drv->Args = (args) ? _eglstrdup(args) : NULL; - if (!drv->Path || (args && !drv->Args)) { - if (drv->Path) - free((char *) drv->Path); - if (drv->Args) - free((char *) drv->Args); - drv->Unload(drv); - if (lib) - close_library(lib); - return NULL; - } + mod->Handle = (void *) lib; + mod->Driver = drv; + + return EGL_TRUE; +} - drv->LibHandle = lib; - return drv; +/** + * Unload a module. + */ +static void +_eglUnloadModule(_EGLModule *mod) +{ + /* destroy the driver */ + if (mod->Driver && mod->Driver->Unload) + mod->Driver->Unload(mod->Driver); + if (mod->Handle) + close_library(mod->Handle); + + mod->Driver = NULL; + mod->Handle = NULL; } /** - * Match a display to a preloaded driver. - * - * The matching is done by finding the driver with the highest score. + * Add a module to the module array. */ -_EGLDriver * -_eglMatchDriver(_EGLDisplay *dpy) +static _EGLModule * +_eglAddModule(const char *path) { - _EGLDriver *best_drv = NULL; - EGLint best_score = -1, i; + _EGLModule *mod; + EGLint i; - /* - * this function is called after preloading and the drivers never change - * after preloading. - */ - for (i = 0; i < _eglGlobal.NumDrivers; i++) { - _EGLDriver *drv = _eglGlobal.Drivers[i]; - EGLint score; - - score = (drv->Probe) ? drv->Probe(drv, dpy) : 0; - if (score > best_score) { - if (best_drv) { - _eglLog(_EGL_DEBUG, "driver %s has higher score than %s", - drv->Name, best_drv->Name); - } + if (!_eglModules) { + _eglModules = _eglCreateArray("Module", 8); + if (!_eglModules) + return NULL; + } - best_drv = drv; - best_score = score; - /* perfect match */ - if (score >= 100) - break; + /* find duplicates */ + for (i = 0; i < _eglModules->Size; i++) { + mod = _eglModules->Elements[i]; + if (strcmp(mod->Path, path) == 0) + return mod; + } + + /* allocate a new one */ + mod = calloc(1, sizeof(*mod)); + if (mod) { + mod->Path = _eglstrdup(path); + if (!mod->Path) { + free(mod); + mod = NULL; } } + if (mod) { + _eglAppendArray(_eglModules, (void *) mod); + _eglLog(_EGL_DEBUG, "added %s to module array", mod->Path); + } - return best_drv; + return mod; +} + + +/** + * Free a module. + */ +static void +_eglFreeModule(void *module) +{ + _EGLModule *mod = (_EGLModule *) module; + + _eglUnloadModule(mod); + free(mod->Path); + free(mod); } +#include <errno.h> /** * A loader function for use with _eglPreloadForEach. The loader data is the @@ -246,7 +268,6 @@ _eglMatchDriver(_EGLDisplay *dpy) static EGLBoolean _eglLoaderFile(const char *dir, size_t len, void *loader_data) { - _EGLDriver *drv; char path[1024]; const char *filename = (const char *) loader_data; size_t flen = strlen(filename); @@ -262,9 +283,7 @@ _eglLoaderFile(const char *dir, size_t len, void *loader_data) len += flen; path[len] = '\0'; - if (library_suffix() == NULL || strstr(path, library_suffix())) - drv = _eglLoadDriver(path, NULL); - else { + if (library_suffix()) { const char *suffix = library_suffix(); size_t slen = strlen(suffix); const char *p; @@ -272,36 +291,100 @@ _eglLoaderFile(const char *dir, size_t len, void *loader_data) p = filename + flen - slen; need_suffix = (p < filename || strcmp(p, suffix) != 0); - if (need_suffix && len + slen + 1 <= sizeof(path)) { + if (need_suffix) { + /* overflow */ + if (len + slen + 1 > sizeof(path)) + return EGL_TRUE; strcpy(path + len, suffix); - drv = _eglLoadDriver(path, NULL); - } else { - drv = NULL; } } - if (!drv) + +#if defined(_EGL_OS_UNIX) + /* check if the file exists */ + if (access(path, F_OK)) return EGL_TRUE; +#endif + + _eglAddModule(path); + + return EGL_TRUE; +} + - /* remember the driver and stop */ - _eglGlobal.Drivers[_eglGlobal.NumDrivers++] = drv; +/** + * A loader function for use with _eglPreloadForEach. The loader data is the + * pattern (prefix) of the files to look for. + */ +static EGLBoolean +_eglLoaderPattern(const char *dir, size_t len, void *loader_data) +{ +#if defined(_EGL_OS_UNIX) + const char *prefix, *suffix; + size_t prefix_len, suffix_len; + DIR *dirp; + struct dirent *dirent; + char path[1024]; + + if (len + 2 > sizeof(path)) + return EGL_TRUE; + if (len) { + memcpy(path, dir, len); + path[len++] = '/'; + } + path[len] = '\0'; + + dirp = opendir(path); + if (!dirp) + return EGL_TRUE; + + prefix = (const char *) loader_data; + prefix_len = strlen(prefix); + suffix = library_suffix(); + suffix_len = (suffix) ? strlen(suffix) : 0; + + while ((dirent = readdir(dirp))) { + size_t dirent_len = strlen(dirent->d_name); + const char *p; + + /* match the prefix */ + if (strncmp(dirent->d_name, prefix, prefix_len) != 0) + continue; + /* match the suffix */ + if (suffix) { + p = dirent->d_name + dirent_len - suffix_len; + if (p < dirent->d_name || strcmp(p, suffix) != 0) + continue; + } + + /* make a full path and add it to the module array */ + if (len + dirent_len + 1 <= sizeof(path)) { + strcpy(path + len, dirent->d_name); + _eglAddModule(path); + } + } + + closedir(dirp); + + return EGL_TRUE; +#else /* _EGL_OS_UNIX */ + /* stop immediately */ return EGL_FALSE; +#endif } /** - * Run the preload function on each driver directory and return the number of - * drivers loaded. + * Run the callback function on each driver directory. * * The process may end prematurely if the callback function returns false. */ -static EGLint +static void _eglPreloadForEach(const char *search_path, EGLBoolean (*loader)(const char *, size_t, void *), void *loader_data) { const char *cur, *next; size_t len; - EGLint num_drivers = _eglGlobal.NumDrivers; cur = search_path; while (cur) { @@ -313,8 +396,6 @@ _eglPreloadForEach(const char *search_path, cur = (next) ? next + 1 : NULL; } - - return (_eglGlobal.NumDrivers - num_drivers); } @@ -359,12 +440,12 @@ _eglGetSearchPath(void) /** - * Preload a user driver. + * Add the user driver to the module array. * - * A user driver can be specified by EGL_DRIVER. + * The user driver is specified by EGL_DRIVER. */ -static EGLBoolean -_eglPreloadUserDriver(void) +static void +_eglAddUserDriver(void) { const char *search_path = _eglGetSearchPath(); char *env; @@ -380,107 +461,206 @@ _eglPreloadUserDriver(void) } } #endif /* _EGL_OS_UNIX */ - if (!env) - return EGL_FALSE; + if (env) + _eglPreloadForEach(search_path, _eglLoaderFile, (void *) env); +} - if (!_eglPreloadForEach(search_path, _eglLoaderFile, (void *) env)) { - _eglLog(_EGL_WARNING, "EGL_DRIVER is set to an invalid driver"); - return EGL_FALSE; + +/** + * Add default drivers to the module array. + */ +static void +_eglAddDefaultDrivers(void) +{ + const char *search_path = _eglGetSearchPath(); + EGLint i; +#if defined(_EGL_OS_WINDOWS) + const char *DefaultDriverNames[] = { + "egl_gallium" + }; +#elif defined(_EGL_OS_UNIX) + const char *DefaultDriverNames[] = { + "egl_gallium", + "egl_dri2", + "egl_glx" + }; +#endif + + for (i = 0; i < ARRAY_SIZE(DefaultDriverNames); i++) { + void *name = (void *) DefaultDriverNames[i]; + _eglPreloadForEach(search_path, _eglLoaderFile, name); } +} - return EGL_TRUE; + +/** + * Add drivers to the module array. Drivers will be loaded as they are matched + * to displays. + */ +static EGLBoolean +_eglAddDrivers(void) +{ + if (_eglModules) + return EGL_TRUE; + + /* the order here decides the priorities of the drivers */ + _eglAddUserDriver(); + _eglAddDefaultDrivers(); + _eglPreloadForEach(_eglGetSearchPath(), _eglLoaderPattern, (void *) "egl_"); + + return (_eglModules != NULL); } /** - * Preload drivers. + * Match a display to a driver. The display is initialized unless use_probe is + * true. * - * This function loads the driver modules and creates the corresponding - * _EGLDriver objects. + * The matching is done by finding the first driver that can initialize the + * display, or when use_probe is true, the driver with highest score. */ -EGLBoolean -_eglPreloadDrivers(void) +_EGLDriver * +_eglMatchDriver(_EGLDisplay *dpy, EGLBoolean use_probe) { - EGLBoolean loaded; + _EGLModule *mod; + _EGLDriver *best_drv = NULL; + EGLint best_score = 0; + EGLint major, minor, i; - /* protect the preloading process */ - _eglLockMutex(_eglGlobal.Mutex); + _eglLockMutex(&_eglModuleMutex); - /* already preloaded */ - if (_eglGlobal.NumDrivers) { - _eglUnlockMutex(_eglGlobal.Mutex); - return EGL_TRUE; + if (!_eglAddDrivers()) { + _eglUnlockMutex(&_eglModuleMutex); + return EGL_FALSE; } - loaded = _eglPreloadUserDriver(); + /* match the loaded modules */ + for (i = 0; i < _eglModules->Size; i++) { + mod = (_EGLModule *) _eglModules->Elements[i]; + if (!mod->Driver) + break; - _eglUnlockMutex(_eglGlobal.Mutex); + if (use_probe) { + EGLint score = (mod->Driver->Probe) ? + mod->Driver->Probe(mod->Driver, dpy) : 1; + if (score > best_score) { + best_drv = mod->Driver; + best_score = score; + } + } + else { + if (mod->Driver->API.Initialize(mod->Driver, dpy, &major, &minor)) { + best_drv = mod->Driver; + best_score = 100; + } + } + /* perfect match */ + if (best_score >= 100) + break; + } - return loaded; -} + /* load more modules */ + if (!best_drv) { + EGLint first_unloaded = i; -/** - * Unload preloaded drivers. - */ -void -_eglUnloadDrivers(void) -{ -#if defined(_EGL_OS_UNIX) - EGLint i; + while (i < _eglModules->Size) { + mod = (_EGLModule *) _eglModules->Elements[i]; + assert(!mod->Driver); - /* this is called at atexit time */ - for (i = 0; i < _eglGlobal.NumDrivers; i++) { - _EGLDriver *drv = _eglGlobal.Drivers[i]; - lib_handle handle = drv->LibHandle; - - if (drv->Path) - free((char *) drv->Path); - if (drv->Args) - free((char *) drv->Args); - - /* destroy driver */ - if (drv->Unload) - drv->Unload(drv); - - if (handle) - close_library(handle); - _eglGlobal.Drivers[i] = NULL; + if (!_eglLoadModule(mod)) { + /* remove invalid modules */ + _eglEraseArray(_eglModules, i, _eglFreeModule); + continue; + } + + if (use_probe) { + best_score = (mod->Driver->Probe) ? + mod->Driver->Probe(mod->Driver, dpy) : 1; + } + else { + if (mod->Driver->API.Initialize(mod->Driver, dpy, &major, &minor)) + best_score = 100; + } + + if (best_score > 0) { + best_drv = mod->Driver; + /* loaded modules come before unloaded ones */ + if (first_unloaded != i) { + void *tmp = _eglModules->Elements[i]; + _eglModules->Elements[i] = + _eglModules->Elements[first_unloaded]; + _eglModules->Elements[first_unloaded] = tmp; + } + break; + } + else { + _eglUnloadModule(mod); + i++; + } + } } - _eglGlobal.NumDrivers = 0; -#elif defined(_EGL_OS_WINDOWS) - /* XXX Windows unloads DLLs before atexit */ -#endif + _eglUnlockMutex(&_eglModuleMutex); + + if (best_drv) { + _eglLog(_EGL_DEBUG, "the best driver is %s (score %d)", + best_drv->Name, best_score); + if (!use_probe) { + dpy->Driver = best_drv; + dpy->Initialized = EGL_TRUE; + dpy->APImajor = major; + dpy->APIminor = minor; + } + } + + return best_drv; } -_EGLDriver * -_eglLoadDefaultDriver(EGLDisplay dpy, EGLint *major, EGLint *minor) + +__eglMustCastToProperFunctionPointerType +_eglGetDriverProc(const char *procname) { - _EGLDriver *drv = NULL; - EGLBoolean ok; - int i; + EGLint i; + _EGLProc proc = NULL; + + if (!_eglModules) { + /* load the driver for the default display */ + EGLDisplay egldpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); + _EGLDisplay *dpy = _eglLookupDisplay(egldpy); + if (!dpy || !_eglMatchDriver(dpy, EGL_TRUE)) + return NULL; + } - _eglLockMutex(_eglGlobal.Mutex); + for (i = 0; i < _eglModules->Size; i++) { + _EGLModule *mod = (_EGLModule *) _eglModules->Elements[i]; - for (i = 0; i < ARRAY_SIZE(DefaultDriverNames); i++) { - _eglPreloadForEach(_eglGetSearchPath(), - _eglLoaderFile, (void *) DefaultDriverNames[i]); - if (_eglGlobal.NumDrivers == 0) - continue; - drv = _eglGlobal.Drivers[0]; - - _eglUnlockMutex(_eglGlobal.Mutex); - ok = drv->API.Initialize(drv, dpy, major, minor); - _eglLockMutex(_eglGlobal.Mutex); - if (ok) + if (!mod->Driver) + break; + proc = mod->Driver->API.GetProcAddress(mod->Driver, procname); + if (proc) break; - - _eglUnloadDrivers(); } - _eglUnlockMutex(_eglGlobal.Mutex); + return proc; +} + - return _eglGlobal.NumDrivers > 0 ? drv : NULL; +/** + * Unload all drivers. + */ +void +_eglUnloadDrivers(void) +{ + /* this is called at atexit time */ + if (_eglModules) { +#if defined(_EGL_OS_UNIX) + _eglDestroyArray(_eglModules, _eglFreeModule); +#elif defined(_EGL_OS_WINDOWS) + /* XXX Windows unloads DLLs before atexit */ + _eglDestroyArray(_eglModules, NULL); +#endif + _eglModules = NULL; + } } diff --git a/src/egl/main/egldriver.h b/src/egl/main/egldriver.h index 711de8ad205..c618feb6b02 100644 --- a/src/egl/main/egldriver.h +++ b/src/egl/main/egldriver.h @@ -41,10 +41,6 @@ typedef _EGLDriver *(*_EGLMain_t)(const char *args); */ struct _egl_driver { - void *LibHandle; /**< dlopen handle */ - const char *Path; /**< path to this driver */ - const char *Args; /**< args to load this driver */ - const char *Name; /**< name of this driver */ /** @@ -73,21 +69,17 @@ _eglMain(const char *args); extern _EGLDriver * -_eglMatchDriver(_EGLDisplay *dpy); +_eglMatchDriver(_EGLDisplay *dpy, EGLBoolean probe_only); -extern EGLBoolean -_eglPreloadDrivers(void); +extern __eglMustCastToProperFunctionPointerType +_eglGetDriverProc(const char *procname); extern void _eglUnloadDrivers(void); -extern _EGLDriver * -_eglLoadDefaultDriver(EGLDisplay dpy, EGLint *major, EGLint *minor); - - PUBLIC void _eglInitDriverFallbacks(_EGLDriver *drv); diff --git a/src/egl/main/eglglobals.c b/src/egl/main/eglglobals.c index e63819e08a2..725a25eca63 100644 --- a/src/egl/main/eglglobals.c +++ b/src/egl/main/eglglobals.c @@ -12,8 +12,6 @@ struct _egl_global _eglGlobal = &_eglGlobalMutex, /* Mutex */ NULL, /* DisplayList */ 1, /* FreeScreenHandle */ - 0, /* NumDrivers */ - { NULL }, /* Drivers */ 2, /* NumAtExitCalls */ { /* default AtExitCalls, called in reverse order */ diff --git a/src/egl/main/eglglobals.h b/src/egl/main/eglglobals.h index 43688980208..e8bf5416e2a 100644 --- a/src/egl/main/eglglobals.h +++ b/src/egl/main/eglglobals.h @@ -18,10 +18,6 @@ struct _egl_global EGLScreenMESA FreeScreenHandle; - /* these never change after preloading */ - EGLint NumDrivers; - _EGLDriver *Drivers[10]; - EGLint NumAtExitCalls; void (*AtExitCalls[10])(void); }; |