838 lines · 28.3 KB · C++
RawDownload
//// Overte OpenXR Plugin//// Copyright 2024 Lubosz Sarnecki// Copyright 2024 Overte e.V.//// SPDX-License-Identifier: Apache-2.0// #include "OpenXrContext.h"#include <QLoggingCategory>#include <QString>#include <QGuiApplication> #if defined(Q_OS_LINUX)#include <QOpenGLContext>#include <QtPlatformHeaders/QGLXNativeContext>#endif #if defined(HAVE_VULKAN)#include <QMessageBox>#endif #include <sstream> Q_DECLARE_LOGGING_CATEGORY(xr_context_cat)Q_LOGGING_CATEGORY(xr_context_cat, "openxr.context") // Checks XrResult, returns false on errors and logs the error as qCritical.bool xrCheck(XrInstance instance, XrResult result, const char* message) {    if (XR_SUCCEEDED(result))        return true;     char errorName[XR_MAX_RESULT_STRING_SIZE];    if (instance != XR_NULL_HANDLE) {        xrResultToString(instance, result, errorName);    } else {        sprintf(errorName, "%d", result);    }     qCCritical(xr_context_cat, "%s: %s", errorName, message);     return false;} XRAPI_ATTR static XrBool32 XRAPI_CALL debugMessageCallback(    XrDebugUtilsMessageSeverityFlagsEXT severity,    XrDebugUtilsMessageTypeFlagsEXT type,    const XrDebugUtilsMessengerCallbackDataEXT* data,    void*) {    // bitflags, so can't use a switch    // TODO: does any runtime actually set multiple of these bits at once?    if (severity <= XR_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) {        qCDebug(xr_context_cat, "%s: %s", data->functionName, data->message);    } else if (severity <= XR_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) {        qCInfo(xr_context_cat, "%s: %s", data->functionName, data->message);    } else if (severity <= XR_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {        qCWarning(xr_context_cat, "%s: %s", data->functionName, data->message);    } else {        // XR_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT        qCCritical(xr_context_cat, "%s: %s", data->functionName, data->message);    }     return XR_FALSE;} // Extension functions must be loaded with xrGetInstanceProcAddrstatic PFN_xrGetOpenGLGraphicsRequirementsKHR pfnGetOpenGLGraphicsRequirementsKHR = nullptr; static bool loadXrFunction(XrInstance instance, const char* name, PFN_xrVoidFunction* out) {    auto result = xrGetInstanceProcAddr(instance, name, out);     if (result != XR_SUCCESS) {        qCCritical(xr_context_cat) << "Failed to load OpenXR function '" << name << "'";        return false;    }     return true;} OpenXrContext::OpenXrContext() {#if defined(HAVE_VULKAN)    _isSupported = false;    qCCritical(xr_context_cat, "OpenXR is not supported on the Vulkan backend yet.");    QMessageBox::critical(nullptr, "OpenXR", "OpenXR is not supported on the Vulkan backend yet.");#else    _isSupported = initPreGraphics();    if (!_isSupported) {        qCWarning(xr_context_cat, "OpenXR is not supported.");    }#endif} OpenXrContext::~OpenXrContext() {    if (_instance == XR_NULL_HANDLE) {        return;    }    XrResult res = xrDestroyInstance(_instance);    if (res != XR_SUCCESS) {        qCCritical(xr_context_cat, "Failed to destroy OpenXR instance");    }    qCDebug(xr_context_cat, "Destroyed instance.");} bool OpenXrContext::initInstance() {#if defined(HAVE_VULKAN)    // VKTODO    return false;#else    uint32_t count = 0;    XrResult result = xrEnumerateInstanceExtensionProperties(nullptr, 0, &count, nullptr);     // Since this is the first OpenXR call we do, check here if RUNTIME_UNAVAILABLE is returned.    if (result == XR_ERROR_RUNTIME_UNAVAILABLE) {        qCCritical(xr_context_cat, "XR_ERROR_RUNTIME_UNAVAILABLE: Is XR_RUNTIME_JSON set correctly?");        return false;    }     if (!xrCheck(XR_NULL_HANDLE, result, "Failed to enumerate number of extensions."))        return false;     std::vector<XrExtensionProperties> properties;    for (uint32_t i = 0; i < count; i++) {        XrExtensionProperties props = { .type = XR_TYPE_EXTENSION_PROPERTIES };        properties.push_back(props);    }     result = xrEnumerateInstanceExtensionProperties(nullptr, count, &count, properties.data());    if (!xrCheck(XR_NULL_HANDLE, result, "Failed to enumerate extensions."))        return false;     bool openglSupported = false;    bool userPresenceSupported = false;    bool odysseyControllerSupported = false;    bool handTrackingSupported = false;    bool palmPoseSupported = false;    bool MNDX_xdevSpaceSupported = false;    bool HTCX_viveTrackerInteractionSupported = false;    bool MNDX_eglEnableSupported = false;    bool EXT_debugUtilsSupported = false;     qCInfo(xr_context_cat, "Runtime supports %d extensions:", count);    for (uint32_t i = 0; i < count; i++) {        qCInfo(xr_context_cat, "%s v%d", properties[i].extensionName, properties[i].extensionVersion);        if (strcmp(XR_KHR_OPENGL_ENABLE_EXTENSION_NAME, properties[i].extensionName) == 0) {            openglSupported = true;        } else if (strcmp(XR_EXT_USER_PRESENCE_EXTENSION_NAME, properties[i].extensionName) == 0) {            userPresenceSupported = true;        } else if (strcmp(XR_EXT_SAMSUNG_ODYSSEY_CONTROLLER_EXTENSION_NAME, properties[i].extensionName) == 0) {            odysseyControllerSupported = true;        } else if (strcmp(XR_EXT_HAND_TRACKING_EXTENSION_NAME, properties[i].extensionName) == 0) {            handTrackingSupported = true;        } else if (strcmp(XR_MNDX_XDEV_SPACE_EXTENSION_NAME, properties[i].extensionName) == 0) {            MNDX_xdevSpaceSupported = true;        } else if (strcmp(XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME, properties[i].extensionName) == 0) {            HTCX_viveTrackerInteractionSupported = true;        } else if (strcmp(XR_EXT_PALM_POSE_EXTENSION_NAME, properties[i].extensionName) == 0) {            palmPoseSupported = true;#if defined(XR_USE_PLATFORM_EGL)        } else if (strcmp(XR_MNDX_EGL_ENABLE_EXTENSION_NAME, properties[i].extensionName) == 0) {            MNDX_eglEnableSupported = true;#endif        } else if (strcmp(XR_EXT_DEBUG_UTILS_EXTENSION_NAME, properties[i].extensionName) == 0) {            EXT_debugUtilsSupported = true;        }    }     if (!openglSupported) {        qCCritical(xr_context_cat, "Runtime does not support OpenGL!");        return false;    }     std::vector<const char*> enabled = {XR_KHR_OPENGL_ENABLE_EXTENSION_NAME};     if (userPresenceSupported) {        enabled.push_back(XR_EXT_USER_PRESENCE_EXTENSION_NAME);        _userPresenceAvailable = true;    }     if (odysseyControllerSupported) {        enabled.push_back(XR_EXT_SAMSUNG_ODYSSEY_CONTROLLER_EXTENSION_NAME);    }     if (handTrackingSupported) {        enabled.push_back(XR_EXT_HAND_TRACKING_EXTENSION_NAME);        _handTrackingSupported = true;    }     if (MNDX_xdevSpaceSupported) {        enabled.push_back(XR_MNDX_XDEV_SPACE_EXTENSION_NAME);        _MNDX_xdevSpaceSupported = true;    }     if (HTCX_viveTrackerInteractionSupported) {        enabled.push_back(XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);        _HTCX_viveTrackerInteractionSupported = true;    }     if (palmPoseSupported) {        enabled.push_back(XR_EXT_PALM_POSE_EXTENSION_NAME);        _palmPoseSupported = true;    } #if defined(XR_USE_PLATFORM_EGL)    if (MNDX_eglEnableSupported) {        enabled.push_back(XR_MNDX_EGL_ENABLE_EXTENSION_NAME);        _MNDX_eglEnableSupported = true;    }#endif     if (EXT_debugUtilsSupported) {        enabled.push_back(XR_EXT_DEBUG_UTILS_EXTENSION_NAME);        _EXT_debugUtilsSupported = true;    }     XrInstanceCreateInfo info = {        .type = XR_TYPE_INSTANCE_CREATE_INFO,        .applicationInfo = {            .applicationName = "Overte",            .applicationVersion = 1,            .engineName = "Overte",            .engineVersion = 0,            .apiVersion = XR_API_VERSION_1_0,        },        .enabledExtensionCount = (uint32_t)enabled.size(),        .enabledExtensionNames = enabled.data(),    };     result = xrCreateInstance(&info, &_instance);     if (result == XR_ERROR_RUNTIME_FAILURE) {        qCCritical(xr_context_cat, "XR_ERROR_RUNTIME_FAILURE: Is the OpenXR runtime up and running?");        return false;    }     if (!xrCheck(XR_NULL_HANDLE, result, "Failed to create OpenXR instance."))        return false;     if (!loadXrFunction(_instance, "xrGetOpenGLGraphicsRequirementsKHR", (PFN_xrVoidFunction*)&pfnGetOpenGLGraphicsRequirementsKHR)) {        qCCritical(xr_context_cat) << "Failed to get OpenGL graphics requirements function!";        return false;    }     xrStringToPath(_instance, "/user/hand/left", &_handPaths[0]);    xrStringToPath(_instance, "/user/hand/right", &_handPaths[1]);     xrStringToPath(_instance, "/interaction_profiles/htc/vive_controller", &_viveControllerPath);     return true;#endif} bool OpenXrContext::initSystem() {    XrSystemGetInfo info = {        .type = XR_TYPE_SYSTEM_GET_INFO,        .formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY,    };     XrResult result = xrGetSystem(_instance, &info, &_systemId);    if (!xrCheck(_instance, result, "Failed to get system for HMD form factor."))        return false;     XrSystemProperties props = {        .type = XR_TYPE_SYSTEM_PROPERTIES,        .next = nullptr,    };     XrSystemHandTrackingPropertiesEXT handTrackingProps = {        .type = XR_TYPE_SYSTEM_HAND_TRACKING_PROPERTIES_EXT,        .next = props.next,    };     if (_handTrackingSupported) {        props.next = &handTrackingProps;    }     XrSystemXDevSpacePropertiesMNDX xdevProps = {        .type =XR_TYPE_SYSTEM_XDEV_SPACE_PROPERTIES_MNDX,        .next = props.next,    };     if (_MNDX_xdevSpaceSupported) {        props.next = &xdevProps;    }     result = xrGetSystemProperties(_instance, _systemId, &props);    if (!xrCheck(_instance, result, "Failed to get System properties"))        return false;     _systemName = QString::fromUtf8(props.systemName);     qCInfo(xr_context_cat, "System name         : %s", props.systemName);    qCInfo(xr_context_cat, "Max layers          : %d", props.graphicsProperties.maxLayerCount);    qCInfo(xr_context_cat, "Max swapchain size  : %dx%d", props.graphicsProperties.maxSwapchainImageHeight,           props.graphicsProperties.maxSwapchainImageWidth);    qCInfo(xr_context_cat, "Orientation Tracking: %d", props.trackingProperties.orientationTracking);    qCInfo(xr_context_cat, "Position Tracking   : %d", props.trackingProperties.positionTracking);     if (_EXT_debugUtilsSupported) {        xrGetInstanceProcAddr(            _instance,            "xrCreateDebugUtilsMessengerEXT",            reinterpret_cast<PFN_xrVoidFunction*>(&xrCreateDebugUtilsMessengerEXT)        );         XrDebugUtilsMessengerCreateInfoEXT createInfo = {            .type = XR_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,            .next = nullptr,            .messageSeverities = XR_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | XR_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,            .messageTypes = XR_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT,            .userCallback = debugMessageCallback,            .userData = nullptr,        };         xrCreateDebugUtilsMessengerEXT(            _instance,            &createInfo,            &_debugMessenger        );    }     auto next = reinterpret_cast<const XrExtensionProperties*>(props.next);    while (next) {        if (next->type == XR_TYPE_SYSTEM_HAND_TRACKING_PROPERTIES_EXT) {            auto ext = reinterpret_cast<const XrSystemHandTrackingPropertiesEXT*>(next);            _handTrackingSupported = ext->supportsHandTracking;             if (!_handTrackingSupported) {                next = reinterpret_cast<const XrExtensionProperties*>(next->next);                continue;            }             xrGetInstanceProcAddr(                _instance,                "xrCreateHandTrackerEXT",                reinterpret_cast<PFN_xrVoidFunction*>(&xrCreateHandTrackerEXT)            );             xrGetInstanceProcAddr(                _instance,                "xrDestroyHandTrackerEXT",                reinterpret_cast<PFN_xrVoidFunction*>(&xrDestroyHandTrackerEXT)            );             xrGetInstanceProcAddr(                _instance,                "xrLocateHandJointsEXT",                reinterpret_cast<PFN_xrVoidFunction*>(&xrLocateHandJointsEXT)            );        }         if (next->type == XR_TYPE_SYSTEM_XDEV_SPACE_PROPERTIES_MNDX) {            auto ext = reinterpret_cast<const XrSystemXDevSpacePropertiesMNDX*>(next);            _MNDX_xdevSpaceSupported = ext->supportsXDevSpace;             if (!_MNDX_xdevSpaceSupported) {                next = reinterpret_cast<const XrExtensionProperties*>(next->next);                continue;            }             xrGetInstanceProcAddr(                _instance,                "xrCreateXDevListMNDX",                reinterpret_cast<PFN_xrVoidFunction*>(&xrCreateXDevListMNDX)            );             xrGetInstanceProcAddr(                _instance,                "xrGetXDevListGenerationNumberMNDX",                reinterpret_cast<PFN_xrVoidFunction*>(&xrGetXDevListGenerationNumberMNDX)            );             xrGetInstanceProcAddr(                _instance,                "xrEnumerateXDevsMNDX",                reinterpret_cast<PFN_xrVoidFunction*>(&xrEnumerateXDevsMNDX)            );             xrGetInstanceProcAddr(                _instance,                "xrGetXDevPropertiesMNDX",                reinterpret_cast<PFN_xrVoidFunction*>(&xrGetXDevPropertiesMNDX)            );             xrGetInstanceProcAddr(                _instance,                "xrDestroyXDevListMNDX",                reinterpret_cast<PFN_xrVoidFunction*>(&xrDestroyXDevListMNDX)            );             xrGetInstanceProcAddr(                _instance,                "xrCreateXDevSpaceMNDX",                reinterpret_cast<PFN_xrVoidFunction*>(&xrCreateXDevSpaceMNDX)            );        }         next = reinterpret_cast<const XrExtensionProperties*>(next->next);    }     // don't start up hand tracking stuff if it's force disabled    if (qApp->arguments().contains("--xrNoHandTracking")) {        _handTrackingSupported = false;    }     if (qApp->arguments().contains("--xrNoBodyTracking")) {        _MNDX_xdevSpaceSupported = false;        _HTCX_viveTrackerInteractionSupported = false;    }     if (qApp->arguments().contains("--xrNoPalmPose")) {        _palmPoseSupported = false;    }     // disable the MNDX tracker extension if they're both available    if (_HTCX_viveTrackerInteractionSupported) {        _MNDX_xdevSpaceSupported = false;         xrGetInstanceProcAddr(            _instance,            "xrEnumerateViveTrackerPathsHTCX",            reinterpret_cast<PFN_xrVoidFunction*>(&xrEnumerateViveTrackerPathsHTCX)        );    }     if (_userPresenceAvailable) {        XrSystemUserPresencePropertiesEXT presenceProps = {XR_TYPE_SYSTEM_USER_PRESENCE_PROPERTIES_EXT};        XrSystemProperties sysProps = {XR_TYPE_SYSTEM_PROPERTIES, &presenceProps};        result = xrGetSystemProperties(_instance, _systemId, &sysProps);        if (xrCheck(XR_NULL_HANDLE, result, "Couldn't get system properties")) {            _userPresenceAvailable = presenceProps.supportsUserPresence;        }    }     return true;} bool OpenXrContext::initGraphics() {#if defined(HAVE_VULKAN)    // VKTODO    return false;#else    XrGraphicsRequirementsOpenGLKHR requirements = { .type = XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_KHR };    XrResult result = pfnGetOpenGLGraphicsRequirementsKHR(_instance, _systemId, &requirements);    return xrCheck(_instance, result, "Failed to get OpenGL graphics requirements!");#endif} bool OpenXrContext::requestExitSession() {    if (_session == XR_NULL_HANDLE) { return true; }     XrResult result = xrRequestExitSession(_session);    return xrCheck(_instance, result, "Failed to request exit session!");} bool OpenXrContext::initSession() {#if defined(HAVE_VULKAN)    // VKTODO    return false;#else    if (_session != XR_NULL_HANDLE) { return true; }     XrSessionCreateInfo info = {        .type = XR_TYPE_SESSION_CREATE_INFO,        .next = nullptr,        .systemId = _systemId,    };     bool eglBindingAvailable = false; #if defined(XR_USE_PLATFORM_EGL)    XrGraphicsBindingEGLMNDX eglBinding = {        .type = XR_TYPE_GRAPHICS_BINDING_EGL_MNDX,        .next = nullptr,    };     // try egl first since it should work on any platform    // do-while so we can break out early    do {        if (!_MNDX_eglEnableSupported) { break; }         auto eglContext = eglGetCurrentContext();        auto eglDisplay = eglGetCurrentDisplay();         if (!eglContext || !eglDisplay) { break; }         auto attribs = std::to_array<EGLint>({            EGL_RED_SIZE, 8,            EGL_GREEN_SIZE, 8,            EGL_RED_SIZE, 8,            EGL_SURFACE_TYPE, EGL_WINDOW_BIT,#if defined(Q_OS_ANDROID)            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT,#else            EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT | EGL_OPENGL_ES3_BIT,#endif            EGL_NONE // terminator        }); 

Lines 1–500 of 838.