diff options
Diffstat (limited to 'src/egl/main/egldriver.c')
-rw-r--r-- | src/egl/main/egldriver.c | 496 |
1 files changed, 269 insertions, 227 deletions
diff --git a/src/egl/main/egldriver.c b/src/egl/main/egldriver.c index f56214472e9..1e3d7d24aa7 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_gdi_swrast" -}; - typedef HMODULE lib_handle; static HMODULE @@ -67,11 +73,6 @@ library_suffix(void) #elif defined(_EGL_OS_UNIX) -static const char *DefaultDriverNames[] = { - "egl_dri2", - "egl_glx" -}; - typedef void * lib_handle; static void * @@ -97,13 +98,6 @@ library_suffix(void) #endif -#define NUM_PROBE_CACHE_SLOTS 8 -static struct { - EGLint keys[NUM_PROBE_CACHE_SLOTS]; - const void *values[NUM_PROBE_CACHE_SLOTS]; -} _eglProbeCache; - - /** * Open the named driver and find its bootstrap function: _eglMain(). */ @@ -163,85 +157,106 @@ _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); } @@ -252,7 +267,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); @@ -268,9 +282,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; @@ -278,19 +290,23 @@ _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 - /* remember the driver and stop */ - _eglGlobal.Drivers[_eglGlobal.NumDrivers++] = drv; - return EGL_FALSE; + _eglAddModule(path); + + return EGL_TRUE; } @@ -326,7 +342,6 @@ _eglLoaderPattern(const char *dir, size_t len, void *loader_data) suffix_len = (suffix) ? strlen(suffix) : 0; while ((dirent = readdir(dirp))) { - _EGLDriver *drv; size_t dirent_len = strlen(dirent->d_name); const char *p; @@ -340,12 +355,10 @@ _eglLoaderPattern(const char *dir, size_t len, void *loader_data) continue; } - /* make a full path and load the driver */ + /* make a full path and add it to the module array */ if (len + dirent_len + 1 <= sizeof(path)) { strcpy(path + len, dirent->d_name); - drv = _eglLoadDriver(path, NULL); - if (drv) - _eglGlobal.Drivers[_eglGlobal.NumDrivers++] = drv; + _eglAddModule(path); } } @@ -360,19 +373,17 @@ _eglLoaderPattern(const char *dir, size_t len, void *loader_data) /** - * 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) { @@ -384,8 +395,6 @@ _eglPreloadForEach(const char *search_path, cur = (next) ? next + 1 : NULL; } - - return (_eglGlobal.NumDrivers - num_drivers); } @@ -430,12 +439,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; @@ -451,132 +460,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; - } - return EGL_TRUE; +/** + * 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); + } } /** - * Preload platform drivers. - * - * Platform drivers are a set of drivers that support a certain window system. - * The window system may be specified by EGL_PLATFORM. - * - * FIXME This makes libEGL a memory hog if an user driver is not specified and - * there are many platform drivers. + * Add drivers to the module array. Drivers will be loaded as they are matched + * to displays. */ static EGLBoolean -_eglPreloadPlatformDrivers(void) +_eglAddDrivers(void) { - const char *dpy; - char prefix[32]; - int ret; - - dpy = getenv("EGL_PLATFORM"); - /* try deprecated env variable */ - if (!dpy || !dpy[0]) - dpy = getenv("EGL_DISPLAY"); - if (!dpy || !dpy[0]) - dpy = _EGL_DEFAULT_PLATFORM; - if (!dpy || !dpy[0]) - return EGL_FALSE; + if (_eglModules) + return EGL_TRUE; - ret = _eglsnprintf(prefix, sizeof(prefix), "egl_%s_", dpy); - if (ret < 0 || ret >= sizeof(prefix)) - return EGL_FALSE; + /* the order here decides the priorities of the drivers */ + _eglAddUserDriver(); + _eglAddDefaultDrivers(); + _eglPreloadForEach(_eglGetSearchPath(), _eglLoaderPattern, (void *) "egl_"); - return (_eglPreloadForEach(_eglGetSearchPath(), - _eglLoaderPattern, (void *) prefix) > 0); + 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; + } + + /* match the loaded modules */ + for (i = 0; i < _eglModules->Size; i++) { + mod = (_EGLModule *) _eglModules->Elements[i]; + if (!mod->Driver) + break; + + 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; + } + + /* load more modules */ + if (!best_drv) { + EGLint first_unloaded = i; + + while (i < _eglModules->Size) { + mod = (_EGLModule *) _eglModules->Elements[i]; + assert(!mod->Driver); + + 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++; + } + } } - loaded = (_eglPreloadUserDriver() || - _eglPreloadPlatformDrivers()); + _eglUnlockMutex(&_eglModuleMutex); - _eglUnlockMutex(_eglGlobal.Mutex); + 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 loaded; + return best_drv; } -/** - * Unload preloaded drivers. - */ -void -_eglUnloadDrivers(void) + +__eglMustCastToProperFunctionPointerType +_eglGetDriverProc(const char *procname) { 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; + } - /* 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; + for (i = 0; i < _eglModules->Size; i++) { + _EGLModule *mod = (_EGLModule *) _eglModules->Elements[i]; + + if (!mod->Driver) + break; + proc = mod->Driver->API.GetProcAddress(mod->Driver, procname); + if (proc) + break; } - _eglGlobal.NumDrivers = 0; + return proc; } -_EGLDriver * -_eglLoadDefaultDriver(EGLDisplay dpy, EGLint *major, EGLint *minor) -{ - _EGLDriver *drv = NULL; - int i; - - _eglLockMutex(_eglGlobal.Mutex); - for (i = 0; i < ARRAY_SIZE(DefaultDriverNames); i++) { - _eglPreloadForEach(_eglGetSearchPath(), - _eglLoaderFile, (void *) DefaultDriverNames[i]); - if (_eglGlobal.NumDrivers == 0) - continue; - drv = _eglGlobal.Drivers[0]; - if (drv->API.Initialize(drv, dpy, major, minor)) - break; - _eglUnloadDrivers(); - } - - _eglUnlockMutex(_eglGlobal.Mutex); - - 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; + } } @@ -656,44 +739,3 @@ _eglSearchPathForEach(EGLBoolean (*callback)(const char *, size_t, void *), const char *search_path = _eglGetSearchPath(); _eglPreloadForEach(search_path, callback, callback_data); } - - -/** - * Set the probe cache at the given key. - * - * A key, instead of a _EGLDriver, is used to allow the probe cache to be share - * by multiple drivers. - */ -void -_eglSetProbeCache(EGLint key, const void *val) -{ - EGLint idx; - - for (idx = 0; idx < NUM_PROBE_CACHE_SLOTS; idx++) { - if (!_eglProbeCache.keys[idx] || _eglProbeCache.keys[idx] == key) - break; - } - assert(key > 0); - assert(idx < NUM_PROBE_CACHE_SLOTS); - - _eglProbeCache.keys[idx] = key; - _eglProbeCache.values[idx] = val; -} - - -/** - * Return the probe cache at the given key. - */ -const void * -_eglGetProbeCache(EGLint key) -{ - EGLint idx; - - for (idx = 0; idx < NUM_PROBE_CACHE_SLOTS; idx++) { - if (!_eglProbeCache.keys[idx] || _eglProbeCache.keys[idx] == key) - break; - } - - return (idx < NUM_PROBE_CACHE_SLOTS && _eglProbeCache.keys[idx] == key) ? - _eglProbeCache.values[idx] : NULL; -} |