From 7ec30239266a04ae5feb066d8d9b9c9f77b751c9 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Sat, 31 Aug 2019 14:39:56 +0200 Subject: Rename internal VAMR files (mainly removes VAMR_ prefix) Also rename VAMR_internal.h to utils.h and move OpenXR include out of it. --- intern/vamr/CMakeLists.txt | 22 +- intern/vamr/intern/CAPI.cc | 81 ++++ intern/vamr/intern/Context.cc | 555 ++++++++++++++++++++++++++++ intern/vamr/intern/Context.h | 130 +++++++ intern/vamr/intern/Event.cc | 70 ++++ intern/vamr/intern/Exception.h | 55 +++ intern/vamr/intern/GraphicsBinding.cc | 308 +++++++++++++++ intern/vamr/intern/IGraphicsBinding.h | 74 ++++ intern/vamr/intern/Session.cc | 575 +++++++++++++++++++++++++++++ intern/vamr/intern/Session.h | 82 ++++ intern/vamr/intern/VAMR.cc | 8 +- intern/vamr/intern/VAMR_Context.cc | 553 --------------------------- intern/vamr/intern/VAMR_Context.h | 130 ------- intern/vamr/intern/VAMR_Event.cc | 67 ---- intern/vamr/intern/VAMR_Exception.h | 55 --- intern/vamr/intern/VAMR_GraphicsBinding.cc | 308 --------------- intern/vamr/intern/VAMR_IGraphicsBinding.h | 74 ---- intern/vamr/intern/VAMR_Session.cc | 574 ---------------------------- intern/vamr/intern/VAMR_Session.h | 82 ---- intern/vamr/intern/VAMR_capi.cc | 80 ---- intern/vamr/intern/VAMR_intern.h | 138 ------- intern/vamr/intern/VAMR_openxr_includes.h | 52 --- intern/vamr/intern/openxr_includes.h | 52 +++ intern/vamr/intern/utils.h | 136 +++++++ 24 files changed, 2134 insertions(+), 2127 deletions(-) create mode 100644 intern/vamr/intern/CAPI.cc create mode 100644 intern/vamr/intern/Context.cc create mode 100644 intern/vamr/intern/Context.h create mode 100644 intern/vamr/intern/Event.cc create mode 100644 intern/vamr/intern/Exception.h create mode 100644 intern/vamr/intern/GraphicsBinding.cc create mode 100644 intern/vamr/intern/IGraphicsBinding.h create mode 100644 intern/vamr/intern/Session.cc create mode 100644 intern/vamr/intern/Session.h delete mode 100644 intern/vamr/intern/VAMR_Context.cc delete mode 100644 intern/vamr/intern/VAMR_Context.h delete mode 100644 intern/vamr/intern/VAMR_Event.cc delete mode 100644 intern/vamr/intern/VAMR_Exception.h delete mode 100644 intern/vamr/intern/VAMR_GraphicsBinding.cc delete mode 100644 intern/vamr/intern/VAMR_IGraphicsBinding.h delete mode 100644 intern/vamr/intern/VAMR_Session.cc delete mode 100644 intern/vamr/intern/VAMR_Session.h delete mode 100644 intern/vamr/intern/VAMR_capi.cc delete mode 100644 intern/vamr/intern/VAMR_intern.h delete mode 100644 intern/vamr/intern/VAMR_openxr_includes.h create mode 100644 intern/vamr/intern/openxr_includes.h create mode 100644 intern/vamr/intern/utils.h diff --git a/intern/vamr/CMakeLists.txt b/intern/vamr/CMakeLists.txt index 71fc535f2af..17765df91e8 100644 --- a/intern/vamr/CMakeLists.txt +++ b/intern/vamr/CMakeLists.txt @@ -26,21 +26,21 @@ set(INC_SYS set(SRC intern/VAMR.cc - intern/VAMR_capi.cc - intern/VAMR_Context.cc - intern/VAMR_Event.cc - intern/VAMR_GraphicsBinding.cc - intern/VAMR_Session.cc + intern/CAPI.cc + intern/Context.cc + intern/Event.cc + intern/GraphicsBinding.cc + intern/Session.cc VAMR_capi.h VAMR_IContext.h VAMR_Types.h - intern/VAMR_Context.h - intern/VAMR_Exception.h - intern/VAMR_intern.h - intern/VAMR_IGraphicsBinding.h - intern/VAMR_openxr_includes.h - intern/VAMR_Session.h + intern/Context.h + intern/Exception.h + intern/utils.h + intern/IGraphicsBinding.h + intern/openxr_includes.h + intern/Session.h ) if(WIN32) diff --git a/intern/vamr/intern/CAPI.cc b/intern/vamr/intern/CAPI.cc new file mode 100644 index 00000000000..575884aee61 --- /dev/null +++ b/intern/vamr/intern/CAPI.cc @@ -0,0 +1,81 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup VAMR + */ + +#include "VAMR_Types.h" +#include "VAMR_capi.h" +#include "VAMR_IContext.h" + +#include "Exception.h" + +#define VAMR_CAPI_CALL(call, ctx) \ + try { \ + call; \ + } \ + catch (VAMR::Exception & e) { \ + (ctx)->dispatchErrorMessage(&e); \ + } + +#define VAMR_CAPI_CALL_RET(call, ctx) \ + try { \ + return call; \ + } \ + catch (VAMR::Exception & e) { \ + (ctx)->dispatchErrorMessage(&e); \ + } + +void VAMR_SessionStart(VAMR_ContextHandle xr_contexthandle, + const VAMR_SessionBeginInfo *begin_info) +{ + VAMR::IContext *xr_context = (VAMR::IContext *)xr_contexthandle; + VAMR_CAPI_CALL(xr_context->startSession(begin_info), xr_context); +} + +void VAMR_SessionEnd(VAMR_ContextHandle xr_contexthandle) +{ + VAMR::IContext *xr_context = (VAMR::IContext *)xr_contexthandle; + VAMR_CAPI_CALL(xr_context->endSession(), xr_context); +} + +int VAMR_SessionIsRunning(const VAMR_ContextHandle xr_contexthandle) +{ + const VAMR::IContext *xr_context = (const VAMR::IContext *)xr_contexthandle; + VAMR_CAPI_CALL_RET(xr_context->isSessionRunning(), xr_context); + return 0; // Only reached if exception is thrown. +} + +void VAMR_SessionDrawViews(VAMR_ContextHandle xr_contexthandle, void *draw_customdata) +{ + VAMR::IContext *xr_context = (VAMR::IContext *)xr_contexthandle; + VAMR_CAPI_CALL(xr_context->drawSessionViews(draw_customdata), xr_context); +} + +void VAMR_GraphicsContextBindFuncs(VAMR_ContextHandle xr_contexthandle, + VAMR_GraphicsContextBindFn bind_fn, + VAMR_GraphicsContextUnbindFn unbind_fn) +{ + VAMR::IContext *xr_context = (VAMR::IContext *)xr_contexthandle; + VAMR_CAPI_CALL(xr_context->setGraphicsContextBindFuncs(bind_fn, unbind_fn), xr_context); +} + +void VAMR_DrawViewFunc(VAMR_ContextHandle xr_contexthandle, VAMR_DrawViewFn draw_view_fn) +{ + VAMR::IContext *xr_context = (VAMR::IContext *)xr_contexthandle; + VAMR_CAPI_CALL(xr_context->setDrawViewFunc(draw_view_fn), xr_context); +} diff --git a/intern/vamr/intern/Context.cc b/intern/vamr/intern/Context.cc new file mode 100644 index 00000000000..5ed44880d64 --- /dev/null +++ b/intern/vamr/intern/Context.cc @@ -0,0 +1,555 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup VAMR + * + * Abstraction for XR (VR, AR, MR, ..) access via OpenXR. + */ + +#include +#include +#include + +#include "VAMR_Types.h" + +#include "openxr_includes.h" + +#include "Context.h" +#include "Exception.h" +#include "Session.h" +#include "utils.h" + +namespace VAMR { + +struct OpenXRInstanceData { + XrInstance instance{XR_NULL_HANDLE}; + XrInstanceProperties instance_properties; + + std::vector extensions; + std::vector layers; + + static PFN_xrCreateDebugUtilsMessengerEXT s_xrCreateDebugUtilsMessengerEXT_fn; + static PFN_xrDestroyDebugUtilsMessengerEXT s_xrDestroyDebugUtilsMessengerEXT_fn; + + XrDebugUtilsMessengerEXT debug_messenger{XR_NULL_HANDLE}; +}; + +PFN_xrCreateDebugUtilsMessengerEXT OpenXRInstanceData::s_xrCreateDebugUtilsMessengerEXT_fn = + nullptr; +PFN_xrDestroyDebugUtilsMessengerEXT OpenXRInstanceData::s_xrDestroyDebugUtilsMessengerEXT_fn = + nullptr; + +VAMR_ErrorHandlerFn Context::s_error_handler = nullptr; +void *Context::s_error_handler_customdata = nullptr; + +/* -------------------------------------------------------------------- */ +/** \name Create, Initialize and Destruct + * + * \{ */ + +Context::Context(const VAMR_ContextCreateInfo *create_info) + : m_oxr(new OpenXRInstanceData()), + m_debug(create_info->context_flag & VAMR_ContextDebug), + m_debug_time(create_info->context_flag & VAMR_ContextDebugTime) +{ +} +Context::~Context() +{ + /* Destroy session data first. Otherwise xrDestroyInstance will implicitly do it, before the + * session had a chance to do so explicitly. */ + m_session = nullptr; + + if (m_oxr->debug_messenger != XR_NULL_HANDLE) { + assert(m_oxr->s_xrDestroyDebugUtilsMessengerEXT_fn != nullptr); + m_oxr->s_xrDestroyDebugUtilsMessengerEXT_fn(m_oxr->debug_messenger); + } + if (m_oxr->instance != XR_NULL_HANDLE) { + CHECK_XR_ASSERT(xrDestroyInstance(m_oxr->instance)); + m_oxr->instance = XR_NULL_HANDLE; + } +} + +void Context::initialize(const VAMR_ContextCreateInfo *create_info) +{ + enumerateApiLayers(); + enumerateExtensions(); + XR_DEBUG_ONLY_CALL(this, printAvailableAPILayersAndExtensionsInfo()); + + m_gpu_binding_type = determineGraphicsBindingTypeToEnable(create_info); + + assert(m_oxr->instance == XR_NULL_HANDLE); + createOpenXRInstance(); + storeInstanceProperties(); + printInstanceInfo(); + XR_DEBUG_ONLY_CALL(this, initDebugMessenger()); +} + +void Context::createOpenXRInstance() +{ + XrInstanceCreateInfo create_info{XR_TYPE_INSTANCE_CREATE_INFO}; + + std::string("Blender").copy(create_info.applicationInfo.applicationName, + XR_MAX_APPLICATION_NAME_SIZE); + create_info.applicationInfo.apiVersion = XR_CURRENT_API_VERSION; + + getAPILayersToEnable(m_enabled_layers); + getExtensionsToEnable(m_enabled_extensions); + create_info.enabledApiLayerCount = m_enabled_layers.size(); + create_info.enabledApiLayerNames = m_enabled_layers.data(); + create_info.enabledExtensionCount = m_enabled_extensions.size(); + create_info.enabledExtensionNames = m_enabled_extensions.data(); + XR_DEBUG_ONLY_CALL(this, printExtensionsAndAPILayersToEnable()); + + CHECK_XR(xrCreateInstance(&create_info, &m_oxr->instance), + "Failed to connect to an OpenXR runtime."); +} + +void Context::storeInstanceProperties() +{ + const std::map runtime_map{ + {"Monado(XRT) by Collabora et al", OPENXR_RUNTIME_MONADO}, + {"Oculus", OPENXR_RUNTIME_OCULUS}, + {"Windows Mixed Reality Runtime", OPENXR_RUNTIME_WMR}}; + decltype(runtime_map)::const_iterator runtime_map_iter; + + m_oxr->instance_properties.type = XR_TYPE_INSTANCE_PROPERTIES; + CHECK_XR(xrGetInstanceProperties(m_oxr->instance, &m_oxr->instance_properties), + "Failed to get OpenXR runtime information. Do you have an active runtime set up?"); + + runtime_map_iter = runtime_map.find(m_oxr->instance_properties.runtimeName); + if (runtime_map_iter != runtime_map.end()) { + m_runtime_id = runtime_map_iter->second; + } +} + +/** \} */ /* Create, Initialize and Destruct */ + +/* -------------------------------------------------------------------- */ +/** \name Debug Printing + * + * \{ */ + +void Context::printInstanceInfo() +{ + assert(m_oxr->instance != XR_NULL_HANDLE); + + printf("Connected to OpenXR runtime: %s (Version %u.%u.%u)\n", + m_oxr->instance_properties.runtimeName, + XR_VERSION_MAJOR(m_oxr->instance_properties.runtimeVersion), + XR_VERSION_MINOR(m_oxr->instance_properties.runtimeVersion), + XR_VERSION_PATCH(m_oxr->instance_properties.runtimeVersion)); +} + +void Context::printAvailableAPILayersAndExtensionsInfo() +{ + puts("Available OpenXR API-layers/extensions:"); + for (XrApiLayerProperties &layer_info : m_oxr->layers) { + printf("Layer: %s\n", layer_info.layerName); + } + for (XrExtensionProperties &ext_info : m_oxr->extensions) { + printf("Extension: %s\n", ext_info.extensionName); + } +} + +void Context::printExtensionsAndAPILayersToEnable() +{ + for (const char *layer_name : m_enabled_layers) { + printf("Enabling OpenXR API-Layer: %s\n", layer_name); + } + for (const char *ext_name : m_enabled_extensions) { + printf("Enabling OpenXR Extension: %s\n", ext_name); + } +} + +static XrBool32 debug_messenger_func(XrDebugUtilsMessageSeverityFlagsEXT /*messageSeverity*/, + XrDebugUtilsMessageTypeFlagsEXT /*messageTypes*/, + const XrDebugUtilsMessengerCallbackDataEXT *callbackData, + void * /*userData*/) +{ + puts("OpenXR Debug Message:"); + puts(callbackData->message); + return XR_FALSE; // OpenXR spec suggests always returning false. +} + +void Context::initDebugMessenger() +{ + XrDebugUtilsMessengerCreateInfoEXT create_info{XR_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT}; + + /* Extension functions need to be obtained through xrGetInstanceProcAddr */ + if (XR_FAILED(xrGetInstanceProcAddr( + m_oxr->instance, + "xrCreateDebugUtilsMessengerEXT", + (PFN_xrVoidFunction *)&m_oxr->s_xrCreateDebugUtilsMessengerEXT_fn)) || + XR_FAILED(xrGetInstanceProcAddr( + m_oxr->instance, + "xrDestroyDebugUtilsMessengerEXT", + (PFN_xrVoidFunction *)&m_oxr->s_xrDestroyDebugUtilsMessengerEXT_fn))) { + m_oxr->s_xrCreateDebugUtilsMessengerEXT_fn = nullptr; + m_oxr->s_xrDestroyDebugUtilsMessengerEXT_fn = nullptr; + + fprintf(stderr, + "Could not use XR_EXT_debug_utils to enable debug prints. Not a fatal error, " + "continuing without the messenger.\n"); + return; + } + + create_info.messageSeverities = XR_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | + XR_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT | + XR_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | + XR_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; + create_info.messageTypes = XR_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | + XR_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | + XR_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; + create_info.userCallback = debug_messenger_func; + + if (XR_FAILED(m_oxr->s_xrCreateDebugUtilsMessengerEXT_fn( + m_oxr->instance, &create_info, &m_oxr->debug_messenger))) { + fprintf(stderr, + "Failed to create OpenXR debug messenger. Not a fatal error, continuing without the " + "messenger.\n"); + return; + } +} + +/** \} */ /* Debug Printing */ + +/* -------------------------------------------------------------------- */ +/** \name Error handling + * + * \{ */ + +void Context::dispatchErrorMessage(const Exception *exception) const +{ + std::ostringstream stream_err_location; + std::string str_err_location; + VAMR_Error error; + + stream_err_location << exception->m_file << ":" << exception->m_line; + str_err_location = stream_err_location.str(); + + error.user_message = exception->m_msg; + error.source_location = str_err_location.c_str(); + error.customdata = s_error_handler_customdata; + + XR_DEBUG_ONLY_CALL(this, + fprintf(stderr, + "Error: \t%s\n\tOpenXR error value: %i\n\tSource: (%s)\n", + error.user_message, + exception->m_res, + error.source_location)); + + /* Potentially destroys VAMR-context */ + s_error_handler(&error); +} + +void Context::setErrorHandler(VAMR_ErrorHandlerFn handler_fn, void *customdata) +{ + s_error_handler = handler_fn; + s_error_handler_customdata = customdata; +} + +/** \} */ /* Error handling */ + +/* -------------------------------------------------------------------- */ +/** \name OpenXR API-Layers and Extensions + * + * \{ */ + +/** + * \param layer_name May be NULL for extensions not belonging to a specific layer. + */ +void Context::enumerateExtensionsEx(std::vector &extensions, + const char *layer_name) +{ + uint32_t extension_count = 0; + + /* Get count for array creation/init first. */ + CHECK_XR(xrEnumerateInstanceExtensionProperties(layer_name, 0, &extension_count, nullptr), + "Failed to query OpenXR runtime information. Do you have an active runtime set up?"); + + if (extension_count == 0) { + /* Extensions are optional, can successfully exit. */ + return; + } + + for (uint32_t i = 0; i < extension_count; i++) { + XrExtensionProperties ext{XR_TYPE_EXTENSION_PROPERTIES}; + extensions.push_back(ext); + } + + /* Actually get the extensions. */ + CHECK_XR(xrEnumerateInstanceExtensionProperties( + layer_name, extension_count, &extension_count, extensions.data()), + "Failed to query OpenXR runtime information. Do you have an active runtime set up?"); +} +void Context::enumerateExtensions() +{ + enumerateExtensionsEx(m_oxr->extensions, nullptr); +} + +void Context::enumerateApiLayers() +{ + uint32_t layer_count = 0; + + /* Get count for array creation/init first. */ + CHECK_XR(xrEnumerateApiLayerProperties(0, &layer_count, nullptr), + "Failed to query OpenXR runtime information. Do you have an active runtime set up?"); + + if (layer_count == 0) { + /* Layers are optional, can safely exit. */ + return; + } + + m_oxr->layers = std::vector(layer_count); + for (XrApiLayerProperties &layer : m_oxr->layers) { + layer.type = XR_TYPE_API_LAYER_PROPERTIES; + } + + /* Actually get the layers. */ + CHECK_XR(xrEnumerateApiLayerProperties(layer_count, &layer_count, m_oxr->layers.data()), + "Failed to query OpenXR runtime information. Do you have an active runtime set up?"); + for (XrApiLayerProperties &layer : m_oxr->layers) { + /* Each layer may have own extensions */ + enumerateExtensionsEx(m_oxr->extensions, layer.layerName); + } +} + +static bool openxr_layer_is_available(const std::vector layers_info, + const std::string &layer_name) +{ + for (const XrApiLayerProperties &layer_info : layers_info) { + if (layer_info.layerName == layer_name) { + return true; + } + } + + return false; +} +static bool openxr_extension_is_available(const std::vector extensions_info, + const std::string &extension_name) +{ + for (const XrExtensionProperties &ext_info : extensions_info) { + if (ext_info.extensionName == extension_name) { + return true; + } + } + + return false; +} + +/** + * Gather an array of names for the API-layers to enable. + */ +void Context::getAPILayersToEnable(std::vector &r_ext_names) +{ + static std::vector try_layers; + + try_layers.clear(); + + XR_DEBUG_ONLY_CALL(this, try_layers.push_back("XR_APILAYER_LUNARG_core_validation")); + + r_ext_names.reserve(try_layers.size()); + + for (const std::string &layer : try_layers) { + if (openxr_layer_is_available(m_oxr->layers, layer)) { + r_ext_names.push_back(layer.c_str()); + } + } +} + +static const char *openxr_ext_name_from_wm_gpu_binding(VAMR_GraphicsBindingType binding) +{ + switch (binding) { + case VAMR_GraphicsBindingTypeOpenGL: + return XR_KHR_OPENGL_ENABLE_EXTENSION_NAME; +#ifdef WIN32 + case VAMR_GraphicsBindingTypeD3D11: + return XR_KHR_D3D11_ENABLE_EXTENSION_NAME; +#endif + case VAMR_GraphicsBindingTypeUnknown: + assert(false); + return nullptr; + } + + return nullptr; +} + +/** + * Gather an array of names for the extensions to enable. + */ +void Context::getExtensionsToEnable(std::vector &r_ext_names) +{ + assert(m_gpu_binding_type != VAMR_GraphicsBindingTypeUnknown); + + const char *gpu_binding = openxr_ext_name_from_wm_gpu_binding(m_gpu_binding_type); + static std::vector try_ext; + + try_ext.clear(); + + /* Try enabling debug extension */ +#ifndef WIN32 + XR_DEBUG_ONLY_CALL(this, try_ext.push_back(XR_EXT_DEBUG_UTILS_EXTENSION_NAME)); +#endif + + r_ext_names.reserve(try_ext.size() + 1); /* + 1 for graphics binding extension. */ + + /* Add graphics binding extension. */ + assert(gpu_binding); + assert(openxr_extension_is_available(m_oxr->extensions, gpu_binding)); + r_ext_names.push_back(gpu_binding); + + for (const std::string &ext : try_ext) { + if (openxr_extension_is_available(m_oxr->extensions, ext)) { + r_ext_names.push_back(ext.c_str()); + } + } +} + +/** + * Decide which graphics binding extension to use based on + * #VAMR_ContextCreateInfo.gpu_binding_candidates and available extensions. + */ +VAMR_GraphicsBindingType Context::determineGraphicsBindingTypeToEnable( + const VAMR_ContextCreateInfo *create_info) +{ + assert(create_info->gpu_binding_candidates != NULL); + assert(create_info->gpu_binding_candidates_count > 0); + + for (uint32_t i = 0; i < create_info->gpu_binding_candidates_count; i++) { + assert(create_info->gpu_binding_candidates[i] != VAMR_GraphicsBindingTypeUnknown); + const char *ext_name = openxr_ext_name_from_wm_gpu_binding( + create_info->gpu_binding_candidates[i]); + if (openxr_extension_is_available(m_oxr->extensions, ext_name)) { + return create_info->gpu_binding_candidates[i]; + } + } + + return VAMR_GraphicsBindingTypeUnknown; +} + +/** \} */ /* OpenXR API-Layers and Extensions */ + +/* -------------------------------------------------------------------- */ +/** \name Session management + * + * Manage session lifetime and delegate public calls to #VAMR::Session. + * \{ */ + +void Context::startSession(const VAMR_SessionBeginInfo *begin_info) +{ + if (m_session == nullptr) { + m_session = std::unique_ptr(new Session(this)); + } + + m_session->start(begin_info); +} +void Context::endSession() +{ + m_session->requestEnd(); +} + +bool Context::isSessionRunning() const +{ + return m_session && m_session->isRunning(); +} + +void Context::drawSessionViews(void *draw_customdata) +{ + m_session->draw(draw_customdata); +} + +/** + * Delegates event to session, allowing context to destruct the session if needed. + */ +void Context::handleSessionStateChange(const XrEventDataSessionStateChanged *lifecycle) +{ + if (m_session && m_session->handleStateChangeEvent(lifecycle) == Session::SESSION_DESTROY) { + m_session = nullptr; + } +} + +/** \} */ /* Session Management */ + +/* -------------------------------------------------------------------- */ +/** \name Public Accessors and Mutators + * + * Public as in, exposed in the VAMR API. + * \{ */ + +/** + * Set context for binding and unbinding a graphics context for a session. The binding callback + * may create a new context thereby. In fact that's the sole reason for this callback approach to + * binding. Just make sure to have an unbind function set that properly destructs. + * + * \param bind_fn Function to retrieve (possibly create) a graphics context. + * \param unbind_fn Function to release (possibly free) a graphics context. + */ +void Context::setGraphicsContextBindFuncs(VAMR_GraphicsContextBindFn bind_fn, + VAMR_GraphicsContextUnbindFn unbind_fn) +{ + if (m_session) { + m_session->unbindGraphicsContext(); + } + m_custom_funcs.gpu_ctx_bind_fn = bind_fn; + m_custom_funcs.gpu_ctx_unbind_fn = unbind_fn; +} + +void Context::setDrawViewFunc(VAMR_DrawViewFn draw_view_fn) +{ + m_custom_funcs.draw_view_fn = draw_view_fn; +} + +/** \} */ /* Public Accessors and Mutators */ + +/* -------------------------------------------------------------------- */ +/** \name VAMR Internal Accessors and Mutators + * + * \{ */ + +OpenXRRuntimeID Context::getOpenXRRuntimeID() const +{ + return m_runtime_id; +} + +const CustomFuncs *Context::getCustomFuncs() const +{ + return &m_custom_funcs; +} + +VAMR_GraphicsBindingType Context::getGraphicsBindingType() const +{ + return m_gpu_binding_type; +} + +XrInstance Context::getInstance() const +{ + return m_oxr->instance; +} + +bool Context::isDebugMode() const +{ + return m_debug; +} + +bool Context::isDebugTimeMode() const +{ + return m_debug_time; +} + +/** \} */ /* VAMR Internal Accessors and Mutators */ + +} // namespace VAMR diff --git a/intern/vamr/intern/Context.h b/intern/vamr/intern/Context.h new file mode 100644 index 00000000000..fcdd6a41b0e --- /dev/null +++ b/intern/vamr/intern/Context.h @@ -0,0 +1,130 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup VAMR + */ + +#ifndef __CONTEXT_H__ +#define __CONTEXT_H__ + +#include +#include + +#include "VAMR_IContext.h" + +namespace VAMR { + +struct CustomFuncs { + /** Function to retrieve (possibly create) a graphics context */ + VAMR_GraphicsContextBindFn gpu_ctx_bind_fn{nullptr}; + /** Function to release (possibly free) a graphics context */ + VAMR_GraphicsContextUnbindFn gpu_ctx_unbind_fn{nullptr}; + + /** Custom per-view draw function for Blender side drawing. */ + VAMR_DrawViewFn draw_view_fn{nullptr}; +}; + +/** + * In some occasions, runtime specific handling is needed, e.g. to work around runtime bugs. + */ +enum OpenXRRuntimeID { + OPENXR_RUNTIME_MONADO, + OPENXR_RUNTIME_OCULUS, + OPENXR_RUNTIME_WMR, /* Windows Mixed Reality */ + + OPENXR_RUNTIME_UNKNOWN +}; + +/** + * \brief Main VAMR container to manage OpenXR through. + * + * Creating a context using #VAMR_ContextCreate involves dynamically connecting to the OpenXR + * runtime, likely reading the OS OpenXR configuration (i.e. active_runtime.json). So this is + * something that should better be done using lazy-initialization. + */ +class Context : public VAMR::IContext { + public: + Context(const VAMR_ContextCreateInfo *create_info); + ~Context(); + void initialize(const VAMR_ContextCreateInfo *create_info); + + void startSession(const VAMR_SessionBeginInfo *begin_info) override; + void endSession() override; + bool isSessionRunning() const override; + void drawSessionViews(void *draw_customdata) override; + + static void setErrorHandler(VAMR_ErrorHandlerFn handler_fn, void *customdata); + void dispatchErrorMessage(const class Exception *exception) const override; + + void setGraphicsContextBindFuncs(VAMR_GraphicsContextBindFn bind_fn, + VAMR_GraphicsContextUnbindFn unbind_fn) override; + void setDrawViewFunc(VAMR_DrawViewFn draw_view_fn) override; + + void handleSessionStateChange(const XrEventDataSessionStateChanged *lifecycle); + + OpenXRRuntimeID getOpenXRRuntimeID() const; + const CustomFuncs *getCustomFuncs() const; + VAMR_GraphicsBindingType getGraphicsBindingType() const; + XrInstance getInstance() const; + bool isDebugMode() const; + bool isDebugTimeMode() const; + + private: + std::unique_ptr m_oxr; + + OpenXRRuntimeID m_runtime_id{OPENXR_RUNTIME_UNKNOWN}; + + /* The active VAMR XR Session. Null while no session runs. */ + std::unique_ptr m_session; + + /** Active graphics binding type. */ + VAMR_GraphicsBindingType m_gpu_binding_type{VAMR_GraphicsBindingTypeUnknown}; + + /** Names of enabled extensions */ + std::vector m_enabled_extensions; + /** Names of enabled API-layers */ + std::vector m_enabled_layers; + + static VAMR_ErrorHandlerFn s_error_handler; + static void *s_error_handler_customdata; + CustomFuncs m_custom_funcs; + + /** Enable debug message prints and OpenXR API validation layers */ + bool m_debug{false}; + bool m_debug_time{false}; + + void createOpenXRInstance(); + void storeInstanceProperties(); + void initDebugMessenger(); + + void printInstanceInfo(); + void printAvailableAPILayersAndExtensionsInfo(); + void printExtensionsAndAPILayersToEnable(); + + void enumerateApiLayers(); + void enumerateExtensions(); + void enumerateExtensionsEx(std::vector &extensions, + const char *layer_name); + void getAPILayersToEnable(std::vector &r_ext_names); + void getExtensionsToEnable(std::vector &r_ext_names); + VAMR_GraphicsBindingType determineGraphicsBindingTypeToEnable( + const VAMR_ContextCreateInfo *create_info); +}; + +} // namespace VAMR + +#endif // __CONTEXT_H__ diff --git a/intern/vamr/intern/Event.cc b/intern/vamr/intern/Event.cc new file mode 100644 index 00000000000..102f897adc0 --- /dev/null +++ b/intern/vamr/intern/Event.cc @@ -0,0 +1,70 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup VAMR + */ + +#include + +#include "VAMR_Types.h" +#include "VAMR_capi.h" + +#include "openxr_includes.h" + +#include "Context.h" +#include "utils.h" + +namespace VAMR { + +static bool VAMR_EventPollNext(XrInstance instance, XrEventDataBuffer &r_event_data) +{ + /* (Re-)initialize as required by specification */ + r_event_data.type = XR_TYPE_EVENT_DATA_BUFFER; + r_event_data.next = nullptr; + + return (xrPollEvent(instance, &r_event_data) == XR_SUCCESS); +} + +::VAMR_TSuccess VAMR_EventsHandle(Context *xr_context) +{ + XrEventDataBuffer event_buffer; /* structure big enought to hold all possible events */ + + if (xr_context == NULL) { + return VAMR_Failure; + } + + while (VAMR_EventPollNext(xr_context->getInstance(), event_buffer)) { + XrEventDataBaseHeader *event = (XrEventDataBaseHeader *)&event_buffer; /* base event struct */ + + switch (event->type) { + case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: + xr_context->handleSessionStateChange((XrEventDataSessionStateChanged *)event); + return VAMR_Success; + + case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING: + VAMR_ContextDestroy((VAMR_ContextHandle)xr_context); + return VAMR_Success; + default: + XR_DEBUG_PRINTF(xr_context, "Unhandled event: %i\n", event->type); + return VAMR_Failure; + } + } + + return VAMR_Failure; +} + +} // namespace VAMR diff --git a/intern/vamr/intern/Exception.h b/intern/vamr/intern/Exception.h new file mode 100644 index 00000000000..060e8f3a458 --- /dev/null +++ b/intern/vamr/intern/Exception.h @@ -0,0 +1,55 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup VAMR + */ + +#ifndef __EXCEPTION_H__ +#define __EXCEPTION_H__ + +#include + +namespace VAMR { + +/** + * For now just a general Exception type (note that it's in namespace VAMR, so name shouldn't cause + * conflicts). + */ +class Exception : public std::exception { + friend class Context; + + public: + Exception(const char *msg, const char *file, int line, int res = 0) + : std::exception(), m_msg(msg), m_file(file), m_line(line), m_res(res) + { + } + + const char *what() const noexcept override + { + return m_msg; + } + + private: + const char *m_msg; + const char *m_file; + const int m_line; + int m_res; +}; + +} // namespace VAMR + +#endif // __EXCEPTION_H__ diff --git a/intern/vamr/intern/GraphicsBinding.cc b/intern/vamr/intern/GraphicsBinding.cc new file mode 100644 index 00000000000..f9b4216b3e3 --- /dev/null +++ b/intern/vamr/intern/GraphicsBinding.cc @@ -0,0 +1,308 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup VAMR + */ + +#include +#include +#include + +#if 0 +# if defined(WITH_X11) +# include "GHOST_ContextGLX.h" +# elif defined(WIN32) +# include "GHOST_ContextWGL.h" +# include "GHOST_ContextD3D.h" +# endif +#endif +#include "VAMR_capi.h" + +#include "IGraphicsBinding.h" +#include "utils.h" + +namespace VAMR { + +static bool choose_swapchain_format_from_candidates(std::vector gpu_binding_formats, + std::vector runtime_formats, + int64_t *r_result) +{ + if (gpu_binding_formats.empty()) { + return false; + } + + auto res = std::find_first_of(gpu_binding_formats.begin(), + gpu_binding_formats.end(), + runtime_formats.begin(), + runtime_formats.end()); + if (res == gpu_binding_formats.end()) { + return false; + } + + *r_result = *res; + return true; +} + +class GraphicsBindingOpenGL : public IGraphicsBinding { + public: + ~GraphicsBindingOpenGL() + { + if (m_fbo != 0) { + glDeleteFramebuffers(1, &m_fbo); + } + } + + bool checkVersionRequirements(GHOST_Context *ghost_ctx, + XrInstance instance, + XrSystemId system_id, + std::string *r_requirement_info) const override + { +#if 0 +# if defined(WITH_X11) + GHOST_ContextGLX *ctx_gl = static_cast(ghost_ctx); +# else + GHOST_ContextWGL *ctx_gl = static_cast(ghost_ctx); +# endif + XrGraphicsRequirementsOpenGLKHR gpu_requirements{XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_KHR}; + const XrVersion gl_version = XR_MAKE_VERSION( + ctx_gl->m_contextMajorVersion, ctx_gl->m_contextMinorVersion, 0); + + xrGetOpenGLGraphicsRequirementsKHR(instance, system_id, &gpu_requirements); + + if (r_requirement_info) { + std::ostringstream strstream; + strstream << "Min OpenGL version " + << XR_VERSION_MAJOR(gpu_requirements.minApiVersionSupported) << "." + << XR_VERSION_MINOR(gpu_requirements.minApiVersionSupported) << std::endl; + strstream << "Max OpenGL version " + << XR_VERSION_MAJOR(gpu_requirements.maxApiVersionSupported) << "." + << XR_VERSION_MINOR(gpu_requirements.maxApiVersionSupported) << std::endl; + + *r_requirement_info = strstream.str(); + } + + return (gl_version >= gpu_requirements.minApiVersionSupported) && + (gl_version <= gpu_requirements.maxApiVersionSupported); +#endif + return true; + } + + void initFromGhostContext(GHOST_Context *ghost_ctx) override + { +#if 0 +# if defined(WITH_X11) + GHOST_ContextGLX *ctx_glx = static_cast(ghost_ctx); + XVisualInfo *visual_info = glXGetVisualFromFBConfig(ctx_glx->m_display, ctx_glx->m_fbconfig); + + oxr_binding.glx.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_XLIB_KHR; + oxr_binding.glx.xDisplay = ctx_glx->m_display; + oxr_binding.glx.glxFBConfig = ctx_glx->m_fbconfig; + oxr_binding.glx.glxDrawable = ctx_glx->m_window; + oxr_binding.glx.glxContext = ctx_glx->m_context; + oxr_binding.glx.visualid = visual_info->visualid; +# elif defined(WIN32) + GHOST_ContextWGL *ctx_wgl = static_cast(ghost_ctx); + + oxr_binding.wgl.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR; + oxr_binding.wgl.hDC = ctx_wgl->m_hDC; + oxr_binding.wgl.hGLRC = ctx_wgl->m_hGLRC; +# endif +#endif + + /* Generate a framebuffer to use for blitting into the texture */ + glGenFramebuffers(1, &m_fbo); + } + + bool chooseSwapchainFormat(const std::vector &runtime_formats, + int64_t *r_result) const override + { + std::vector gpu_binding_formats = {GL_RGBA8}; + return choose_swapchain_format_from_candidates(gpu_binding_formats, runtime_formats, r_result); + } + + std::vector createSwapchainImages(uint32_t image_count) override + { + std::vector ogl_images(image_count); + std::vector base_images; + + // Need to return vector of base header pointers, so of a different type. Need to build a new + // list with this type, and keep the initial one alive. + for (XrSwapchainImageOpenGLKHR &image : ogl_images) { + image.type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR; + base_images.push_back(reinterpret_cast(&image)); + } + + // Keep alive. + m_image_cache.push_back(std::move(ogl_images)); + + return base_images; + } + + void submitToSwapchain(XrSwapchainImageBaseHeader *swapchain_image, + const VAMR_DrawViewInfo *draw_info) override + { + XrSwapchainImageOpenGLKHR *ogl_swapchain_image = reinterpret_cast( + swapchain_image); + + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbo); + + glFramebufferTexture2D( + GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ogl_swapchain_image->image, 0); + + glBlitFramebuffer(draw_info->ofsx, + draw_info->ofsy, + draw_info->ofsx + draw_info->width, + draw_info->ofsy + draw_info->height, + draw_info->ofsx, + draw_info->ofsy, + draw_info->ofsx + draw_info->width, + draw_info->ofsy + draw_info->height, + GL_COLOR_BUFFER_BIT, + GL_LINEAR); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + } + + private: + std::list> m_image_cache; + GLuint m_fbo{0}; +}; + +#ifdef WIN32 +class GraphicsBindingD3D : public IGraphicsBinding { + public: + ~GraphicsBindingD3D() + { + if (m_shared_resource) { + m_ghost_ctx->disposeSharedOpenGLResource(m_shared_resource); + } + } + + bool checkVersionRequirements(GHOST_Context *ghost_ctx, + XrInstance instance, + XrSystemId system_id, + std::string *r_requirement_info) const override + { + + GHOST_ContextD3D *ctx_dx = static_cast(ghost_ctx); + XrGraphicsRequirementsD3D11KHR gpu_requirements{XR_TYPE_GRAPHICS_REQUIREMENTS_D3D11_KHR}; + + xrGetD3D11GraphicsRequirementsKHR(instance, system_id, &gpu_requirements); + + if (r_requirement_info) { + std::ostringstream strstream; + strstream << "Min DirectX 11 Feature Level " << gpu_requirements.minFeatureLevel + << std::endl; + + *r_requirement_info = std::move(strstream.str()); + } + + return ctx_dx->m_device->GetFeatureLevel() >= gpu_requirements.minFeatureLevel; + } + + void initFromGhostContext(GHOST_Context *ghost_ctx) override + { + GHOST_ContextD3D *ctx_d3d = static_cast(ghost_ctx); + + oxr_binding.d3d11.type = XR_TYPE_GRAPHICS_BINDING_D3D11_KHR; + oxr_binding.d3d11.device = ctx_d3d->m_device; + m_ghost_ctx = ctx_d3d; + } + + bool chooseSwapchainFormat(const std::vector &runtime_formats, + int64_t *r_result) const override + { + std::vector gpu_binding_formats = {DXGI_FORMAT_R8G8B8A8_UNORM}; + return choose_swapchain_format_from_candidates(gpu_binding_formats, runtime_formats, r_result); + } + + std::vector createSwapchainImages(uint32_t image_count) override + { + std::vector d3d_images(image_count); + std::vector base_images; + + // Need to return vector of base header pointers, so of a different type. Need to build a new + // list with this type, and keep the initial one alive. + for (XrSwapchainImageD3D11KHR &image : d3d_images) { + image.type = XR_TYPE_SWAPCHAIN_IMAGE_D3D11_KHR; + base_images.push_back(reinterpret_cast(&image)); + } + + // Keep alive. + m_image_cache.push_back(std::move(d3d_images)); + + return base_images; + } + + void submitToSwapchain(XrSwapchainImageBaseHeader *swapchain_image, + const VAMR_DrawViewInfo *draw_info) override + { + XrSwapchainImageD3D11KHR *d3d_swapchain_image = reinterpret_cast( + swapchain_image); + +# if 0 + /* Ideally we'd just create a render target view for the OpenXR swapchain image texture and + * blit from the OpenGL context into it. The NV_DX_interop extension doesn't want to work with + * this though. At least not with Optimus hardware. See: + * https://github.com/mpv-player/mpv/issues/2949#issuecomment-197262807. + */ + + ID3D11RenderTargetView *rtv; + CD3D11_RENDER_TARGET_VIEW_DESC rtv_desc(D3D11_RTV_DIMENSION_TEXTURE2D, + DXGI_FORMAT_R8G8B8A8_UNORM); + + m_ghost_ctx->m_device->CreateRenderTargetView(d3d_swapchain_image->texture, &rtv_desc, &rtv); + if (!m_shared_resource) { + m_shared_resource = m_ghost_ctx->createSharedOpenGLResource( + draw_info->width, draw_info->height, rtv); + } + m_ghost_ctx->blitFromOpenGLContext(m_shared_resource, draw_info->width, draw_info->height); +# else + if (!m_shared_resource) { + m_shared_resource = m_ghost_ctx->createSharedOpenGLResource(draw_info->width, + draw_info->height); + } + m_ghost_ctx->blitFromOpenGLContext(m_shared_resource, draw_info->width, draw_info->height); + + m_ghost_ctx->m_device_ctx->OMSetRenderTargets(0, nullptr, nullptr); + m_ghost_ctx->m_device_ctx->CopyResource(d3d_swapchain_image->texture, + m_ghost_ctx->getSharedTexture2D(m_shared_resource)); +# endif + } + + private: + GHOST_ContextD3D *m_ghost_ctx; + GHOST_SharedOpenGLResource *m_shared_resource; + std::list> m_image_cache; +}; +#endif // WIN32 + +std::unique_ptr GraphicsBindingCreateFromType(VAMR_GraphicsBindingType type) +{ + switch (type) { + case VAMR_GraphicsBindingTypeOpenGL: + return std::unique_ptr(new GraphicsBindingOpenGL()); +#ifdef WIN32 + case VAMR_GraphicsBindingTypeD3D11: + return std::unique_ptr(new GraphicsBindingD3D()); +#endif + default: + return nullptr; + } +} + +} // namespace VAMR diff --git a/intern/vamr/intern/IGraphicsBinding.h b/intern/vamr/intern/IGraphicsBinding.h new file mode 100644 index 00000000000..00134f8e5cd --- /dev/null +++ b/intern/vamr/intern/IGraphicsBinding.h @@ -0,0 +1,74 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup VAMR + */ + +#ifndef __IGRAPHICSBINDING_H__ +#define __IGRAPHICSBINDING_H__ + +#include +#include +#include + +#include "openxr_includes.h" + +namespace VAMR { + +class IGraphicsBinding { + friend std::unique_ptr GraphicsBindingCreateFromType( + VAMR_GraphicsBindingType type); + + public: + union { +#if defined(WITH_X11) + XrGraphicsBindingOpenGLXlibKHR glx; +#elif defined(WIN32) + XrGraphicsBindingOpenGLWin32KHR wgl; + XrGraphicsBindingD3D11KHR d3d11; +#endif + } oxr_binding; + + /** + * Does __not__ require this object is initialized (can be called prior to + * #initFromGhostContext). It's actually meant to be called first. + * + * \param r_requirement_info Return argument to retrieve an informal string on the requirements + * to be met. Useful for error/debug messages. + */ + virtual bool checkVersionRequirements(class GHOST_Context *ghost_ctx, + XrInstance instance, + XrSystemId system_id, + std::string *r_requirement_info) const = 0; + virtual void initFromGhostContext(class GHOST_Context *ghost_ctx) = 0; + virtual bool chooseSwapchainFormat(const std::vector &runtime_formats, + int64_t *r_result) const = 0; + virtual std::vector createSwapchainImages( + uint32_t image_count) = 0; + virtual void submitToSwapchain(XrSwapchainImageBaseHeader *swapchain_image, + const VAMR_DrawViewInfo *draw_info) = 0; + + protected: + /* Use GraphicsBindingCreateFromType */ + IGraphicsBinding() = default; +}; + +std::unique_ptr GraphicsBindingCreateFromType(VAMR_GraphicsBindingType type); + +} // namespace VAMR + +#endif /* __IGRAPHICSBINDING_H__ */ diff --git a/intern/vamr/intern/Session.cc b/intern/vamr/intern/Session.cc new file mode 100644 index 00000000000..af64a01dc61 --- /dev/null +++ b/intern/vamr/intern/Session.cc @@ -0,0 +1,575 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup VAMR + */ + +#include +#include +#include +#include +#include +#include + +#include "VAMR_capi.h" + +#include "openxr_includes.h" + +#include "Context.h" +#include "Exception.h" +#include "IGraphicsBinding.h" +#include "Session.h" +#include "utils.h" + +namespace VAMR { + +struct OpenXRSessionData { + XrSystemId system_id{XR_NULL_SYSTEM_ID}; + XrSession session{XR_NULL_HANDLE}; + XrSessionState session_state{XR_SESSION_STATE_UNKNOWN}; + + // Only stereo rendering supported now. + const XrViewConfigurationType view_type{XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO}; + XrSpace reference_space; + std::vector views; + std::vector swapchains; + std::map> swapchain_images; + int32_t swapchain_image_width, swapchain_image_height; +}; + +struct DrawInfo { + XrFrameState frame_state; + + /** Time at frame start to benchmark frame render durations. */ + std::chrono::high_resolution_clock::time_point frame_begin_time; + /* Time previous frames took for rendering (in ms) */ + std::list last_frame_times; +}; + +/* -------------------------------------------------------------------- */ +/** \name Create, Initialize and Destruct + * + * \{ */ + +Session::Session(Context *xr_context) : m_context(xr_context), m_oxr(new OpenXRSessionData()) +{ +} + +Session::~Session() +{ + unbindGraphicsContext(); + + for (XrSwapchain &swapchain : m_oxr->swapchains) { + CHECK_XR_ASSERT(xrDestroySwapchain(swapchain)); + } + m_oxr->swapchains.clear(); + if (m_oxr->reference_space != XR_NULL_HANDLE) { + CHECK_XR_ASSERT(xrDestroySpace(m_oxr->reference_space)); + } + if (m_oxr->session != XR_NULL_HANDLE) { + CHECK_XR_ASSERT(xrDestroySession(m_oxr->session)); + } + + m_oxr->session = XR_NULL_HANDLE; + m_oxr->session_state = XR_SESSION_STATE_UNKNOWN; +} + +/** + * A system in OpenXR the combination of some sort of HMD plus controllers and whatever other + * devices are managed through OpenXR. So this attempts to init the HMD and the other devices. + */ +void Session::initSystem() +{ + assert(m_context->getInstance() != XR_NULL_HANDLE); + assert(m_oxr->system_id == XR_NULL_SYSTEM_ID); + + XrSystemGetInfo system_info{}; + system_info.type = XR_TYPE_SYSTEM_GET_INFO; + system_info.formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY; + + CHECK_XR(xrGetSystem(m_context->getInstance(), &system_info, &m_oxr->system_id), + "Failed to get device information. Is a device plugged in?"); +} + +/** \} */ /* Create, Initialize and Destruct */ + +/* -------------------------------------------------------------------- */ +/** \name State Management + * + * \{ */ + +static void create_reference_space(OpenXRSessionData *oxr, const VAMR_Pose *base_pose) +{ + XrReferenceSpaceCreateInfo create_info{XR_TYPE_REFERENCE_SPACE_CREATE_INFO}; + create_info.poseInReferenceSpace.orientation.w = 1.0f; + + create_info.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL; +#if 0 +/* Proper reference space set up is not supported yet. We simply hand OpenXR + * the global space as reference space and apply its pose onto the active + * camera matrix to get a basic viewing experience going. If there's no active + * camera with stick to the world origin. + * + * Once we have proper reference space set up (i.e. a way to define origin, up- + * direction and an initial view rotation perpendicular to the up-direction), + * we can hand OpenXR a proper reference pose/space. + */ + create_info.poseInReferenceSpace.position.x = base_pose->position[0]; + create_info.poseInReferenceSpace.position.y = base_pose->position[2]; + create_info.poseInReferenceSpace.position.z = -base_pose->position[1]; + create_info.poseInReferenceSpace.orientation.x = base_pose->orientation_quat[1]; + create_info.poseInReferenceSpace.orientation.y = base_pose->orientation_quat[3]; + create_info.poseInReferenceSpace.orientation.z = -base_pose->orientation_quat[2]; + create_info.poseInReferenceSpace.orientation.w = base_pose->orientation_quat[0]; +#else + (void)base_pose; +#endif + + CHECK_XR(xrCreateReferenceSpace(oxr->session, &create_info, &oxr->reference_space), + "Failed to create reference space."); +} + +void Session::start(const VAMR_SessionBeginInfo *begin_info) +{ + assert(m_context->getInstance() != XR_NULL_HANDLE); + assert(m_oxr->session == XR_NULL_HANDLE); + if (m_context->getCustomFuncs()->gpu_ctx_bind_fn == nullptr) { + THROW_XR( + "Invalid API usage: No way to bind graphics context to the XR session. Call " + "VAMR_GraphicsContextBindFuncs() with valid parameters before starting the " + "session (through VAMR_SessionStart())."); + } + + initSystem(); + + bindGraphicsContext(); + if (m_gpu_ctx == nullptr) { + THROW_XR( + "Invalid API usage: No graphics context returned through the callback set with " + "VAMR_GraphicsContextBindFuncs(). This is required for session starting (through " + "VAMR_SessionStart())."); + } + + std::string requirement_str; + m_gpu_binding = GraphicsBindingCreateFromType(m_context->getGraphicsBindingType()); + if (!m_gpu_binding->checkVersionRequirements( + m_gpu_ctx, m_context->getInstance(), m_oxr->system_id, &requirement_str)) { + std::ostringstream strstream; + strstream << "Available graphics context version does not meet the following requirements: " + << requirement_str; + THROW_XR(strstream.str().c_str()); + } + m_gpu_binding->initFromGhostContext(m_gpu_ctx); + + XrSessionCreateInfo create_info{}; + create_info.type = XR_TYPE_SESSION_CREATE_INFO; + create_info.systemId = m_oxr->system_id; + create_info.next = &m_gpu_binding->oxr_binding; + + CHECK_XR(xrCreateSession(m_context->getInstance(), &create_info, &m_oxr->session), + "Failed to create VR session. The OpenXR runtime may have additional requirements for " + "the graphics driver that are not met. Other causes are possible too however.\nTip: " + "The --debug-xr command line option for Blender might allow the runtime to output " + "detailed error information to the command line."); + + prepareDrawing(); + create_reference_space(m_oxr.get(), &begin_info->base_pose); +} + +void Session::requestEnd() +{ + xrRequestExitSession(m_oxr->session); +} + +void Session::end() +{ + assert(m_oxr->session != XR_NULL_HANDLE); + + CHECK_XR(xrEndSession(m_oxr->session), "Failed to cleanly end the VR session."); + unbindGraphicsContext(); + m_draw_info = nullptr; +} + +Session::eLifeExpectancy Session::handleStateChangeEvent( + const XrEventDataSessionStateChanged *lifecycle) +{ + m_oxr->session_state = lifecycle->state; + + /* Runtime may send events for apparently destroyed session. Our handle should be NULL then. */ + assert((m_oxr->session == XR_NULL_HANDLE) || (m_oxr->session == lifecycle->session)); + + switch (lifecycle->state) { + case XR_SESSION_STATE_READY: { + XrSessionBeginInfo begin_info{}; + + begin_info.type = XR_TYPE_SESSION_BEGIN_INFO; + begin_info.primaryViewConfigurationType = m_oxr->view_type; + CHECK_XR(xrBeginSession(m_oxr->session, &begin_info), + "Failed to cleanly begin the VR session."); + break; + } + case XR_SESSION_STATE_STOPPING: + /* Runtime will change state to STATE_EXITING, don't destruct session yet. */ + end(); + break; + case XR_SESSION_STATE_EXITING: + case XR_SESSION_STATE_LOSS_PENDING: + return SESSION_DESTROY; + default: + break; + } + + return SESSION_KEEP_ALIVE; +} +/** \} */ /* State Management */ + +/* -------------------------------------------------------------------- */ +/** \name Drawing + * + * \{ */ + +static std::vector swapchain_images_create( + XrSwapchain swapchain, IGraphicsBinding *gpu_binding) +{ + std::vector images; + uint32_t image_count; + + CHECK_XR(xrEnumerateSwapchainImages(swapchain, 0, &image_count, nullptr), + "Failed to get count of swapchain images to create for the VR session."); + images = gpu_binding->createSwapchainImages(image_count); + CHECK_XR(xrEnumerateSwapchainImages(swapchain, images.size(), &image_count, images[0]), + "Failed to create swapchain images for the VR session."); + + return images; +} + +static unique_oxr_ptr swapchain_create(const XrSession session, + IGraphicsBinding *gpu_binding, + const XrViewConfigurationView *xr_view) +{ + XrSwapchainCreateInfo create_info{XR_TYPE_SWAPCHAIN_CREATE_INFO}; + unique_oxr_ptr swapchain(xrDestroySwapchain); + uint32_t format_count = 0; + int64_t chosen_format; + + CHECK_XR(xrEnumerateSwapchainFormats(session, 0, &format_count, nullptr), + "Failed to get count of swapchain image formats."); + std::vector swapchain_formats(format_count); + CHECK_XR(xrEnumerateSwapchainFormats( + session, swapchain_formats.size(), &format_count, swapchain_formats.data()), + "Failed to get swapchain image formats."); + assert(swapchain_formats.size() == format_count); + + if (!gpu_binding->chooseSwapchainFormat(swapchain_formats, &chosen_format)) { + THROW_XR("Error: No format matching OpenXR runtime supported swapchain formats found."); + } + + create_info.usageFlags = XR_SWAPCHAIN_USAGE_SAMPLED_BIT | + XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT; + create_info.format = chosen_format; + create_info.sampleCount = xr_view->recommendedSwapchainSampleCount; + create_info.width = xr_view->recommendedImageRectWidth; + create_info.height = xr_view->recommendedImageRectHeight; + create_info.faceCount = 1; + create_info.arraySize = 1; + create_info.mipCount = 1; + CHECK_XR(swapchain.construct(xrCreateSwapchain, session, &create_info), + "Failed to create OpenXR swapchain."); + + return swapchain; +} + +void Session::prepareDrawing() +{ + std::vector view_configs; + uint32_t view_count; + + CHECK_XR( + xrEnumerateViewConfigurationViews( + m_context->getInstance(), m_oxr->system_id, m_oxr->view_type, 0, &view_count, nullptr), + "Failed to get count of view configurations."); + view_configs.resize(view_count, {XR_TYPE_VIEW_CONFIGURATION_VIEW}); + CHECK_XR(xrEnumerateViewConfigurationViews(m_context->getInstance(), + m_oxr->system_id, + m_oxr->view_type, + view_configs.size(), + &view_count, + view_configs.data()), + "Failed to get count of view configurations."); + + for (const XrViewConfigurationView &view : view_configs) { + unique_oxr_ptr swapchain = swapchain_create( + m_oxr->session, m_gpu_binding.get(), &view); + auto images = swapchain_images_create(swapchain.get(), m_gpu_binding.get()); + + m_oxr->swapchain_image_width = view.recommendedImageRectWidth; + m_oxr->swapchain_image_height = view.recommendedImageRectHeight; + m_oxr->swapchains.push_back(swapchain.get()); + m_oxr->swapchain_images.insert(std::make_pair(swapchain.get(), std::move(images))); + + swapchain.release(); + } + + m_oxr->views.resize(view_count, {XR_TYPE_VIEW}); + + m_draw_info = std::unique_ptr(new DrawInfo()); +} + +void Session::beginFrameDrawing() +{ + XrFrameWaitInfo wait_info{XR_TYPE_FRAME_WAIT_INFO}; + XrFrameBeginInfo begin_info{XR_TYPE_FRAME_BEGIN_INFO}; + XrFrameState frame_state{XR_TYPE_FRAME_STATE}; + + // TODO Blocking call. Does this intefer with other drawing? + CHECK_XR(xrWaitFrame(m_oxr->session, &wait_info, &frame_state), + "Failed to synchronize frame rates between Blender and the device."); + + CHECK_XR(xrBeginFrame(m_oxr->session, &begin_info), + "Failed to submit frame rendering start state."); + + m_draw_info->frame_state = frame_state; + + if (m_context->isDebugTimeMode()) { + m_draw_info->frame_begin_time = std::chrono::high_resolution_clock::now(); + } +} + +static void print_debug_timings(DrawInfo *draw_info) +{ + /** Render time of last 8 frames (in ms) to calculate an average. */ + std::chrono::duration duration = std::chrono::high_resolution_clock::now() - + draw_info->frame_begin_time; + const double duration_ms = duration.count(); + const int avg_frame_count = 8; + double avg_ms_tot = 0.0; + + if (draw_info->last_frame_times.size() >= avg_frame_count) { + draw_info->last_frame_times.pop_front(); + assert(draw_info->last_frame_times.size() == avg_frame_count - 1); + } + draw_info->last_frame_times.push_back(duration_ms); + for (double ms_iter : draw_info->last_frame_times) { + avg_ms_tot += ms_iter; + } + + printf("VR frame render time: %.0fms - %.2f FPS (%.2f FPS 8 frames average)\n", + duration_ms, + 1000.0 / duration_ms, + 1000.0 / (avg_ms_tot / draw_info->last_frame_times.size())); +} + +void Session::endFrameDrawing(std::vector *layers) +{ + XrFrameEndInfo end_info{XR_TYPE_FRAME_END_INFO}; + + end_info.displayTime = m_draw_info->frame_state.predictedDisplayTime; + end_info.environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE; + end_info.layerCount = layers->size(); + end_info.layers = layers->data(); + + CHECK_XR(xrEndFrame(m_oxr->session, &end_info), "Failed to submit rendered frame."); + + if (m_context->isDebugTimeMode()) { + print_debug_timings(m_draw_info.get()); + } +} + +void Session::draw(void *draw_customdata) +{ + std::vector + projection_layer_views; // Keep alive until xrEndFrame() call! + XrCompositionLayerProjection proj_layer; + std::vector layers; + + beginFrameDrawing(); + + if (m_draw_info->frame_state.shouldRender) { + proj_layer = drawLayer(projection_layer_views, draw_customdata); + layers.push_back(reinterpret_cast(&proj_layer)); + } + + endFrameDrawing(&layers); +} + +static void vamr_draw_view_info_from_view(const XrView &view, VAMR_DrawViewInfo &r_info) +{ +#if 0 + /* Set and convert to Blender coodinate space */ + r_info.pose.position[0] = view.pose.position.x; + r_info.pose.position[1] = -view.pose.position.z; + r_info.pose.position[2] = view.pose.position.y; + r_info.pose.orientation_quat[0] = view.pose.orientation.w; + r_info.pose.orientation_quat[1] = view.pose.orientation.x; + r_info.pose.orientation_quat[2] = -view.pose.orientation.z; + r_info.pose.orientation_quat[3] = view.pose.orientation.y; +#else + r_info.pose.position[0] = view.pose.position.x; + r_info.pose.position[1] = view.pose.position.y; + r_info.pose.position[2] = view.pose.position.z; + r_info.pose.orientation_quat[0] = view.pose.orientation.w; + r_info.pose.orientation_quat[1] = view.pose.orientation.x; + r_info.pose.orientation_quat[2] = view.pose.orientation.y; + r_info.pose.orientation_quat[3] = view.pose.orientation.z; +#endif + + r_info.fov.angle_left = view.fov.angleLeft; + r_info.fov.angle_right = view.fov.angleRight; + r_info.fov.angle_up = view.fov.angleUp; + r_info.fov.angle_down = view.fov.angleDown; +} + +static bool vamr_draw_view_expects_srgb_buffer(const Context *context) +{ + /* WMR seems to be faulty and doesn't do OETF transform correctly. So expect a SRGB buffer to + * compensate. */ + return context->getOpenXRRuntimeID() == OPENXR_RUNTIME_WMR; +} + +void Session::drawView(XrSwapchain swapchain, + XrCompositionLayerProjectionView &proj_layer_view, + XrView &view, + void *draw_customdata) +{ + XrSwapchainImageAcquireInfo acquire_info{XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO}; + XrSwapchainImageWaitInfo wait_info{XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO}; + XrSwapchainImageReleaseInfo release_info{XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO}; + XrSwapchainImageBaseHeader *swapchain_image; + VAMR_DrawViewInfo draw_view_info{}; + uint32_t swapchain_idx; + + CHECK_XR(xrAcquireSwapchainImage(swapchain, &acquire_info, &swapchain_idx), + "Failed to acquire swapchain image for the VR session."); + wait_info.timeout = XR_INFINITE_DURATION; + CHECK_XR(xrWaitSwapchainImage(swapchain, &wait_info), + "Failed to acquire swapchain image for the VR session."); + + proj_layer_view.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW; + proj_layer_view.pose = view.pose; + proj_layer_view.fov = view.fov; + proj_layer_view.subImage.swapchain = swapchain; + proj_layer_view.subImage.imageRect.offset = {0, 0}; + proj_layer_view.subImage.imageRect.extent = {m_oxr->swapchain_image_width, + m_oxr->swapchain_image_height}; + + swapchain_image = m_oxr->swapchain_images[swapchain][swapchain_idx]; + + draw_view_info.expects_srgb_buffer = vamr_draw_view_expects_srgb_buffer(m_context); + draw_view_info.ofsx = proj_layer_view.subImage.imageRect.offset.x; + draw_view_info.ofsy = proj_layer_view.subImage.imageRect.offset.y; + draw_view_info.width = proj_layer_view.subImage.imageRect.extent.width; + draw_view_info.height = proj_layer_view.subImage.imageRect.extent.height; + vamr_draw_view_info_from_view(view, draw_view_info); + + m_context->getCustomFuncs()->draw_view_fn(&draw_view_info, draw_customdata); + m_gpu_binding->submitToSwapchain(swapchain_image, &draw_view_info); + + CHECK_XR(xrReleaseSwapchainImage(swapchain, &release_info), + "Failed to release swapchain image used to submit VR session frame."); +} + +XrCompositionLayerProjection Session::drawLayer( + std::vector &proj_layer_views, void *draw_customdata) +{ + XrViewLocateInfo viewloc_info{XR_TYPE_VIEW_LOCATE_INFO}; + XrViewState view_state{XR_TYPE_VIEW_STATE}; + XrCompositionLayerProjection layer{XR_TYPE_COMPOSITION_LAYER_PROJECTION}; + uint32_t view_count; + + viewloc_info.viewConfigurationType = m_oxr->view_type; + viewloc_info.displayTime = m_draw_info->frame_state.predictedDisplayTime; + viewloc_info.space = m_oxr->reference_space; + + CHECK_XR(xrLocateViews(m_oxr->session, + &viewloc_info, + &view_state, + m_oxr->views.size(), + &view_count, + m_oxr->views.data()), + "Failed to query frame view and projection state."); + assert(m_oxr->swapchains.size() == view_count); + + proj_layer_views.resize(view_count); + + for (uint32_t view_idx = 0; view_idx < view_count; view_idx++) { + drawView(m_oxr->swapchains[view_idx], + proj_layer_views[view_idx], + m_oxr->views[view_idx], + draw_customdata); + } + + layer.space = m_oxr->reference_space; + layer.viewCount = proj_layer_views.size(); + layer.views = proj_layer_views.data(); + + return layer; +} + +/** \} */ /* Drawing */ + +/* -------------------------------------------------------------------- */ +/** \name State Queries + * + * \{ */ + +bool Session::isRunning() const +{ + if (m_oxr->session == XR_NULL_HANDLE) { + return false; + } + switch (m_oxr->session_state) { + case XR_SESSION_STATE_READY: + case XR_SESSION_STATE_SYNCHRONIZED: + case XR_SESSION_STATE_VISIBLE: + case XR_SESSION_STATE_FOCUSED: + return true; + default: + return false; + } +} + +/** \} */ /* State Queries */ + +/* -------------------------------------------------------------------- */ +/** \name Graphics Context Injection + * + * Sessions need access to Ghost graphics context information. Additionally, this API allows + * creating contexts on the fly (created on start, destructed on end). For this, callbacks to bind + * (potentially create) and unbind (potentially destruct) a Ghost graphics context have to be set, + * which will be called on session start and end respectively. + * + * \{ */ + +void Session::bindGraphicsContext() +{ + const CustomFuncs *custom_funcs = m_context->getCustomFuncs(); + assert(custom_funcs->gpu_ctx_bind_fn); + m_gpu_ctx = static_cast( + custom_funcs->gpu_ctx_bind_fn(m_context->getGraphicsBindingType())); +} +void Session::unbindGraphicsContext() +{ + const CustomFuncs *custom_funcs = m_context->getCustomFuncs(); + if (custom_funcs->gpu_ctx_unbind_fn) { + custom_funcs->gpu_ctx_unbind_fn(m_context->getGraphicsBindingType(), m_gpu_ctx); + } + m_gpu_ctx = nullptr; +} + +/** \} */ /* Graphics Context Injection */ + +} // namespace VAMR diff --git a/intern/vamr/intern/Session.h b/intern/vamr/intern/Session.h new file mode 100644 index 00000000000..1ba1a1d658a --- /dev/null +++ b/intern/vamr/intern/Session.h @@ -0,0 +1,82 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup VAMR + */ + +#ifndef __SESSION_H__ +#define __SESSION_H__ + +#include +#include + +namespace VAMR { + +class Session { + public: + enum eLifeExpectancy { + SESSION_KEEP_ALIVE, + SESSION_DESTROY, + }; + + Session(class Context *xr_context); + ~Session(); + + void start(const VAMR_SessionBeginInfo *begin_info); + void requestEnd(); + + eLifeExpectancy handleStateChangeEvent(const struct XrEventDataSessionStateChanged *lifecycle); + + bool isRunning() const; + + void unbindGraphicsContext(); /* public so context can ensure it's unbound as needed. */ + + void draw(void *draw_customdata); + + private: + /** Pointer back to context managing this session. Would be nice to avoid, but needed to access + * custom callbacks set before session start. */ + class Context *m_context; + + std::unique_ptr m_oxr; /* Could use stack, but PImpl is preferable */ + + /** Active Ghost graphic context. Owned by Blender, not VAMR. */ + class GHOST_Context *m_gpu_ctx{nullptr}; + std::unique_ptr m_gpu_binding; + + /** Rendering information. Set when drawing starts. */ + std::unique_ptr m_draw_info; + + void initSystem(); + void end(); + + void bindGraphicsContext(); + + void prepareDrawing(); + XrCompositionLayerProjection drawLayer( + std::vector &proj_layer_views, void *draw_customdata); + void drawView(XrSwapchain swapchain, + XrCompositionLayerProjectionView &proj_layer_view, + XrView &view, + void *draw_customdata); + void beginFrameDrawing(); + void endFrameDrawing(std::vector *layers); +}; + +} // namespace VAMR + +#endif /* __SESSION_H__ */ diff --git a/intern/vamr/intern/VAMR.cc b/intern/vamr/intern/VAMR.cc index f65055df958..6e3268ddb4f 100644 --- a/intern/vamr/intern/VAMR.cc +++ b/intern/vamr/intern/VAMR.cc @@ -25,9 +25,11 @@ #include "VAMR_capi.h" -#include "VAMR_intern.h" -#include "VAMR_Context.h" -#include "VAMR_Exception.h" +#include "openxr_includes.h" + +#include "Context.h" +#include "Exception.h" +#include "utils.h" using namespace VAMR; diff --git a/intern/vamr/intern/VAMR_Context.cc b/intern/vamr/intern/VAMR_Context.cc deleted file mode 100644 index 87b90bb5635..00000000000 --- a/intern/vamr/intern/VAMR_Context.cc +++ /dev/null @@ -1,553 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup VAMR - * - * Abstraction for XR (VR, AR, MR, ..) access via OpenXR. - */ - -#include -#include -#include - -#include "VAMR_Types.h" -#include "VAMR_intern.h" -#include "VAMR_Exception.h" -#include "VAMR_Session.h" - -#include "VAMR_Context.h" - -namespace VAMR { - -struct OpenXRInstanceData { - XrInstance instance{XR_NULL_HANDLE}; - XrInstanceProperties instance_properties; - - std::vector extensions; - std::vector layers; - - static PFN_xrCreateDebugUtilsMessengerEXT s_xrCreateDebugUtilsMessengerEXT_fn; - static PFN_xrDestroyDebugUtilsMessengerEXT s_xrDestroyDebugUtilsMessengerEXT_fn; - - XrDebugUtilsMessengerEXT debug_messenger{XR_NULL_HANDLE}; -}; - -PFN_xrCreateDebugUtilsMessengerEXT OpenXRInstanceData::s_xrCreateDebugUtilsMessengerEXT_fn = - nullptr; -PFN_xrDestroyDebugUtilsMessengerEXT OpenXRInstanceData::s_xrDestroyDebugUtilsMessengerEXT_fn = - nullptr; - -VAMR_ErrorHandlerFn Context::s_error_handler = nullptr; -void *Context::s_error_handler_customdata = nullptr; - -/* -------------------------------------------------------------------- */ -/** \name Create, Initialize and Destruct - * - * \{ */ - -Context::Context(const VAMR_ContextCreateInfo *create_info) - : m_oxr(new OpenXRInstanceData()), - m_debug(create_info->context_flag & VAMR_ContextDebug), - m_debug_time(create_info->context_flag & VAMR_ContextDebugTime) -{ -} -Context::~Context() -{ - /* Destroy session data first. Otherwise xrDestroyInstance will implicitly do it, before the - * session had a chance to do so explicitly. */ - m_session = nullptr; - - if (m_oxr->debug_messenger != XR_NULL_HANDLE) { - assert(m_oxr->s_xrDestroyDebugUtilsMessengerEXT_fn != nullptr); - m_oxr->s_xrDestroyDebugUtilsMessengerEXT_fn(m_oxr->debug_messenger); - } - if (m_oxr->instance != XR_NULL_HANDLE) { - CHECK_XR_ASSERT(xrDestroyInstance(m_oxr->instance)); - m_oxr->instance = XR_NULL_HANDLE; - } -} - -void Context::initialize(const VAMR_ContextCreateInfo *create_info) -{ - enumerateApiLayers(); - enumerateExtensions(); - XR_DEBUG_ONLY_CALL(this, printAvailableAPILayersAndExtensionsInfo()); - - m_gpu_binding_type = determineGraphicsBindingTypeToEnable(create_info); - - assert(m_oxr->instance == XR_NULL_HANDLE); - createOpenXRInstance(); - storeInstanceProperties(); - printInstanceInfo(); - XR_DEBUG_ONLY_CALL(this, initDebugMessenger()); -} - -void Context::createOpenXRInstance() -{ - XrInstanceCreateInfo create_info{XR_TYPE_INSTANCE_CREATE_INFO}; - - std::string("Blender").copy(create_info.applicationInfo.applicationName, - XR_MAX_APPLICATION_NAME_SIZE); - create_info.applicationInfo.apiVersion = XR_CURRENT_API_VERSION; - - getAPILayersToEnable(m_enabled_layers); - getExtensionsToEnable(m_enabled_extensions); - create_info.enabledApiLayerCount = m_enabled_layers.size(); - create_info.enabledApiLayerNames = m_enabled_layers.data(); - create_info.enabledExtensionCount = m_enabled_extensions.size(); - create_info.enabledExtensionNames = m_enabled_extensions.data(); - XR_DEBUG_ONLY_CALL(this, printExtensionsAndAPILayersToEnable()); - - CHECK_XR(xrCreateInstance(&create_info, &m_oxr->instance), - "Failed to connect to an OpenXR runtime."); -} - -void Context::storeInstanceProperties() -{ - const std::map runtime_map{ - {"Monado(XRT) by Collabora et al", OPENXR_RUNTIME_MONADO}, - {"Oculus", OPENXR_RUNTIME_OCULUS}, - {"Windows Mixed Reality Runtime", OPENXR_RUNTIME_WMR}}; - decltype(runtime_map)::const_iterator runtime_map_iter; - - m_oxr->instance_properties.type = XR_TYPE_INSTANCE_PROPERTIES; - CHECK_XR(xrGetInstanceProperties(m_oxr->instance, &m_oxr->instance_properties), - "Failed to get OpenXR runtime information. Do you have an active runtime set up?"); - - runtime_map_iter = runtime_map.find(m_oxr->instance_properties.runtimeName); - if (runtime_map_iter != runtime_map.end()) { - m_runtime_id = runtime_map_iter->second; - } -} - -/** \} */ /* Create, Initialize and Destruct */ - -/* -------------------------------------------------------------------- */ -/** \name Debug Printing - * - * \{ */ - -void Context::printInstanceInfo() -{ - assert(m_oxr->instance != XR_NULL_HANDLE); - - printf("Connected to OpenXR runtime: %s (Version %u.%u.%u)\n", - m_oxr->instance_properties.runtimeName, - XR_VERSION_MAJOR(m_oxr->instance_properties.runtimeVersion), - XR_VERSION_MINOR(m_oxr->instance_properties.runtimeVersion), - XR_VERSION_PATCH(m_oxr->instance_properties.runtimeVersion)); -} - -void Context::printAvailableAPILayersAndExtensionsInfo() -{ - puts("Available OpenXR API-layers/extensions:"); - for (XrApiLayerProperties &layer_info : m_oxr->layers) { - printf("Layer: %s\n", layer_info.layerName); - } - for (XrExtensionProperties &ext_info : m_oxr->extensions) { - printf("Extension: %s\n", ext_info.extensionName); - } -} - -void Context::printExtensionsAndAPILayersToEnable() -{ - for (const char *layer_name : m_enabled_layers) { - printf("Enabling OpenXR API-Layer: %s\n", layer_name); - } - for (const char *ext_name : m_enabled_extensions) { - printf("Enabling OpenXR Extension: %s\n", ext_name); - } -} - -static XrBool32 debug_messenger_func(XrDebugUtilsMessageSeverityFlagsEXT /*messageSeverity*/, - XrDebugUtilsMessageTypeFlagsEXT /*messageTypes*/, - const XrDebugUtilsMessengerCallbackDataEXT *callbackData, - void * /*userData*/) -{ - puts("OpenXR Debug Message:"); - puts(callbackData->message); - return XR_FALSE; // OpenXR spec suggests always returning false. -} - -void Context::initDebugMessenger() -{ - XrDebugUtilsMessengerCreateInfoEXT create_info{XR_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT}; - - /* Extension functions need to be obtained through xrGetInstanceProcAddr */ - if (XR_FAILED(xrGetInstanceProcAddr( - m_oxr->instance, - "xrCreateDebugUtilsMessengerEXT", - (PFN_xrVoidFunction *)&m_oxr->s_xrCreateDebugUtilsMessengerEXT_fn)) || - XR_FAILED(xrGetInstanceProcAddr( - m_oxr->instance, - "xrDestroyDebugUtilsMessengerEXT", - (PFN_xrVoidFunction *)&m_oxr->s_xrDestroyDebugUtilsMessengerEXT_fn))) { - m_oxr->s_xrCreateDebugUtilsMessengerEXT_fn = nullptr; - m_oxr->s_xrDestroyDebugUtilsMessengerEXT_fn = nullptr; - - fprintf(stderr, - "Could not use XR_EXT_debug_utils to enable debug prints. Not a fatal error, " - "continuing without the messenger.\n"); - return; - } - - create_info.messageSeverities = XR_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | - XR_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT | - XR_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | - XR_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; - create_info.messageTypes = XR_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | - XR_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | - XR_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; - create_info.userCallback = debug_messenger_func; - - if (XR_FAILED(m_oxr->s_xrCreateDebugUtilsMessengerEXT_fn( - m_oxr->instance, &create_info, &m_oxr->debug_messenger))) { - fprintf(stderr, - "Failed to create OpenXR debug messenger. Not a fatal error, continuing without the " - "messenger.\n"); - return; - } -} - -/** \} */ /* Debug Printing */ - -/* -------------------------------------------------------------------- */ -/** \name Error handling - * - * \{ */ - -void Context::dispatchErrorMessage(const Exception *exception) const -{ - std::ostringstream stream_err_location; - std::string str_err_location; - VAMR_Error error; - - stream_err_location << exception->m_file << ":" << exception->m_line; - str_err_location = stream_err_location.str(); - - error.user_message = exception->m_msg; - error.source_location = str_err_location.c_str(); - error.customdata = s_error_handler_customdata; - - XR_DEBUG_ONLY_CALL(this, - fprintf(stderr, - "Error: \t%s\n\tOpenXR error value: %i\n\tSource: (%s)\n", - error.user_message, - exception->m_res, - error.source_location)); - - /* Potentially destroys VAMR-context */ - s_error_handler(&error); -} - -void Context::setErrorHandler(VAMR_ErrorHandlerFn handler_fn, void *customdata) -{ - s_error_handler = handler_fn; - s_error_handler_customdata = customdata; -} - -/** \} */ /* Error handling */ - -/* -------------------------------------------------------------------- */ -/** \name OpenXR API-Layers and Extensions - * - * \{ */ - -/** - * \param layer_name May be NULL for extensions not belonging to a specific layer. - */ -void Context::enumerateExtensionsEx(std::vector &extensions, - const char *layer_name) -{ - uint32_t extension_count = 0; - - /* Get count for array creation/init first. */ - CHECK_XR(xrEnumerateInstanceExtensionProperties(layer_name, 0, &extension_count, nullptr), - "Failed to query OpenXR runtime information. Do you have an active runtime set up?"); - - if (extension_count == 0) { - /* Extensions are optional, can successfully exit. */ - return; - } - - for (uint32_t i = 0; i < extension_count; i++) { - XrExtensionProperties ext{XR_TYPE_EXTENSION_PROPERTIES}; - extensions.push_back(ext); - } - - /* Actually get the extensions. */ - CHECK_XR(xrEnumerateInstanceExtensionProperties( - layer_name, extension_count, &extension_count, extensions.data()), - "Failed to query OpenXR runtime information. Do you have an active runtime set up?"); -} -void Context::enumerateExtensions() -{ - enumerateExtensionsEx(m_oxr->extensions, nullptr); -} - -void Context::enumerateApiLayers() -{ - uint32_t layer_count = 0; - - /* Get count for array creation/init first. */ - CHECK_XR(xrEnumerateApiLayerProperties(0, &layer_count, nullptr), - "Failed to query OpenXR runtime information. Do you have an active runtime set up?"); - - if (layer_count == 0) { - /* Layers are optional, can safely exit. */ - return; - } - - m_oxr->layers = std::vector(layer_count); - for (XrApiLayerProperties &layer : m_oxr->layers) { - layer.type = XR_TYPE_API_LAYER_PROPERTIES; - } - - /* Actually get the layers. */ - CHECK_XR(xrEnumerateApiLayerProperties(layer_count, &layer_count, m_oxr->layers.data()), - "Failed to query OpenXR runtime information. Do you have an active runtime set up?"); - for (XrApiLayerProperties &layer : m_oxr->layers) { - /* Each layer may have own extensions */ - enumerateExtensionsEx(m_oxr->extensions, layer.layerName); - } -} - -static bool openxr_layer_is_available(const std::vector layers_info, - const std::string &layer_name) -{ - for (const XrApiLayerProperties &layer_info : layers_info) { - if (layer_info.layerName == layer_name) { - return true; - } - } - - return false; -} -static bool openxr_extension_is_available(const std::vector extensions_info, - const std::string &extension_name) -{ - for (const XrExtensionProperties &ext_info : extensions_info) { - if (ext_info.extensionName == extension_name) { - return true; - } - } - - return false; -} - -/** - * Gather an array of names for the API-layers to enable. - */ -void Context::getAPILayersToEnable(std::vector &r_ext_names) -{ - static std::vector try_layers; - - try_layers.clear(); - - XR_DEBUG_ONLY_CALL(this, try_layers.push_back("XR_APILAYER_LUNARG_core_validation")); - - r_ext_names.reserve(try_layers.size()); - - for (const std::string &layer : try_layers) { - if (openxr_layer_is_available(m_oxr->layers, layer)) { - r_ext_names.push_back(layer.c_str()); - } - } -} - -static const char *openxr_ext_name_from_wm_gpu_binding(VAMR_GraphicsBindingType binding) -{ - switch (binding) { - case VAMR_GraphicsBindingTypeOpenGL: - return XR_KHR_OPENGL_ENABLE_EXTENSION_NAME; -#ifdef WIN32 - case VAMR_GraphicsBindingTypeD3D11: - return XR_KHR_D3D11_ENABLE_EXTENSION_NAME; -#endif - case VAMR_GraphicsBindingTypeUnknown: - assert(false); - return nullptr; - } - - return nullptr; -} - -/** - * Gather an array of names for the extensions to enable. - */ -void Context::getExtensionsToEnable(std::vector &r_ext_names) -{ - assert(m_gpu_binding_type != VAMR_GraphicsBindingTypeUnknown); - - const char *gpu_binding = openxr_ext_name_from_wm_gpu_binding(m_gpu_binding_type); - static std::vector try_ext; - - try_ext.clear(); - - /* Try enabling debug extension */ -#ifndef WIN32 - XR_DEBUG_ONLY_CALL(this, try_ext.push_back(XR_EXT_DEBUG_UTILS_EXTENSION_NAME)); -#endif - - r_ext_names.reserve(try_ext.size() + 1); /* + 1 for graphics binding extension. */ - - /* Add graphics binding extension. */ - assert(gpu_binding); - assert(openxr_extension_is_available(m_oxr->extensions, gpu_binding)); - r_ext_names.push_back(gpu_binding); - - for (const std::string &ext : try_ext) { - if (openxr_extension_is_available(m_oxr->extensions, ext)) { - r_ext_names.push_back(ext.c_str()); - } - } -} - -/** - * Decide which graphics binding extension to use based on - * #VAMR_ContextCreateInfo.gpu_binding_candidates and available extensions. - */ -VAMR_GraphicsBindingType Context::determineGraphicsBindingTypeToEnable( - const VAMR_ContextCreateInfo *create_info) -{ - assert(create_info->gpu_binding_candidates != NULL); - assert(create_info->gpu_binding_candidates_count > 0); - - for (uint32_t i = 0; i < create_info->gpu_binding_candidates_count; i++) { - assert(create_info->gpu_binding_candidates[i] != VAMR_GraphicsBindingTypeUnknown); - const char *ext_name = openxr_ext_name_from_wm_gpu_binding( - create_info->gpu_binding_candidates[i]); - if (openxr_extension_is_available(m_oxr->extensions, ext_name)) { - return create_info->gpu_binding_candidates[i]; - } - } - - return VAMR_GraphicsBindingTypeUnknown; -} - -/** \} */ /* OpenXR API-Layers and Extensions */ - -/* -------------------------------------------------------------------- */ -/** \name Session management - * - * Manage session lifetime and delegate public calls to #VAMR::Session. - * \{ */ - -void Context::startSession(const VAMR_SessionBeginInfo *begin_info) -{ - if (m_session == nullptr) { - m_session = std::unique_ptr(new Session(this)); - } - - m_session->start(begin_info); -} -void Context::endSession() -{ - m_session->requestEnd(); -} - -bool Context::isSessionRunning() const -{ - return m_session && m_session->isRunning(); -} - -void Context::drawSessionViews(void *draw_customdata) -{ - m_session->draw(draw_customdata); -} - -/** - * Delegates event to session, allowing context to destruct the session if needed. - */ -void Context::handleSessionStateChange(const XrEventDataSessionStateChanged *lifecycle) -{ - if (m_session && m_session->handleStateChangeEvent(lifecycle) == Session::SESSION_DESTROY) { - m_session = nullptr; - } -} - -/** \} */ /* Session Management */ - -/* -------------------------------------------------------------------- */ -/** \name Public Accessors and Mutators - * - * Public as in, exposed in the VAMR API. - * \{ */ - -/** - * Set context for binding and unbinding a graphics context for a session. The binding callback - * may create a new context thereby. In fact that's the sole reason for this callback approach to - * binding. Just make sure to have an unbind function set that properly destructs. - * - * \param bind_fn Function to retrieve (possibly create) a graphics context. - * \param unbind_fn Function to release (possibly free) a graphics context. - */ -void Context::setGraphicsContextBindFuncs(VAMR_GraphicsContextBindFn bind_fn, - VAMR_GraphicsContextUnbindFn unbind_fn) -{ - if (m_session) { - m_session->unbindGraphicsContext(); - } - m_custom_funcs.gpu_ctx_bind_fn = bind_fn; - m_custom_funcs.gpu_ctx_unbind_fn = unbind_fn; -} - -void Context::setDrawViewFunc(VAMR_DrawViewFn draw_view_fn) -{ - m_custom_funcs.draw_view_fn = draw_view_fn; -} - -/** \} */ /* Public Accessors and Mutators */ - -/* -------------------------------------------------------------------- */ -/** \name VAMR Internal Accessors and Mutators - * - * \{ */ - -OpenXRRuntimeID Context::getOpenXRRuntimeID() const -{ - return m_runtime_id; -} - -const CustomFuncs *Context::getCustomFuncs() const -{ - return &m_custom_funcs; -} - -VAMR_GraphicsBindingType Context::getGraphicsBindingType() const -{ - return m_gpu_binding_type; -} - -XrInstance Context::getInstance() const -{ - return m_oxr->instance; -} - -bool Context::isDebugMode() const -{ - return m_debug; -} - -bool Context::isDebugTimeMode() const -{ - return m_debug_time; -} - -/** \} */ /* VAMR Internal Accessors and Mutators */ - -} // namespace VAMR diff --git a/intern/vamr/intern/VAMR_Context.h b/intern/vamr/intern/VAMR_Context.h deleted file mode 100644 index f0b6d7ddcb8..00000000000 --- a/intern/vamr/intern/VAMR_Context.h +++ /dev/null @@ -1,130 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup VAMR - */ - -#ifndef __VAMR_CONTEXT_H__ -#define __VAMR_CONTEXT_H__ - -#include -#include - -#include "VAMR_IContext.h" - -namespace VAMR { - -struct CustomFuncs { - /** Function to retrieve (possibly create) a graphics context */ - VAMR_GraphicsContextBindFn gpu_ctx_bind_fn{nullptr}; - /** Function to release (possibly free) a graphics context */ - VAMR_GraphicsContextUnbindFn gpu_ctx_unbind_fn{nullptr}; - - /** Custom per-view draw function for Blender side drawing. */ - VAMR_DrawViewFn draw_view_fn{nullptr}; -}; - -/** - * In some occasions, runtime specific handling is needed, e.g. to work around runtime bugs. - */ -enum OpenXRRuntimeID { - OPENXR_RUNTIME_MONADO, - OPENXR_RUNTIME_OCULUS, - OPENXR_RUNTIME_WMR, /* Windows Mixed Reality */ - - OPENXR_RUNTIME_UNKNOWN -}; - -/** - * \brief Main VAMR container to manage OpenXR through. - * - * Creating a context using #VAMR_ContextCreate involves dynamically connecting to the OpenXR - * runtime, likely reading the OS OpenXR configuration (i.e. active_runtime.json). So this is - * something that should better be done using lazy-initialization. - */ -class Context : public VAMR::IContext { - public: - Context(const VAMR_ContextCreateInfo *create_info); - ~Context(); - void initialize(const VAMR_ContextCreateInfo *create_info); - - void startSession(const VAMR_SessionBeginInfo *begin_info) override; - void endSession() override; - bool isSessionRunning() const override; - void drawSessionViews(void *draw_customdata) override; - - static void setErrorHandler(VAMR_ErrorHandlerFn handler_fn, void *customdata); - void dispatchErrorMessage(const class Exception *exception) const override; - - void setGraphicsContextBindFuncs(VAMR_GraphicsContextBindFn bind_fn, - VAMR_GraphicsContextUnbindFn unbind_fn) override; - void setDrawViewFunc(VAMR_DrawViewFn draw_view_fn) override; - - void handleSessionStateChange(const XrEventDataSessionStateChanged *lifecycle); - - OpenXRRuntimeID getOpenXRRuntimeID() const; - const CustomFuncs *getCustomFuncs() const; - VAMR_GraphicsBindingType getGraphicsBindingType() const; - XrInstance getInstance() const; - bool isDebugMode() const; - bool isDebugTimeMode() const; - - private: - std::unique_ptr m_oxr; - - OpenXRRuntimeID m_runtime_id{OPENXR_RUNTIME_UNKNOWN}; - - /* The active VAMR XR Session. Null while no session runs. */ - std::unique_ptr m_session; - - /** Active graphics binding type. */ - VAMR_GraphicsBindingType m_gpu_binding_type{VAMR_GraphicsBindingTypeUnknown}; - - /** Names of enabled extensions */ - std::vector m_enabled_extensions; - /** Names of enabled API-layers */ - std::vector m_enabled_layers; - - static VAMR_ErrorHandlerFn s_error_handler; - static void *s_error_handler_customdata; - CustomFuncs m_custom_funcs; - - /** Enable debug message prints and OpenXR API validation layers */ - bool m_debug{false}; - bool m_debug_time{false}; - - void createOpenXRInstance(); - void storeInstanceProperties(); - void initDebugMessenger(); - - void printInstanceInfo(); - void printAvailableAPILayersAndExtensionsInfo(); - void printExtensionsAndAPILayersToEnable(); - - void enumerateApiLayers(); - void enumerateExtensions(); - void enumerateExtensionsEx(std::vector &extensions, - const char *layer_name); - void getAPILayersToEnable(std::vector &r_ext_names); - void getExtensionsToEnable(std::vector &r_ext_names); - VAMR_GraphicsBindingType determineGraphicsBindingTypeToEnable( - const VAMR_ContextCreateInfo *create_info); -}; - -} // namespace VAMR - -#endif // __VAMR_CONTEXT_H__ diff --git a/intern/vamr/intern/VAMR_Event.cc b/intern/vamr/intern/VAMR_Event.cc deleted file mode 100644 index b2656c29de2..00000000000 --- a/intern/vamr/intern/VAMR_Event.cc +++ /dev/null @@ -1,67 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup VAMR - */ - -#include - -#include "VAMR_Types.h" -#include "VAMR_intern.h" -#include "VAMR_capi.h" -#include "VAMR_Context.h" - -namespace VAMR { - -static bool VAMR_EventPollNext(XrInstance instance, XrEventDataBuffer &r_event_data) -{ - /* (Re-)initialize as required by specification */ - r_event_data.type = XR_TYPE_EVENT_DATA_BUFFER; - r_event_data.next = nullptr; - - return (xrPollEvent(instance, &r_event_data) == XR_SUCCESS); -} - -::VAMR_TSuccess VAMR_EventsHandle(Context *xr_context) -{ - XrEventDataBuffer event_buffer; /* structure big enought to hold all possible events */ - - if (xr_context == NULL) { - return VAMR_Failure; - } - - while (VAMR_EventPollNext(xr_context->getInstance(), event_buffer)) { - XrEventDataBaseHeader *event = (XrEventDataBaseHeader *)&event_buffer; /* base event struct */ - - switch (event->type) { - case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: - xr_context->handleSessionStateChange((XrEventDataSessionStateChanged *)event); - return VAMR_Success; - - case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING: - VAMR_ContextDestroy((VAMR_ContextHandle)xr_context); - return VAMR_Success; - default: - XR_DEBUG_PRINTF(xr_context, "Unhandled event: %i\n", event->type); - return VAMR_Failure; - } - } - - return VAMR_Failure; -} - -} // namespace VAMR diff --git a/intern/vamr/intern/VAMR_Exception.h b/intern/vamr/intern/VAMR_Exception.h deleted file mode 100644 index f4f831f4c08..00000000000 --- a/intern/vamr/intern/VAMR_Exception.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup VAMR - */ - -#ifndef __VAMR_EXCEPTION_H__ -#define __VAMR_EXCEPTION_H__ - -#include - -namespace VAMR { - -/** - * For now just a general Exception type (note that it's in namespace VAMR, so name shouldn't cause - * conflicts). - */ -class Exception : public std::exception { - friend class Context; - - public: - Exception(const char *msg, const char *file, int line, int res = 0) - : std::exception(), m_msg(msg), m_file(file), m_line(line), m_res(res) - { - } - - const char *what() const noexcept override - { - return m_msg; - } - - private: - const char *m_msg; - const char *m_file; - const int m_line; - int m_res; -}; - -} // namespace VAMR - -#endif // __VAMR_EXCEPTION_H__ diff --git a/intern/vamr/intern/VAMR_GraphicsBinding.cc b/intern/vamr/intern/VAMR_GraphicsBinding.cc deleted file mode 100644 index c036421d517..00000000000 --- a/intern/vamr/intern/VAMR_GraphicsBinding.cc +++ /dev/null @@ -1,308 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup VAMR - */ - -#include -#include -#include - -#if 0 -# if defined(WITH_X11) -# include "GHOST_ContextGLX.h" -# elif defined(WIN32) -# include "GHOST_ContextWGL.h" -# include "GHOST_ContextD3D.h" -# endif -#endif -#include "VAMR_capi.h" -#include "VAMR_intern.h" - -#include "VAMR_IGraphicsBinding.h" - -namespace VAMR { - -static bool choose_swapchain_format_from_candidates(std::vector gpu_binding_formats, - std::vector runtime_formats, - int64_t *r_result) -{ - if (gpu_binding_formats.empty()) { - return false; - } - - auto res = std::find_first_of(gpu_binding_formats.begin(), - gpu_binding_formats.end(), - runtime_formats.begin(), - runtime_formats.end()); - if (res == gpu_binding_formats.end()) { - return false; - } - - *r_result = *res; - return true; -} - -class GraphicsBindingOpenGL : public IGraphicsBinding { - public: - ~GraphicsBindingOpenGL() - { - if (m_fbo != 0) { - glDeleteFramebuffers(1, &m_fbo); - } - } - - bool checkVersionRequirements(GHOST_Context *ghost_ctx, - XrInstance instance, - XrSystemId system_id, - std::string *r_requirement_info) const override - { -#if 0 -# if defined(WITH_X11) - GHOST_ContextGLX *ctx_gl = static_cast(ghost_ctx); -# else - GHOST_ContextWGL *ctx_gl = static_cast(ghost_ctx); -# endif - XrGraphicsRequirementsOpenGLKHR gpu_requirements{XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_KHR}; - const XrVersion gl_version = XR_MAKE_VERSION( - ctx_gl->m_contextMajorVersion, ctx_gl->m_contextMinorVersion, 0); - - xrGetOpenGLGraphicsRequirementsKHR(instance, system_id, &gpu_requirements); - - if (r_requirement_info) { - std::ostringstream strstream; - strstream << "Min OpenGL version " - << XR_VERSION_MAJOR(gpu_requirements.minApiVersionSupported) << "." - << XR_VERSION_MINOR(gpu_requirements.minApiVersionSupported) << std::endl; - strstream << "Max OpenGL version " - << XR_VERSION_MAJOR(gpu_requirements.maxApiVersionSupported) << "." - << XR_VERSION_MINOR(gpu_requirements.maxApiVersionSupported) << std::endl; - - *r_requirement_info = strstream.str(); - } - - return (gl_version >= gpu_requirements.minApiVersionSupported) && - (gl_version <= gpu_requirements.maxApiVersionSupported); -#endif - return true; - } - - void initFromGhostContext(GHOST_Context *ghost_ctx) override - { -#if 0 -# if defined(WITH_X11) - GHOST_ContextGLX *ctx_glx = static_cast(ghost_ctx); - XVisualInfo *visual_info = glXGetVisualFromFBConfig(ctx_glx->m_display, ctx_glx->m_fbconfig); - - oxr_binding.glx.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_XLIB_KHR; - oxr_binding.glx.xDisplay = ctx_glx->m_display; - oxr_binding.glx.glxFBConfig = ctx_glx->m_fbconfig; - oxr_binding.glx.glxDrawable = ctx_glx->m_window; - oxr_binding.glx.glxContext = ctx_glx->m_context; - oxr_binding.glx.visualid = visual_info->visualid; -# elif defined(WIN32) - GHOST_ContextWGL *ctx_wgl = static_cast(ghost_ctx); - - oxr_binding.wgl.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR; - oxr_binding.wgl.hDC = ctx_wgl->m_hDC; - oxr_binding.wgl.hGLRC = ctx_wgl->m_hGLRC; -# endif -#endif - - /* Generate a framebuffer to use for blitting into the texture */ - glGenFramebuffers(1, &m_fbo); - } - - bool chooseSwapchainFormat(const std::vector &runtime_formats, - int64_t *r_result) const override - { - std::vector gpu_binding_formats = {GL_RGBA8}; - return choose_swapchain_format_from_candidates(gpu_binding_formats, runtime_formats, r_result); - } - - std::vector createSwapchainImages(uint32_t image_count) override - { - std::vector ogl_images(image_count); - std::vector base_images; - - // Need to return vector of base header pointers, so of a different type. Need to build a new - // list with this type, and keep the initial one alive. - for (XrSwapchainImageOpenGLKHR &image : ogl_images) { - image.type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR; - base_images.push_back(reinterpret_cast(&image)); - } - - // Keep alive. - m_image_cache.push_back(std::move(ogl_images)); - - return base_images; - } - - void submitToSwapchain(XrSwapchainImageBaseHeader *swapchain_image, - const VAMR_DrawViewInfo *draw_info) override - { - XrSwapchainImageOpenGLKHR *ogl_swapchain_image = reinterpret_cast( - swapchain_image); - - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbo); - - glFramebufferTexture2D( - GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ogl_swapchain_image->image, 0); - - glBlitFramebuffer(draw_info->ofsx, - draw_info->ofsy, - draw_info->ofsx + draw_info->width, - draw_info->ofsy + draw_info->height, - draw_info->ofsx, - draw_info->ofsy, - draw_info->ofsx + draw_info->width, - draw_info->ofsy + draw_info->height, - GL_COLOR_BUFFER_BIT, - GL_LINEAR); - - glBindFramebuffer(GL_FRAMEBUFFER, 0); - } - - private: - std::list> m_image_cache; - GLuint m_fbo{0}; -}; - -#ifdef WIN32 -class GraphicsBindingD3D : public IGraphicsBinding { - public: - ~GraphicsBindingD3D() - { - if (m_shared_resource) { - m_ghost_ctx->disposeSharedOpenGLResource(m_shared_resource); - } - } - - bool checkVersionRequirements(GHOST_Context *ghost_ctx, - XrInstance instance, - XrSystemId system_id, - std::string *r_requirement_info) const override - { - - GHOST_ContextD3D *ctx_dx = static_cast(ghost_ctx); - XrGraphicsRequirementsD3D11KHR gpu_requirements{XR_TYPE_GRAPHICS_REQUIREMENTS_D3D11_KHR}; - - xrGetD3D11GraphicsRequirementsKHR(instance, system_id, &gpu_requirements); - - if (r_requirement_info) { - std::ostringstream strstream; - strstream << "Min DirectX 11 Feature Level " << gpu_requirements.minFeatureLevel - << std::endl; - - *r_requirement_info = std::move(strstream.str()); - } - - return ctx_dx->m_device->GetFeatureLevel() >= gpu_requirements.minFeatureLevel; - } - - void initFromGhostContext(GHOST_Context *ghost_ctx) override - { - GHOST_ContextD3D *ctx_d3d = static_cast(ghost_ctx); - - oxr_binding.d3d11.type = XR_TYPE_GRAPHICS_BINDING_D3D11_KHR; - oxr_binding.d3d11.device = ctx_d3d->m_device; - m_ghost_ctx = ctx_d3d; - } - - bool chooseSwapchainFormat(const std::vector &runtime_formats, - int64_t *r_result) const override - { - std::vector gpu_binding_formats = {DXGI_FORMAT_R8G8B8A8_UNORM}; - return choose_swapchain_format_from_candidates(gpu_binding_formats, runtime_formats, r_result); - } - - std::vector createSwapchainImages(uint32_t image_count) override - { - std::vector d3d_images(image_count); - std::vector base_images; - - // Need to return vector of base header pointers, so of a different type. Need to build a new - // list with this type, and keep the initial one alive. - for (XrSwapchainImageD3D11KHR &image : d3d_images) { - image.type = XR_TYPE_SWAPCHAIN_IMAGE_D3D11_KHR; - base_images.push_back(reinterpret_cast(&image)); - } - - // Keep alive. - m_image_cache.push_back(std::move(d3d_images)); - - return base_images; - } - - void submitToSwapchain(XrSwapchainImageBaseHeader *swapchain_image, - const VAMR_DrawViewInfo *draw_info) override - { - XrSwapchainImageD3D11KHR *d3d_swapchain_image = reinterpret_cast( - swapchain_image); - -# if 0 - /* Ideally we'd just create a render target view for the OpenXR swapchain image texture and - * blit from the OpenGL context into it. The NV_DX_interop extension doesn't want to work with - * this though. At least not with Optimus hardware. See: - * https://github.com/mpv-player/mpv/issues/2949#issuecomment-197262807. - */ - - ID3D11RenderTargetView *rtv; - CD3D11_RENDER_TARGET_VIEW_DESC rtv_desc(D3D11_RTV_DIMENSION_TEXTURE2D, - DXGI_FORMAT_R8G8B8A8_UNORM); - - m_ghost_ctx->m_device->CreateRenderTargetView(d3d_swapchain_image->texture, &rtv_desc, &rtv); - if (!m_shared_resource) { - m_shared_resource = m_ghost_ctx->createSharedOpenGLResource( - draw_info->width, draw_info->height, rtv); - } - m_ghost_ctx->blitFromOpenGLContext(m_shared_resource, draw_info->width, draw_info->height); -# else - if (!m_shared_resource) { - m_shared_resource = m_ghost_ctx->createSharedOpenGLResource(draw_info->width, - draw_info->height); - } - m_ghost_ctx->blitFromOpenGLContext(m_shared_resource, draw_info->width, draw_info->height); - - m_ghost_ctx->m_device_ctx->OMSetRenderTargets(0, nullptr, nullptr); - m_ghost_ctx->m_device_ctx->CopyResource(d3d_swapchain_image->texture, - m_ghost_ctx->getSharedTexture2D(m_shared_resource)); -# endif - } - - private: - GHOST_ContextD3D *m_ghost_ctx; - GHOST_SharedOpenGLResource *m_shared_resource; - std::list> m_image_cache; -}; -#endif // WIN32 - -std::unique_ptr GraphicsBindingCreateFromType(VAMR_GraphicsBindingType type) -{ - switch (type) { - case VAMR_GraphicsBindingTypeOpenGL: - return std::unique_ptr(new GraphicsBindingOpenGL()); -#ifdef WIN32 - case VAMR_GraphicsBindingTypeD3D11: - return std::unique_ptr(new GraphicsBindingD3D()); -#endif - default: - return nullptr; - } -} - -} // namespace VAMR diff --git a/intern/vamr/intern/VAMR_IGraphicsBinding.h b/intern/vamr/intern/VAMR_IGraphicsBinding.h deleted file mode 100644 index ae03d3f86d2..00000000000 --- a/intern/vamr/intern/VAMR_IGraphicsBinding.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup VAMR - */ - -#ifndef __VAMR_IGRAPHICSBINDING_H__ -#define __VAMR_IGRAPHICSBINDING_H__ - -#include -#include -#include - -#include "VAMR_openxr_includes.h" - -namespace VAMR { - -class IGraphicsBinding { - friend std::unique_ptr GraphicsBindingCreateFromType( - VAMR_GraphicsBindingType type); - - public: - union { -#if defined(WITH_X11) - XrGraphicsBindingOpenGLXlibKHR glx; -#elif defined(WIN32) - XrGraphicsBindingOpenGLWin32KHR wgl; - XrGraphicsBindingD3D11KHR d3d11; -#endif - } oxr_binding; - - /** - * Does __not__ require this object is initialized (can be called prior to - * #initFromGhostContext). It's actually meant to be called first. - * - * \param r_requirement_info Return argument to retrieve an informal string on the requirements - * to be met. Useful for error/debug messages. - */ - virtual bool checkVersionRequirements(class GHOST_Context *ghost_ctx, - XrInstance instance, - XrSystemId system_id, - std::string *r_requirement_info) const = 0; - virtual void initFromGhostContext(class GHOST_Context *ghost_ctx) = 0; - virtual bool chooseSwapchainFormat(const std::vector &runtime_formats, - int64_t *r_result) const = 0; - virtual std::vector createSwapchainImages( - uint32_t image_count) = 0; - virtual void submitToSwapchain(XrSwapchainImageBaseHeader *swapchain_image, - const VAMR_DrawViewInfo *draw_info) = 0; - - protected: - /* Use GraphicsBindingCreateFromType */ - IGraphicsBinding() = default; -}; - -std::unique_ptr GraphicsBindingCreateFromType(VAMR_GraphicsBindingType type); - -} // namespace VAMR - -#endif /* __VAMR_IGRAPHICSBINDING_H__ */ diff --git a/intern/vamr/intern/VAMR_Session.cc b/intern/vamr/intern/VAMR_Session.cc deleted file mode 100644 index 315bf0f3f7b..00000000000 --- a/intern/vamr/intern/VAMR_Session.cc +++ /dev/null @@ -1,574 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup VAMR - */ - -#include -#include -#include -#include -#include -#include - -#include "VAMR_capi.h" - -#include "VAMR_IGraphicsBinding.h" -#include "VAMR_intern.h" -#include "VAMR_Context.h" -#include "VAMR_Exception.h" - -#include "VAMR_Session.h" - -namespace VAMR { - -struct OpenXRSessionData { - XrSystemId system_id{XR_NULL_SYSTEM_ID}; - XrSession session{XR_NULL_HANDLE}; - XrSessionState session_state{XR_SESSION_STATE_UNKNOWN}; - - // Only stereo rendering supported now. - const XrViewConfigurationType view_type{XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO}; - XrSpace reference_space; - std::vector views; - std::vector swapchains; - std::map> swapchain_images; - int32_t swapchain_image_width, swapchain_image_height; -}; - -struct DrawInfo { - XrFrameState frame_state; - - /** Time at frame start to benchmark frame render durations. */ - std::chrono::high_resolution_clock::time_point frame_begin_time; - /* Time previous frames took for rendering (in ms) */ - std::list last_frame_times; -}; - -/* -------------------------------------------------------------------- */ -/** \name Create, Initialize and Destruct - * - * \{ */ - -Session::Session(Context *xr_context) : m_context(xr_context), m_oxr(new OpenXRSessionData()) -{ -} - -Session::~Session() -{ - unbindGraphicsContext(); - - for (XrSwapchain &swapchain : m_oxr->swapchains) { - CHECK_XR_ASSERT(xrDestroySwapchain(swapchain)); - } - m_oxr->swapchains.clear(); - if (m_oxr->reference_space != XR_NULL_HANDLE) { - CHECK_XR_ASSERT(xrDestroySpace(m_oxr->reference_space)); - } - if (m_oxr->session != XR_NULL_HANDLE) { - CHECK_XR_ASSERT(xrDestroySession(m_oxr->session)); - } - - m_oxr->session = XR_NULL_HANDLE; - m_oxr->session_state = XR_SESSION_STATE_UNKNOWN; -} - -/** - * A system in OpenXR the combination of some sort of HMD plus controllers and whatever other - * devices are managed through OpenXR. So this attempts to init the HMD and the other devices. - */ -void Session::initSystem() -{ - assert(m_context->getInstance() != XR_NULL_HANDLE); - assert(m_oxr->system_id == XR_NULL_SYSTEM_ID); - - XrSystemGetInfo system_info{}; - system_info.type = XR_TYPE_SYSTEM_GET_INFO; - system_info.formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY; - - CHECK_XR(xrGetSystem(m_context->getInstance(), &system_info, &m_oxr->system_id), - "Failed to get device information. Is a device plugged in?"); -} - -/** \} */ /* Create, Initialize and Destruct */ - -/* -------------------------------------------------------------------- */ -/** \name State Management - * - * \{ */ - -static void create_reference_space(OpenXRSessionData *oxr, const VAMR_Pose *base_pose) -{ - XrReferenceSpaceCreateInfo create_info{XR_TYPE_REFERENCE_SPACE_CREATE_INFO}; - create_info.poseInReferenceSpace.orientation.w = 1.0f; - - create_info.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL; -#if 0 -/* Proper reference space set up is not supported yet. We simply hand OpenXR - * the global space as reference space and apply its pose onto the active - * camera matrix to get a basic viewing experience going. If there's no active - * camera with stick to the world origin. - * - * Once we have proper reference space set up (i.e. a way to define origin, up- - * direction and an initial view rotation perpendicular to the up-direction), - * we can hand OpenXR a proper reference pose/space. - */ - create_info.poseInReferenceSpace.position.x = base_pose->position[0]; - create_info.poseInReferenceSpace.position.y = base_pose->position[2]; - create_info.poseInReferenceSpace.position.z = -base_pose->position[1]; - create_info.poseInReferenceSpace.orientation.x = base_pose->orientation_quat[1]; - create_info.poseInReferenceSpace.orientation.y = base_pose->orientation_quat[3]; - create_info.poseInReferenceSpace.orientation.z = -base_pose->orientation_quat[2]; - create_info.poseInReferenceSpace.orientation.w = base_pose->orientation_quat[0]; -#else - (void)base_pose; -#endif - - CHECK_XR(xrCreateReferenceSpace(oxr->session, &create_info, &oxr->reference_space), - "Failed to create reference space."); -} - -void Session::start(const VAMR_SessionBeginInfo *begin_info) -{ - assert(m_context->getInstance() != XR_NULL_HANDLE); - assert(m_oxr->session == XR_NULL_HANDLE); - if (m_context->getCustomFuncs()->gpu_ctx_bind_fn == nullptr) { - THROW_XR( - "Invalid API usage: No way to bind graphics context to the XR session. Call " - "VAMR_GraphicsContextBindFuncs() with valid parameters before starting the " - "session (through VAMR_SessionStart())."); - } - - initSystem(); - - bindGraphicsContext(); - if (m_gpu_ctx == nullptr) { - THROW_XR( - "Invalid API usage: No graphics context returned through the callback set with " - "VAMR_GraphicsContextBindFuncs(). This is required for session starting (through " - "VAMR_SessionStart())."); - } - - std::string requirement_str; - m_gpu_binding = GraphicsBindingCreateFromType(m_context->getGraphicsBindingType()); - if (!m_gpu_binding->checkVersionRequirements( - m_gpu_ctx, m_context->getInstance(), m_oxr->system_id, &requirement_str)) { - std::ostringstream strstream; - strstream << "Available graphics context version does not meet the following requirements: " - << requirement_str; - THROW_XR(strstream.str().c_str()); - } - m_gpu_binding->initFromGhostContext(m_gpu_ctx); - - XrSessionCreateInfo create_info{}; - create_info.type = XR_TYPE_SESSION_CREATE_INFO; - create_info.systemId = m_oxr->system_id; - create_info.next = &m_gpu_binding->oxr_binding; - - CHECK_XR(xrCreateSession(m_context->getInstance(), &create_info, &m_oxr->session), - "Failed to create VR session. The OpenXR runtime may have additional requirements for " - "the graphics driver that are not met. Other causes are possible too however.\nTip: " - "The --debug-xr command line option for Blender might allow the runtime to output " - "detailed error information to the command line."); - - prepareDrawing(); - create_reference_space(m_oxr.get(), &begin_info->base_pose); -} - -void Session::requestEnd() -{ - xrRequestExitSession(m_oxr->session); -} - -void Session::end() -{ - assert(m_oxr->session != XR_NULL_HANDLE); - - CHECK_XR(xrEndSession(m_oxr->session), "Failed to cleanly end the VR session."); - unbindGraphicsContext(); - m_draw_info = nullptr; -} - -Session::eLifeExpectancy Session::handleStateChangeEvent( - const XrEventDataSessionStateChanged *lifecycle) -{ - m_oxr->session_state = lifecycle->state; - - /* Runtime may send events for apparently destroyed session. Our handle should be NULL then. */ - assert((m_oxr->session == XR_NULL_HANDLE) || (m_oxr->session == lifecycle->session)); - - switch (lifecycle->state) { - case XR_SESSION_STATE_READY: { - XrSessionBeginInfo begin_info{}; - - begin_info.type = XR_TYPE_SESSION_BEGIN_INFO; - begin_info.primaryViewConfigurationType = m_oxr->view_type; - CHECK_XR(xrBeginSession(m_oxr->session, &begin_info), - "Failed to cleanly begin the VR session."); - break; - } - case XR_SESSION_STATE_STOPPING: - /* Runtime will change state to STATE_EXITING, don't destruct session yet. */ - end(); - break; - case XR_SESSION_STATE_EXITING: - case XR_SESSION_STATE_LOSS_PENDING: - return SESSION_DESTROY; - default: - break; - } - - return SESSION_KEEP_ALIVE; -} -/** \} */ /* State Management */ - -/* -------------------------------------------------------------------- */ -/** \name Drawing - * - * \{ */ - -static std::vector swapchain_images_create( - XrSwapchain swapchain, IGraphicsBinding *gpu_binding) -{ - std::vector images; - uint32_t image_count; - - CHECK_XR(xrEnumerateSwapchainImages(swapchain, 0, &image_count, nullptr), - "Failed to get count of swapchain images to create for the VR session."); - images = gpu_binding->createSwapchainImages(image_count); - CHECK_XR(xrEnumerateSwapchainImages(swapchain, images.size(), &image_count, images[0]), - "Failed to create swapchain images for the VR session."); - - return images; -} - -static unique_oxr_ptr swapchain_create(const XrSession session, - IGraphicsBinding *gpu_binding, - const XrViewConfigurationView *xr_view) -{ - XrSwapchainCreateInfo create_info{XR_TYPE_SWAPCHAIN_CREATE_INFO}; - unique_oxr_ptr swapchain(xrDestroySwapchain); - uint32_t format_count = 0; - int64_t chosen_format; - - CHECK_XR(xrEnumerateSwapchainFormats(session, 0, &format_count, nullptr), - "Failed to get count of swapchain image formats."); - std::vector swapchain_formats(format_count); - CHECK_XR(xrEnumerateSwapchainFormats( - session, swapchain_formats.size(), &format_count, swapchain_formats.data()), - "Failed to get swapchain image formats."); - assert(swapchain_formats.size() == format_count); - - if (!gpu_binding->chooseSwapchainFormat(swapchain_formats, &chosen_format)) { - THROW_XR("Error: No format matching OpenXR runtime supported swapchain formats found."); - } - - create_info.usageFlags = XR_SWAPCHAIN_USAGE_SAMPLED_BIT | - XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT; - create_info.format = chosen_format; - create_info.sampleCount = xr_view->recommendedSwapchainSampleCount; - create_info.width = xr_view->recommendedImageRectWidth; - create_info.height = xr_view->recommendedImageRectHeight; - create_info.faceCount = 1; - create_info.arraySize = 1; - create_info.mipCount = 1; - CHECK_XR(swapchain.construct(xrCreateSwapchain, session, &create_info), - "Failed to create OpenXR swapchain."); - - return swapchain; -} - -void Session::prepareDrawing() -{ - std::vector view_configs; - uint32_t view_count; - - CHECK_XR( - xrEnumerateViewConfigurationViews( - m_context->getInstance(), m_oxr->system_id, m_oxr->view_type, 0, &view_count, nullptr), - "Failed to get count of view configurations."); - view_configs.resize(view_count, {XR_TYPE_VIEW_CONFIGURATION_VIEW}); - CHECK_XR(xrEnumerateViewConfigurationViews(m_context->getInstance(), - m_oxr->system_id, - m_oxr->view_type, - view_configs.size(), - &view_count, - view_configs.data()), - "Failed to get count of view configurations."); - - for (const XrViewConfigurationView &view : view_configs) { - unique_oxr_ptr swapchain = swapchain_create( - m_oxr->session, m_gpu_binding.get(), &view); - auto images = swapchain_images_create(swapchain.get(), m_gpu_binding.get()); - - m_oxr->swapchain_image_width = view.recommendedImageRectWidth; - m_oxr->swapchain_image_height = view.recommendedImageRectHeight; - m_oxr->swapchains.push_back(swapchain.get()); - m_oxr->swapchain_images.insert(std::make_pair(swapchain.get(), std::move(images))); - - swapchain.release(); - } - - m_oxr->views.resize(view_count, {XR_TYPE_VIEW}); - - m_draw_info = std::unique_ptr(new DrawInfo()); -} - -void Session::beginFrameDrawing() -{ - XrFrameWaitInfo wait_info{XR_TYPE_FRAME_WAIT_INFO}; - XrFrameBeginInfo begin_info{XR_TYPE_FRAME_BEGIN_INFO}; - XrFrameState frame_state{XR_TYPE_FRAME_STATE}; - - // TODO Blocking call. Does this intefer with other drawing? - CHECK_XR(xrWaitFrame(m_oxr->session, &wait_info, &frame_state), - "Failed to synchronize frame rates between Blender and the device."); - - CHECK_XR(xrBeginFrame(m_oxr->session, &begin_info), - "Failed to submit frame rendering start state."); - - m_draw_info->frame_state = frame_state; - - if (m_context->isDebugTimeMode()) { - m_draw_info->frame_begin_time = std::chrono::high_resolution_clock::now(); - } -} - -static void print_debug_timings(DrawInfo *draw_info) -{ - /** Render time of last 8 frames (in ms) to calculate an average. */ - std::chrono::duration duration = std::chrono::high_resolution_clock::now() - - draw_info->frame_begin_time; - const double duration_ms = duration.count(); - const int avg_frame_count = 8; - double avg_ms_tot = 0.0; - - if (draw_info->last_frame_times.size() >= avg_frame_count) { - draw_info->last_frame_times.pop_front(); - assert(draw_info->last_frame_times.size() == avg_frame_count - 1); - } - draw_info->last_frame_times.push_back(duration_ms); - for (double ms_iter : draw_info->last_frame_times) { - avg_ms_tot += ms_iter; - } - - printf("VR frame render time: %.0fms - %.2f FPS (%.2f FPS 8 frames average)\n", - duration_ms, - 1000.0 / duration_ms, - 1000.0 / (avg_ms_tot / draw_info->last_frame_times.size())); -} - -void Session::endFrameDrawing(std::vector *layers) -{ - XrFrameEndInfo end_info{XR_TYPE_FRAME_END_INFO}; - - end_info.displayTime = m_draw_info->frame_state.predictedDisplayTime; - end_info.environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE; - end_info.layerCount = layers->size(); - end_info.layers = layers->data(); - - CHECK_XR(xrEndFrame(m_oxr->session, &end_info), "Failed to submit rendered frame."); - - if (m_context->isDebugTimeMode()) { - print_debug_timings(m_draw_info.get()); - } -} - -void Session::draw(void *draw_customdata) -{ - std::vector - projection_layer_views; // Keep alive until xrEndFrame() call! - XrCompositionLayerProjection proj_layer; - std::vector layers; - - beginFrameDrawing(); - - if (m_draw_info->frame_state.shouldRender) { - proj_layer = drawLayer(projection_layer_views, draw_customdata); - layers.push_back(reinterpret_cast(&proj_layer)); - } - - endFrameDrawing(&layers); -} - -static void vamr_draw_view_info_from_view(const XrView &view, VAMR_DrawViewInfo &r_info) -{ -#if 0 - /* Set and convert to Blender coodinate space */ - r_info.pose.position[0] = view.pose.position.x; - r_info.pose.position[1] = -view.pose.position.z; - r_info.pose.position[2] = view.pose.position.y; - r_info.pose.orientation_quat[0] = view.pose.orientation.w; - r_info.pose.orientation_quat[1] = view.pose.orientation.x; - r_info.pose.orientation_quat[2] = -view.pose.orientation.z; - r_info.pose.orientation_quat[3] = view.pose.orientation.y; -#else - r_info.pose.position[0] = view.pose.position.x; - r_info.pose.position[1] = view.pose.position.y; - r_info.pose.position[2] = view.pose.position.z; - r_info.pose.orientation_quat[0] = view.pose.orientation.w; - r_info.pose.orientation_quat[1] = view.pose.orientation.x; - r_info.pose.orientation_quat[2] = view.pose.orientation.y; - r_info.pose.orientation_quat[3] = view.pose.orientation.z; -#endif - - r_info.fov.angle_left = view.fov.angleLeft; - r_info.fov.angle_right = view.fov.angleRight; - r_info.fov.angle_up = view.fov.angleUp; - r_info.fov.angle_down = view.fov.angleDown; -} - -static bool vamr_draw_view_expects_srgb_buffer(const Context *context) -{ - /* WMR seems to be faulty and doesn't do OETF transform correctly. So expect a SRGB buffer to - * compensate. */ - return context->getOpenXRRuntimeID() == OPENXR_RUNTIME_WMR; -} - -void Session::drawView(XrSwapchain swapchain, - XrCompositionLayerProjectionView &proj_layer_view, - XrView &view, - void *draw_customdata) -{ - XrSwapchainImageAcquireInfo acquire_info{XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO}; - XrSwapchainImageWaitInfo wait_info{XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO}; - XrSwapchainImageReleaseInfo release_info{XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO}; - XrSwapchainImageBaseHeader *swapchain_image; - VAMR_DrawViewInfo draw_view_info{}; - uint32_t swapchain_idx; - - CHECK_XR(xrAcquireSwapchainImage(swapchain, &acquire_info, &swapchain_idx), - "Failed to acquire swapchain image for the VR session."); - wait_info.timeout = XR_INFINITE_DURATION; - CHECK_XR(xrWaitSwapchainImage(swapchain, &wait_info), - "Failed to acquire swapchain image for the VR session."); - - proj_layer_view.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW; - proj_layer_view.pose = view.pose; - proj_layer_view.fov = view.fov; - proj_layer_view.subImage.swapchain = swapchain; - proj_layer_view.subImage.imageRect.offset = {0, 0}; - proj_layer_view.subImage.imageRect.extent = {m_oxr->swapchain_image_width, - m_oxr->swapchain_image_height}; - - swapchain_image = m_oxr->swapchain_images[swapchain][swapchain_idx]; - - draw_view_info.expects_srgb_buffer = vamr_draw_view_expects_srgb_buffer(m_context); - draw_view_info.ofsx = proj_layer_view.subImage.imageRect.offset.x; - draw_view_info.ofsy = proj_layer_view.subImage.imageRect.offset.y; - draw_view_info.width = proj_layer_view.subImage.imageRect.extent.width; - draw_view_info.height = proj_layer_view.subImage.imageRect.extent.height; - vamr_draw_view_info_from_view(view, draw_view_info); - - m_context->getCustomFuncs()->draw_view_fn(&draw_view_info, draw_customdata); - m_gpu_binding->submitToSwapchain(swapchain_image, &draw_view_info); - - CHECK_XR(xrReleaseSwapchainImage(swapchain, &release_info), - "Failed to release swapchain image used to submit VR session frame."); -} - -XrCompositionLayerProjection Session::drawLayer( - std::vector &proj_layer_views, void *draw_customdata) -{ - XrViewLocateInfo viewloc_info{XR_TYPE_VIEW_LOCATE_INFO}; - XrViewState view_state{XR_TYPE_VIEW_STATE}; - XrCompositionLayerProjection layer{XR_TYPE_COMPOSITION_LAYER_PROJECTION}; - uint32_t view_count; - - viewloc_info.viewConfigurationType = m_oxr->view_type; - viewloc_info.displayTime = m_draw_info->frame_state.predictedDisplayTime; - viewloc_info.space = m_oxr->reference_space; - - CHECK_XR(xrLocateViews(m_oxr->session, - &viewloc_info, - &view_state, - m_oxr->views.size(), - &view_count, - m_oxr->views.data()), - "Failed to query frame view and projection state."); - assert(m_oxr->swapchains.size() == view_count); - - proj_layer_views.resize(view_count); - - for (uint32_t view_idx = 0; view_idx < view_count; view_idx++) { - drawView(m_oxr->swapchains[view_idx], - proj_layer_views[view_idx], - m_oxr->views[view_idx], - draw_customdata); - } - - layer.space = m_oxr->reference_space; - layer.viewCount = proj_layer_views.size(); - layer.views = proj_layer_views.data(); - - return layer; -} - -/** \} */ /* Drawing */ - -/* -------------------------------------------------------------------- */ -/** \name State Queries - * - * \{ */ - -bool Session::isRunning() const -{ - if (m_oxr->session == XR_NULL_HANDLE) { - return false; - } - switch (m_oxr->session_state) { - case XR_SESSION_STATE_READY: - case XR_SESSION_STATE_SYNCHRONIZED: - case XR_SESSION_STATE_VISIBLE: - case XR_SESSION_STATE_FOCUSED: - return true; - default: - return false; - } -} - -/** \} */ /* State Queries */ - -/* -------------------------------------------------------------------- */ -/** \name Graphics Context Injection - * - * Sessions need access to Ghost graphics context information. Additionally, this API allows - * creating contexts on the fly (created on start, destructed on end). For this, callbacks to bind - * (potentially create) and unbind (potentially destruct) a Ghost graphics context have to be set, - * which will be called on session start and end respectively. - * - * \{ */ - -void Session::bindGraphicsContext() -{ - const CustomFuncs *custom_funcs = m_context->getCustomFuncs(); - assert(custom_funcs->gpu_ctx_bind_fn); - m_gpu_ctx = static_cast( - custom_funcs->gpu_ctx_bind_fn(m_context->getGraphicsBindingType())); -} -void Session::unbindGraphicsContext() -{ - const CustomFuncs *custom_funcs = m_context->getCustomFuncs(); - if (custom_funcs->gpu_ctx_unbind_fn) { - custom_funcs->gpu_ctx_unbind_fn(m_context->getGraphicsBindingType(), m_gpu_ctx); - } - m_gpu_ctx = nullptr; -} - -/** \} */ /* Graphics Context Injection */ - -} // namespace VAMR diff --git a/intern/vamr/intern/VAMR_Session.h b/intern/vamr/intern/VAMR_Session.h deleted file mode 100644 index ab324dc2abb..00000000000 --- a/intern/vamr/intern/VAMR_Session.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup VAMR - */ - -#ifndef __VAMR_SESSION_H__ -#define __VAMR_SESSION_H__ - -#include -#include - -namespace VAMR { - -class Session { - public: - enum eLifeExpectancy { - SESSION_KEEP_ALIVE, - SESSION_DESTROY, - }; - - Session(class Context *xr_context); - ~Session(); - - void start(const VAMR_SessionBeginInfo *begin_info); - void requestEnd(); - - eLifeExpectancy handleStateChangeEvent(const struct XrEventDataSessionStateChanged *lifecycle); - - bool isRunning() const; - - void unbindGraphicsContext(); /* public so context can ensure it's unbound as needed. */ - - void draw(void *draw_customdata); - - private: - /** Pointer back to context managing this session. Would be nice to avoid, but needed to access - * custom callbacks set before session start. */ - class Context *m_context; - - std::unique_ptr m_oxr; /* Could use stack, but PImpl is preferable */ - - /** Active Ghost graphic context. Owned by Blender, not VAMR. */ - class GHOST_Context *m_gpu_ctx{nullptr}; - std::unique_ptr m_gpu_binding; - - /** Rendering information. Set when drawing starts. */ - std::unique_ptr m_draw_info; - - void initSystem(); - void end(); - - void bindGraphicsContext(); - - void prepareDrawing(); - XrCompositionLayerProjection drawLayer( - std::vector &proj_layer_views, void *draw_customdata); - void drawView(XrSwapchain swapchain, - XrCompositionLayerProjectionView &proj_layer_view, - XrView &view, - void *draw_customdata); - void beginFrameDrawing(); - void endFrameDrawing(std::vector *layers); -}; - -} // namespace VAMR - -#endif /* __VAMR_SESSION_H__ */ diff --git a/intern/vamr/intern/VAMR_capi.cc b/intern/vamr/intern/VAMR_capi.cc deleted file mode 100644 index dc4f8d51943..00000000000 --- a/intern/vamr/intern/VAMR_capi.cc +++ /dev/null @@ -1,80 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup VAMR - */ - -#include "VAMR_Exception.h" -#include "VAMR_IContext.h" -#include "VAMR_Types.h" -#include "VAMR_capi.h" - -#define VAMR_CAPI_CALL(call, ctx) \ - try { \ - call; \ - } \ - catch (VAMR::Exception & e) { \ - (ctx)->dispatchErrorMessage(&e); \ - } - -#define VAMR_CAPI_CALL_RET(call, ctx) \ - try { \ - return call; \ - } \ - catch (VAMR::Exception & e) { \ - (ctx)->dispatchErrorMessage(&e); \ - } - -void VAMR_SessionStart(VAMR_ContextHandle xr_contexthandle, - const VAMR_SessionBeginInfo *begin_info) -{ - VAMR::IContext *xr_context = (VAMR::IContext *)xr_contexthandle; - VAMR_CAPI_CALL(xr_context->startSession(begin_info), xr_context); -} - -void VAMR_SessionEnd(VAMR_ContextHandle xr_contexthandle) -{ - VAMR::IContext *xr_context = (VAMR::IContext *)xr_contexthandle; - VAMR_CAPI_CALL(xr_context->endSession(), xr_context); -} - -int VAMR_SessionIsRunning(const VAMR_ContextHandle xr_contexthandle) -{ - const VAMR::IContext *xr_context = (const VAMR::IContext *)xr_contexthandle; - VAMR_CAPI_CALL_RET(xr_context->isSessionRunning(), xr_context); - return 0; // Only reached if exception is thrown. -} - -void VAMR_SessionDrawViews(VAMR_ContextHandle xr_contexthandle, void *draw_customdata) -{ - VAMR::IContext *xr_context = (VAMR::IContext *)xr_contexthandle; - VAMR_CAPI_CALL(xr_context->drawSessionViews(draw_customdata), xr_context); -} - -void VAMR_GraphicsContextBindFuncs(VAMR_ContextHandle xr_contexthandle, - VAMR_GraphicsContextBindFn bind_fn, - VAMR_GraphicsContextUnbindFn unbind_fn) -{ - VAMR::IContext *xr_context = (VAMR::IContext *)xr_contexthandle; - VAMR_CAPI_CALL(xr_context->setGraphicsContextBindFuncs(bind_fn, unbind_fn), xr_context); -} - -void VAMR_DrawViewFunc(VAMR_ContextHandle xr_contexthandle, VAMR_DrawViewFn draw_view_fn) -{ - VAMR::IContext *xr_context = (VAMR::IContext *)xr_contexthandle; - VAMR_CAPI_CALL(xr_context->setDrawViewFunc(draw_view_fn), xr_context); -} diff --git a/intern/vamr/intern/VAMR_intern.h b/intern/vamr/intern/VAMR_intern.h deleted file mode 100644 index a7c78035229..00000000000 --- a/intern/vamr/intern/VAMR_intern.h +++ /dev/null @@ -1,138 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup VAMR - */ - -#ifndef __VAMR_INTERN_H__ -#define __VAMR_INTERN_H__ - -#include -#include - -#include "VAMR_openxr_includes.h" - -namespace VAMR { - -#define CHECK_XR(call, error_msg) \ - { \ - XrResult _res = call; \ - if (XR_FAILED(_res)) { \ - throw VAMR::Exception(error_msg, __FILE__, __LINE__, _res); \ - } \ - } \ - (void)0 - -#define CHECK_XR_ASSERT(call) \ - { \ - XrResult _res = call; \ - assert(_res == XR_SUCCESS); \ - (void)_res; \ - } \ - (void)0 - -#define THROW_XR(error_msg) throw VAMR::Exception(error_msg, __FILE__, __LINE__); - -#define XR_DEBUG_ONLY_BEGIN(ctx) \ - if ((ctx)->isDebugMode()) { \ - (void)0 -#define XR_DEBUG_ONLY_END \ - } \ - (void)0 - -#define XR_DEBUG_PRINTF(ctx, ...) \ - if ((ctx)->isDebugMode()) { \ - printf(__VA_ARGS__); \ - } \ - (void)0 - -#define XR_DEBUG_ONLY_CALL(ctx, call) \ - if ((ctx)->isDebugMode()) { \ - call; \ - } \ - (void)0 - -/** - * Helper for RAII usage of OpenXR handles (defined with XR_DEFINE_HANDLE). This is based on - * `std::unique_ptr`, to give similar behavior and usage (e.g. functions like #get() and #release() - * are supported). - */ -template class unique_oxr_ptr { - public: - using xr_destroy_func = XrResult (*)(_OXR_HANDLE); - - unique_oxr_ptr(xr_destroy_func destroy_fn) : m_destroy_fn(destroy_fn) - { - } - - unique_oxr_ptr(unique_oxr_ptr &&other) : m_ptr(other.release()), m_destroy_fn(other.m_destroy_fn) - { - } - - /** - * Construct the pointer from a xrCreate function passed as \a create_fn, with additional - * arguments forwarded from \a args. - * For example, this: - * \code - * some_unique_oxr_ptr.contruct(xrCreateFoo, some_arg, some_other_arg); - * \endcode - * effectively results in this call: - * \code - * xrCreateFoo(some_arg, some_other_arg, &some_unique_oxr_ptr.m_ptr); - * \endcode - */ - template - XrResult construct(_create_func create_fn, _Args &&... args) - { - assert(m_ptr == XR_NULL_HANDLE); - return create_fn(std::forward<_Args>(args)..., &m_ptr); - } - - ~unique_oxr_ptr() - { - if (m_ptr != XR_NULL_HANDLE) { - m_destroy_fn(m_ptr); - } - } - - _OXR_HANDLE get() - { - return m_ptr; - } - - _OXR_HANDLE release() - { - _OXR_HANDLE ptr = get(); - m_ptr = XR_NULL_HANDLE; - return ptr; - } - - /* operator= defines not needed for now. */ - - unique_oxr_ptr(const unique_oxr_ptr &) = delete; - unique_oxr_ptr &operator=(const unique_oxr_ptr &) = delete; - - private: - _OXR_HANDLE m_ptr{XR_NULL_HANDLE}; - xr_destroy_func m_destroy_fn; -}; - -::VAMR_TSuccess VAMR_EventsHandle(class Context *xr_context); - -} // namespace VAMR - -#endif /* __VAMR_INTERN_H__ */ diff --git a/intern/vamr/intern/VAMR_openxr_includes.h b/intern/vamr/intern/VAMR_openxr_includes.h deleted file mode 100644 index e5f053e5919..00000000000 --- a/intern/vamr/intern/VAMR_openxr_includes.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup VAMR - * - * \note This is taken mostly from the OpenXR SDK, but with modified D3D versions (e.g. d3d11_4.h - * -> d3d11.h). Take care for that when updating, we don't want to require newest Win SDKs to be - * installed. - */ - -#ifndef __VAMR_OPENXR_INCLUDES_H__ -#define __VAMR_OPENXR_INCLUDES_H__ - -/* Platform headers */ -#ifdef XR_USE_PLATFORM_WIN32 -# define WIN32_LEAN_AND_MEAN -# define NOMINMAX -# include -#endif - -/* Graphics headers */ -#ifdef XR_USE_GRAPHICS_API_D3D10 -# include -#endif -#ifdef XR_USE_GRAPHICS_API_D3D11 -# include -#endif -#ifdef XR_USE_GRAPHICS_API_D3D12 -# include -#endif -#ifdef WITH_X11 -# include -#endif - -#include -#include - -#endif /* __VAMR_OPENXR_INCLUDES_H__ */ diff --git a/intern/vamr/intern/openxr_includes.h b/intern/vamr/intern/openxr_includes.h new file mode 100644 index 00000000000..e5f053e5919 --- /dev/null +++ b/intern/vamr/intern/openxr_includes.h @@ -0,0 +1,52 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup VAMR + * + * \note This is taken mostly from the OpenXR SDK, but with modified D3D versions (e.g. d3d11_4.h + * -> d3d11.h). Take care for that when updating, we don't want to require newest Win SDKs to be + * installed. + */ + +#ifndef __VAMR_OPENXR_INCLUDES_H__ +#define __VAMR_OPENXR_INCLUDES_H__ + +/* Platform headers */ +#ifdef XR_USE_PLATFORM_WIN32 +# define WIN32_LEAN_AND_MEAN +# define NOMINMAX +# include +#endif + +/* Graphics headers */ +#ifdef XR_USE_GRAPHICS_API_D3D10 +# include +#endif +#ifdef XR_USE_GRAPHICS_API_D3D11 +# include +#endif +#ifdef XR_USE_GRAPHICS_API_D3D12 +# include +#endif +#ifdef WITH_X11 +# include +#endif + +#include +#include + +#endif /* __VAMR_OPENXR_INCLUDES_H__ */ diff --git a/intern/vamr/intern/utils.h b/intern/vamr/intern/utils.h new file mode 100644 index 00000000000..88fbeda96e5 --- /dev/null +++ b/intern/vamr/intern/utils.h @@ -0,0 +1,136 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup VAMR + */ + +#ifndef __UTILS_H__ +#define __UTILS_H__ + +#include +#include + +namespace VAMR { + +#define CHECK_XR(call, error_msg) \ + { \ + XrResult _res = call; \ + if (XR_FAILED(_res)) { \ + throw VAMR::Exception(error_msg, __FILE__, __LINE__, _res); \ + } \ + } \ + (void)0 + +#define CHECK_XR_ASSERT(call) \ + { \ + XrResult _res = call; \ + assert(_res == XR_SUCCESS); \ + (void)_res; \ + } \ + (void)0 + +#define THROW_XR(error_msg) throw VAMR::Exception(error_msg, __FILE__, __LINE__); + +#define XR_DEBUG_ONLY_BEGIN(ctx) \ + if ((ctx)->isDebugMode()) { \ + (void)0 +#define XR_DEBUG_ONLY_END \ + } \ + (void)0 + +#define XR_DEBUG_PRINTF(ctx, ...) \ + if ((ctx)->isDebugMode()) { \ + printf(__VA_ARGS__); \ + } \ + (void)0 + +#define XR_DEBUG_ONLY_CALL(ctx, call) \ + if ((ctx)->isDebugMode()) { \ + call; \ + } \ + (void)0 + +/** + * Helper for RAII usage of OpenXR handles (defined with XR_DEFINE_HANDLE). This is based on + * `std::unique_ptr`, to give similar behavior and usage (e.g. functions like #get() and #release() + * are supported). + */ +template class unique_oxr_ptr { + public: + using xr_destroy_func = XrResult (*)(_OXR_HANDLE); + + unique_oxr_ptr(xr_destroy_func destroy_fn) : m_destroy_fn(destroy_fn) + { + } + + unique_oxr_ptr(unique_oxr_ptr &&other) : m_ptr(other.release()), m_destroy_fn(other.m_destroy_fn) + { + } + + /** + * Construct the pointer from a xrCreate function passed as \a create_fn, with additional + * arguments forwarded from \a args. + * For example, this: + * \code + * some_unique_oxr_ptr.contruct(xrCreateFoo, some_arg, some_other_arg); + * \endcode + * effectively results in this call: + * \code + * xrCreateFoo(some_arg, some_other_arg, &some_unique_oxr_ptr.m_ptr); + * \endcode + */ + template + XrResult construct(_create_func create_fn, _Args &&... args) + { + assert(m_ptr == XR_NULL_HANDLE); + return create_fn(std::forward<_Args>(args)..., &m_ptr); + } + + ~unique_oxr_ptr() + { + if (m_ptr != XR_NULL_HANDLE) { + m_destroy_fn(m_ptr); + } + } + + _OXR_HANDLE get() + { + return m_ptr; + } + + _OXR_HANDLE release() + { + _OXR_HANDLE ptr = get(); + m_ptr = XR_NULL_HANDLE; + return ptr; + } + + /* operator= defines not needed for now. */ + + unique_oxr_ptr(const unique_oxr_ptr &) = delete; + unique_oxr_ptr &operator=(const unique_oxr_ptr &) = delete; + + private: + _OXR_HANDLE m_ptr{XR_NULL_HANDLE}; + xr_destroy_func m_destroy_fn; +}; + +::VAMR_TSuccess VAMR_EventsHandle(class Context *xr_context); + +} // namespace VAMR + +#endif /* __UTILS_H__ */ -- cgit v1.2.3