diff options
| author | Chia-I Wu <olvaffe@gmail.com> | 2014-08-01 11:21:23 +0800 |
|---|---|---|
| committer | Chia-I Wu <olvaffe@gmail.com> | 2014-08-01 11:36:57 +0800 |
| commit | 8b24debfdac174e9209d9f086e49e30b74bcfc13 (patch) | |
| tree | 50ef404e879102b7e2d3fbada44a35de4e5c1397 /loader/loader.c | |
| parent | 3356335dcbec0142c65bbe7bd344d5230e6d10f9 (diff) | |
| download | usermoji-8b24debfdac174e9209d9f086e49e30b74bcfc13.tar.xz | |
loader: add a generic ICD loader
Diffstat (limited to 'loader/loader.c')
| -rw-r--r-- | loader/loader.c | 426 |
1 files changed, 426 insertions, 0 deletions
diff --git a/loader/loader.c b/loader/loader.c new file mode 100644 index 00000000..4e5f231f --- /dev/null +++ b/loader/loader.c @@ -0,0 +1,426 @@ +/* + * XGL + * + * Copyright (C) 2014 LunarG, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS 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 SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <stdbool.h> +#include <string.h> + +#include <dlfcn.h> +#include <pthread.h> + +#include <xgl.h> +#include <xglDbg.h> + +#include "loader.generated" + +typedef XGL_RESULT (XGLAPI *InitAndEnumerateGpusT)(const XGL_APPLICATION_INFO* pAppInfo, const XGL_ALLOC_CALLBACKS* pAllocCb, XGL_UINT maxGpus, XGL_UINT* pGpuCount, XGL_PHYSICAL_GPU* pGpus); +typedef XGL_RESULT (XGLAPI *DbgRegisterMsgCallbackT)(XGL_DBG_MSG_CALLBACK_FUNCTION pfnMsgCallback, XGL_VOID* pUserData); +typedef XGL_RESULT (XGLAPI *DbgUnregisterMsgCallbackT)(XGL_DBG_MSG_CALLBACK_FUNCTION pfnMsgCallback); +typedef XGL_RESULT (XGLAPI *DbgSetGlobalOptionT)(XGL_INT dbgOption, XGL_SIZE dataSize, const XGL_VOID* pData); + +struct loader_icd { + void *handle; + + InitAndEnumerateGpusT InitAndEnumerateGpus; + DbgRegisterMsgCallbackT DbgRegisterMsgCallback; + DbgUnregisterMsgCallbackT DbgUnregisterMsgCallback; + DbgSetGlobalOptionT DbgSetGlobalOption; + + struct loader_icd *next; +}; + +struct loader_msg_callback { + XGL_DBG_MSG_CALLBACK_FUNCTION func; + XGL_VOID *data; + + struct loader_msg_callback *next; +}; + +static struct { + bool scanned; + struct loader_icd *icds; + + struct loader_msg_callback *msg_callbacks; + + bool debug_echo_enable; + bool break_on_error; + bool break_on_warning; +} loader; + +static XGL_RESULT loader_msg_callback_add(XGL_DBG_MSG_CALLBACK_FUNCTION func, + XGL_VOID *data) +{ + struct loader_msg_callback *cb; + + cb = malloc(sizeof(*cb)); + if (!cb) + return XGL_ERROR_OUT_OF_MEMORY; + + cb->func = func; + cb->data = data; + + cb->next = loader.msg_callbacks; + loader.msg_callbacks = cb; + + return XGL_SUCCESS; +} + +static XGL_RESULT loader_msg_callback_remove(XGL_DBG_MSG_CALLBACK_FUNCTION func) +{ + struct loader_msg_callback *cb = loader.msg_callbacks; + + /* + * Find the first match (last registered). + * + * XXX What if the same callback function is registered more than once? + */ + while (cb) { + if (cb->func == func) { + break; + } + + cb = cb->next; + } + + if (!cb) + return XGL_ERROR_INVALID_POINTER; + + free(cb); + + return XGL_SUCCESS; +} + +static void loader_msg_callback_clear(void) +{ + struct loader_msg_callback *cb = loader.msg_callbacks; + + while (cb) { + struct loader_msg_callback *next = cb->next; + free(cb); + cb = next; + } + + loader.msg_callbacks = NULL; +} + +static void loader_log(XGL_DBG_MSG_TYPE msg_type, XGL_INT msg_code, + const char *format, ...) +{ + const struct loader_msg_callback *cb = loader.msg_callbacks; + char msg[256]; + va_list ap; + int ret; + + va_start(ap, format); + ret = vsnprintf(msg, sizeof(msg), format, ap); + if (ret >= sizeof(msg) || ret < 0) { + msg[sizeof(msg) - 1] = '\0'; + } + va_end(ap); + + if (loader.debug_echo_enable || !cb) { + fputs(msg, stderr); + fputc('\n', stderr); + } + + while (cb) { + cb->func(msg_type, XGL_VALIDATION_LEVEL_0, XGL_NULL_HANDLE, 0, + msg_code, (const XGL_CHAR *) msg, cb->data); + cb = cb->next; + } + + switch (msg_type) { + case XGL_DBG_MSG_ERROR: + if (loader.break_on_error) { + exit(1); + } + /* fall through */ + case XGL_DBG_MSG_WARNING: + if (loader.break_on_warning) { + exit(1); + } + break; + default: + break; + } +} + +static void +loader_icd_destroy(struct loader_icd *icd) +{ + dlclose(icd->handle); + free(icd); +} + +static struct loader_icd * +loader_icd_create(const char *filename) +{ + struct loader_icd *icd; + + icd = malloc(sizeof(*icd)); + if (!icd) + return NULL; + + icd->handle = dlopen(filename, RTLD_LAZY | RTLD_LOCAL); + if (!icd->handle) { + loader_log(XGL_DBG_MSG_WARNING, 0, dlerror()); + free(icd); + return NULL; + } + +#define LOOKUP(icd, func) do { \ + icd->func = (func## T) dlsym(icd->handle, "xgl" #func); \ + if (!icd->func) { \ + loader_log(XGL_DBG_MSG_WARNING, 0, dlerror()); \ + loader_icd_destroy(icd); \ + return NULL; \ + } \ +} while (0) + LOOKUP(icd, InitAndEnumerateGpus); + LOOKUP(icd, DbgRegisterMsgCallback); + LOOKUP(icd, DbgUnregisterMsgCallback); + LOOKUP(icd, DbgSetGlobalOption); +#undef LOOKUP + + return icd; +} + +static XGL_RESULT loader_icd_register_msg_callbacks(const struct loader_icd *icd) +{ + const struct loader_msg_callback *cb = loader.msg_callbacks; + XGL_RESULT res; + + while (cb) { + res = icd->DbgRegisterMsgCallback(cb->func, cb->data); + if (res != XGL_SUCCESS) { + break; + } + + cb = cb->next; + } + + /* roll back on errors */ + if (cb) { + const struct loader_msg_callback *tmp = loader.msg_callbacks; + + while (tmp != cb) { + icd->DbgUnregisterMsgCallback(cb->func); + tmp = tmp->next; + } + + return res; + } + + return XGL_SUCCESS; +} + +static XGL_RESULT loader_icd_set_global_options(const struct loader_icd *icd) +{ +#define SETB(icd, opt, val) do { \ + if (val) { \ + const XGL_RESULT res = \ + icd->DbgSetGlobalOption(opt, sizeof(val), &val); \ + if (res != XGL_SUCCESS) \ + return res; \ + } \ +} while (0) + SETB(icd, XGL_DBG_OPTION_DEBUG_ECHO_ENABLE, loader.debug_echo_enable); + SETB(icd, XGL_DBG_OPTION_BREAK_ON_ERROR, loader.break_on_error); + SETB(icd, XGL_DBG_OPTION_BREAK_ON_WARNING, loader.break_on_warning); +#undef SETB + +return XGL_SUCCESS; +} + +static void +loader_icd_scan(void) +{ + /* XXX How to discover ICDs? */ + static const char *filenames[] = { + "libmesaxgl.so", + "libintelxgl.so", + "libxgl.so", + }; + int i; + + for (i = 0; i < sizeof(filenames) / sizeof(filenames[0]); i++) { + struct loader_icd *icd; + + icd = loader_icd_create(filenames[i]); + if (!icd) { + continue; + } + + if (loader_icd_set_global_options(icd) != XGL_SUCCESS || + loader_icd_register_msg_callbacks(icd) != XGL_SUCCESS) { + loader_log(XGL_DBG_MSG_WARNING, 0, + "%s ignored: failed to migrate settings", filenames[i]); + loader_icd_destroy(icd); + continue; + } + + /* prepend to the list */ + icd->next = loader.icds; + loader.icds = icd; + } + + /* we have nothing to log anymore */ + loader_msg_callback_clear(); + + loader.scanned = true; +} + +XGL_RESULT XGLAPI xglInitAndEnumerateGpus(const XGL_APPLICATION_INFO* pAppInfo, const XGL_ALLOC_CALLBACKS* pAllocCb, XGL_UINT maxGpus, XGL_UINT* pGpuCount, XGL_PHYSICAL_GPU* pGpus) +{ + static pthread_once_t once = PTHREAD_ONCE_INIT; + const struct loader_icd *icd; + XGL_UINT count = 0; + XGL_RESULT res; + + pthread_once(&once, loader_icd_scan); + + if (!loader.icds) + return XGL_ERROR_UNAVAILABLE; + + icd = loader.icds; + while (icd) { + XGL_PHYSICAL_GPU gpus[XGL_MAX_PHYSICAL_GPUS]; + XGL_UINT n, max = maxGpus - count; + + if (max > XGL_MAX_PHYSICAL_GPUS) { + max = XGL_MAX_PHYSICAL_GPUS; + } + + res = icd->InitAndEnumerateGpus(pAppInfo, pAllocCb, max, &n, gpus); + if (res == XGL_SUCCESS) { + memcpy(pGpus + count, gpus, sizeof(*pGpus) * n); + count += n; + + if (count >= maxGpus) { + break; + } + } + + icd = icd->next; + } + + *pGpuCount = count; + + return (count > 0) ? XGL_SUCCESS : res; +} + +XGL_RESULT XGLAPI xglDbgRegisterMsgCallback(XGL_DBG_MSG_CALLBACK_FUNCTION pfnMsgCallback, XGL_VOID* pUserData) +{ + const struct loader_icd *icd = loader.icds; + XGL_RESULT res; + + if (!loader.scanned) { + return loader_msg_callback_add(pfnMsgCallback, pUserData); + } + + while (icd) { + res = icd->DbgRegisterMsgCallback(pfnMsgCallback, pUserData); + if (res != XGL_SUCCESS) { + break; + } + + icd = icd->next; + } + + /* roll back on errors */ + if (icd) { + const struct loader_icd *tmp = loader.icds; + + while (tmp != icd) { + tmp->DbgUnregisterMsgCallback(pfnMsgCallback); + tmp = tmp->next; + } + + return res; + } + + return XGL_SUCCESS; +} + +XGL_RESULT XGLAPI xglDbgUnregisterMsgCallback(XGL_DBG_MSG_CALLBACK_FUNCTION pfnMsgCallback) +{ + const struct loader_icd *icd = loader.icds; + XGL_RESULT res = XGL_SUCCESS; + + if (!loader.scanned) { + return loader_msg_callback_remove(pfnMsgCallback); + } + + while (icd) { + XGL_RESULT r = icd->DbgUnregisterMsgCallback(pfnMsgCallback); + if (r != XGL_SUCCESS) { + res = r; + } + icd = icd->next; + } + + return res; +} + +XGL_RESULT XGLAPI xglDbgSetGlobalOption(XGL_DBG_GLOBAL_OPTION dbgOption, XGL_SIZE dataSize, const XGL_VOID* pData) +{ + const struct loader_icd *icd = loader.icds; + XGL_RESULT res = XGL_SUCCESS; + + if (!loader.scanned) { + if (dataSize == 0) + return XGL_ERROR_INVALID_VALUE; + + switch (dbgOption) { + case XGL_DBG_OPTION_DEBUG_ECHO_ENABLE: + loader.debug_echo_enable = *((const bool *) pData); + break; + case XGL_DBG_OPTION_BREAK_ON_ERROR: + loader.break_on_error = *((const bool *) pData); + break; + case XGL_DBG_OPTION_BREAK_ON_WARNING: + loader.break_on_warning = *((const bool *) pData); + break; + default: + res = XGL_ERROR_INVALID_VALUE; + break; + } + + return res; + } + + while (icd) { + XGL_RESULT r = icd->DbgSetGlobalOption(dbgOption, dataSize, pData); + /* unfortunately we cannot roll back */ + if (r != XGL_SUCCESS) { + res = r; + } + + icd = icd->next; + } + + return res; +} |
