summaryrefslogtreecommitdiffstats
path: root/src/egl/main/egldriver.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/egl/main/egldriver.c')
-rw-r--r--src/egl/main/egldriver.c488
1 files changed, 334 insertions, 154 deletions
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;
+ }
}