/* * 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 #include #include #include #include #include #include #include #include #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; }