#include "LunarWM_xr.h" #include "common.h" #include "vec.h" #include #include #include GLuint LunarWM_xr_get_swapchain_image( LunarWM *wm, int swapchain_images_i, uint32_t index) { return wm->xr.swapchain_images[swapchain_images_i].a_imgs[index].image; } bool LunarWM_xr_init(LunarWM *this) { XrResult res = XR_SUCCESS; XrApplicationInfo app_info = { .applicationVersion = 1, .engineVersion = 1, .apiVersion = XR_CURRENT_API_VERSION, }; strncpy((char *)app_info.applicationName, "LunarWM", XR_MAX_APPLICATION_NAME_SIZE); strncpy( (char *)app_info.engineName, "LunarWM Engine", XR_MAX_ENGINE_NAME_SIZE); char const *required_instance_extensions[] = { XR_EXT_DEBUG_UTILS_EXTENSION_NAME, XR_MNDX_EGL_ENABLE_EXTENSION_NAME, XR_KHR_OPENGL_ES_ENABLE_EXTENSION_NAME, }; char const *optional_instance_extensions[] = { XR_EXT_HAND_TRACKING_EXTENSION_NAME, }; bool hand_tracking_ext_available = false; char const **v_active_instance_extensions = vector_create(); uint32_t extension_properties_count = 0; XrExtensionProperties *extension_properties; if (xrEnumerateInstanceExtensionProperties( nullptr, 0, &extension_properties_count, nullptr) != XR_SUCCESS) { wlr_log(WLR_ERROR, "Failed to enumerate OpenXR instance extension properties (count)"); return false; } extension_properties = malloc(sizeof(*extension_properties) * extension_properties_count); for (uint32_t i = 0; i < extension_properties_count; ++i) { extension_properties[i].type = XR_TYPE_EXTENSION_PROPERTIES; extension_properties[i].next = NULL; } if (xrEnumerateInstanceExtensionProperties(nullptr, extension_properties_count, &extension_properties_count, extension_properties) != XR_SUCCESS) { wlr_log(WLR_ERROR, "Failed to enumerate OpenXR instance extension properties"); return false; } for (size_t i = 0; i < ARRAY_SZ(required_instance_extensions); i++) { char const *requested_instance_extension = required_instance_extensions[i]; bool found = false; for (int j = 0; j < extension_properties_count; j++) { if (strcmp(requested_instance_extension, extension_properties[j].extensionName) != 0) { continue; } vector_add( &v_active_instance_extensions, requested_instance_extension); found = true; break; } if (!found) { wlr_log(WLR_ERROR, "Failed to find OpenXR instance extension: %s", requested_instance_extension); return false; } } for (size_t i = 0; i < ARRAY_SZ(optional_instance_extensions); i++) { char const *requested_instance_extension = optional_instance_extensions[i]; bool found = false; for (int j = 0; j < extension_properties_count; j++) { if (strcmp(requested_instance_extension, extension_properties[j].extensionName) != 0) { continue; } vector_add( &v_active_instance_extensions, requested_instance_extension); found = true; break; } if (!found) { wlr_log(WLR_INFO, "Optional OpenXR instance extension missing: %s", requested_instance_extension); } else if (strcmp(requested_instance_extension, XR_EXT_HAND_TRACKING_EXTENSION_NAME) == 0) { hand_tracking_ext_available = true; } } { XrInstanceCreateInfo const ci = { .type = XR_TYPE_INSTANCE_CREATE_INFO, .next = nullptr, .createFlags = 0, .applicationInfo = app_info, .enabledApiLayerCount = 0, .enabledApiLayerNames = nullptr, .enabledExtensionCount = (uint32_t)vector_size(v_active_instance_extensions), .enabledExtensionNames = v_active_instance_extensions, }; XrInstance instance = nullptr; if (xrCreateInstance(&ci, &instance) != XR_SUCCESS) { wlr_log(WLR_ERROR, "Failed to create OpenXR instance"); return false; } this->xr.instance = instance; this->xr.CreateHandTrackerEXT = NULL; this->xr.DestroyHandTrackerEXT = NULL; this->xr.LocateHandJointsEXT = NULL; if (hand_tracking_ext_available) { res = xrGetInstanceProcAddr(this->xr.instance, "xrCreateHandTrackerEXT", (PFN_xrVoidFunction *)&this->xr.CreateHandTrackerEXT); if (res != XR_SUCCESS) { wlr_log(WLR_ERROR, "Failed to get proc addr xrCreateHandTrackerEXT " "(optional): %d", res); hand_tracking_ext_available = false; } } if (hand_tracking_ext_available) { res = xrGetInstanceProcAddr(this->xr.instance, "xrDestroyHandTrackerEXT", (PFN_xrVoidFunction *)&this->xr.DestroyHandTrackerEXT); if (res != XR_SUCCESS) { wlr_log(WLR_ERROR, "Failed to get proc addr xrDestroyHandTrackerEXT " "(optional): %d", res); hand_tracking_ext_available = false; } } if (hand_tracking_ext_available) { res = xrGetInstanceProcAddr(this->xr.instance, "xrLocateHandJointsEXT", (PFN_xrVoidFunction *)&this->xr.LocateHandJointsEXT); if (res != XR_SUCCESS) { wlr_log(WLR_ERROR, "Failed to get proc addr xrLocateHandJointsEXT (optional): " "%d", res); hand_tracking_ext_available = false; } } } { XrSystemGetInfo gi = { .type = XR_TYPE_SYSTEM_GET_INFO, .next = nullptr, }; XrFormFactor const factors[2] = { XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY, XR_FORM_FACTOR_HANDHELD_DISPLAY, }; for (size_t i = 0; i < ARRAY_SZ(factors); i++) { auto factor = factors[i]; gi.formFactor = factor; XrSystemId system_id = 0; if (xrGetSystem(this->xr.instance, &gi, &system_id) == XR_SUCCESS) { this->xr.system_id = system_id; break; } } if (!this->xr.system_id) { wlr_log(WLR_ERROR, "Failed to find valid form factor"); return false; } } { this->xr.hand_tracking_enabled = hand_tracking_ext_available; XrSystemProperties system_props = { .type = XR_TYPE_SYSTEM_PROPERTIES, .next = this->xr.hand_tracking_enabled ? &this->xr.hand_tracking_system_properties : NULL, }; res = xrGetSystemProperties( this->xr.instance, this->xr.system_id, &system_props); if (res != XR_SUCCESS) { wlr_log(WLR_ERROR, "xrGetSystemProperties failed: %d", res); return false; } if (this->xr.hand_tracking_enabled && !this->xr.hand_tracking_system_properties.supportsHandTracking) { wlr_log(WLR_INFO, "Hand tracking extension present but system does not support " "it"); this->xr.hand_tracking_enabled = false; } } XrGraphicsRequirementsOpenGLESKHR reqs = { .type = XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_ES_KHR, .next = nullptr, }; PFN_xrGetOpenGLESGraphicsRequirementsKHR xrGetOpenGLESGraphicsRequirementsKHR = nullptr; xrGetInstanceProcAddr(this->xr.instance, "xrGetOpenGLESGraphicsRequirementsKHR", (PFN_xrVoidFunction *)&xrGetOpenGLESGraphicsRequirementsKHR); if (xrGetOpenGLESGraphicsRequirementsKHR( this->xr.instance, this->xr.system_id, &reqs) != XR_SUCCESS) { wlr_log(WLR_ERROR, "Failed to get GLES graphics requirements"); return false; } wlr_log(WLR_INFO, "OpenGL ES range: %d.%d.%d - %d.%d.%d\n", XR_VERSION_MAJOR(reqs.minApiVersionSupported), XR_VERSION_MINOR(reqs.minApiVersionSupported), XR_VERSION_PATCH(reqs.minApiVersionSupported), XR_VERSION_MAJOR(reqs.maxApiVersionSupported), XR_VERSION_MINOR(reqs.maxApiVersionSupported), XR_VERSION_PATCH(reqs.maxApiVersionSupported)); wlr_log(WLR_DEBUG, "Creating XR stuff.."); glEnable(GL_DEBUG_OUTPUT_KHR); glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR); { XrGraphicsBindingEGLMNDX gbind = { .type = XR_TYPE_GRAPHICS_BINDING_EGL_MNDX, .next = nullptr, .getProcAddress = eglGetProcAddress, .display = this->wayland.egl_display, .config = this->wayland.egl_config, .context = this->wayland.egl_context, }; XrSessionCreateInfo const ci = { .type = XR_TYPE_SESSION_CREATE_INFO, .next = &gbind, .createFlags = 0, .systemId = this->xr.system_id, }; if (xrCreateSession(this->xr.instance, &ci, &this->xr.session) != XR_SUCCESS) { wlr_log(WLR_ERROR, "Failed to create OpenXR session"); return false; } } // Swapchain time! wlr_log(WLR_DEBUG, "Creating XR swapchains..."); XrViewConfigurationType *a_view_config_types; uint32_t view_config_types_count = 0; { if (xrEnumerateViewConfigurations(this->xr.instance, this->xr.system_id, 0, &view_config_types_count, nullptr) != XR_SUCCESS) { wlr_log(WLR_ERROR, "Failed to get amount of OpenXR view configurations"); return false; } a_view_config_types = malloc(sizeof(*a_view_config_types) * view_config_types_count); for (uint32_t i = 0; i < this->xr.view_configuration_views_count; ++i) { this->xr.a_view_configuration_views[i].type = XR_TYPE_VIEW_CONFIGURATION_VIEW; this->xr.a_view_configuration_views[i].next = NULL; } if (xrEnumerateViewConfigurations(this->xr.instance, this->xr.system_id, view_config_types_count, &view_config_types_count, a_view_config_types) != XR_SUCCESS) { wlr_log( WLR_ERROR, "Failed to enumerate OpenXR view configurations"); return false; } } { bool found = false; for (size_t i = 0; i < view_config_types_count; i++) { if (a_view_config_types[i] == XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO) { found = true; break; } } if (!found) { wlr_log(WLR_ERROR, "XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO not present"); return false; } } { if (xrEnumerateViewConfigurationViews(this->xr.instance, this->xr.system_id, XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, 0, &this->xr.view_configuration_views_count, nullptr) != XR_SUCCESS) { wlr_log(WLR_ERROR, "Failed to get amount of OpenXR view configuration views"); return false; } this->xr.a_view_configuration_views = malloc(sizeof(*this->xr.a_view_configuration_views) * this->xr.view_configuration_views_count); for (uint32_t i = 0; i < this->xr.view_configuration_views_count; ++i) { this->xr.a_view_configuration_views[i].type = XR_TYPE_VIEW_CONFIGURATION_VIEW; this->xr.a_view_configuration_views[i].next = NULL; } if (xrEnumerateViewConfigurationViews(this->xr.instance, this->xr.system_id, XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, this->xr.view_configuration_views_count, &this->xr.view_configuration_views_count, this->xr.a_view_configuration_views) != XR_SUCCESS) { wlr_log(WLR_ERROR, "Failed to enumerate OpenXR view configuration views"); return false; } } int64_t *a_swapchain_formats; uint32_t a_swapchain_formats_count = 0; { if (xrEnumerateSwapchainFormats( this->xr.session, 0, &a_swapchain_formats_count, nullptr) != XR_SUCCESS) { wlr_log( WLR_ERROR, "Failed to get amount of OpenXR swapchain formats"); return false; } a_swapchain_formats = malloc(sizeof(*a_swapchain_formats) * a_swapchain_formats_count); if (xrEnumerateSwapchainFormats(this->xr.session, a_swapchain_formats_count, &a_swapchain_formats_count, a_swapchain_formats) != XR_SUCCESS) { wlr_log(WLR_ERROR, "Failed to enumerate OpenXR swapchain formats"); return false; } } { bool found = false; for (size_t i = 0; i < a_swapchain_formats_count; i++) { if (a_swapchain_formats[i] == GL_DEPTH_COMPONENT16) { found = true; break; } } if (!found) { wlr_log( WLR_ERROR, "Failed to find satisfying depth swapchain format"); return false; } } auto const swapchain_format_depth = GL_DEPTH_COMPONENT24; auto const swapchain_format_color = GL_RGBA8; { bool found = false; for (size_t i = 0; i < a_swapchain_formats_count; i++) { if (a_swapchain_formats[i] == swapchain_format_color) { found = true; break; } } if (!found) { wlr_log( WLR_ERROR, "Failed to find satisfying color swapchain format"); return false; } } uint32_t const view_count = this->xr.view_configuration_views_count; uint32_t const eye_w = this->xr.a_view_configuration_views[0].recommendedImageRectWidth; uint32_t const eye_h = this->xr.a_view_configuration_views[0].recommendedImageRectHeight; uint32_t const buf_w = eye_w * view_count; if (!this->xr.swapchains.v_color) { this->xr.swapchains.v_color = vector_create(); } if (!this->xr.swapchains.v_depth) { this->xr.swapchains.v_depth = vector_create(); } vector_add(&this->xr.swapchains.v_color, (LunarWM_SwapchainInfo) {}); vector_add(&this->xr.swapchains.v_depth, (LunarWM_SwapchainInfo) {}); { auto *color_sc = &this->xr.swapchains.v_color[0]; auto *depth_sc = &this->xr.swapchains.v_depth[0]; auto *vcv = &this->xr.a_view_configuration_views[0]; { // Create color swapchain XrSwapchainCreateInfo const ci = { .type = XR_TYPE_SWAPCHAIN_CREATE_INFO, .next = nullptr, .createFlags = 0, .usageFlags = XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT, .format = swapchain_format_color, .sampleCount = vcv->recommendedSwapchainSampleCount, .width = buf_w, .height = eye_h, .faceCount = 1, .arraySize = 1, .mipCount = 1, }; color_sc->swapchain_format = ci.format; if (xrCreateSwapchain(this->xr.session, &ci, &color_sc->swapchain) != XR_SUCCESS) { wlr_log(WLR_ERROR, "Failed to create OpenXR color swapchain"); return false; } } { // Create depth swapchain XrSwapchainCreateInfo const ci = { .type = XR_TYPE_SWAPCHAIN_CREATE_INFO, .next = nullptr, .createFlags = 0, .usageFlags = XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, .format = swapchain_format_depth, .sampleCount = vcv->recommendedSwapchainSampleCount, .width = buf_w, .height = eye_h, .faceCount = 1, .arraySize = 1, .mipCount = 1, }; depth_sc->swapchain_format = ci.format; if (xrCreateSwapchain(this->xr.session, &ci, &depth_sc->swapchain) != XR_SUCCESS) { wlr_log(WLR_ERROR, "Failed to create OpenXR depth swapchain"); return false; } } // Enumerate swapchain images { // Color this->xr.swapchain_images[0].handle = color_sc->swapchain; if (xrEnumerateSwapchainImages(color_sc->swapchain, 0, &this->xr.swapchain_images[0].a_imgs_count, nullptr) != XR_SUCCESS) { wlr_log( WLR_ERROR, "Failed to get Color Swapchain Images count."); return false; } this->xr.swapchain_images[0].a_imgs = malloc(sizeof(*this->xr.swapchain_images[0].a_imgs) * this->xr.swapchain_images[0].a_imgs_count); for (uint32_t i = 0; i < this->xr.swapchain_images[0].a_imgs_count; ++i) { this->xr.swapchain_images[0].a_imgs[i].type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR; this->xr.swapchain_images[0].a_imgs[i].next = NULL; } if (xrEnumerateSwapchainImages(color_sc->swapchain, this->xr.swapchain_images[0].a_imgs_count, &this->xr.swapchain_images[0].a_imgs_count, (XrSwapchainImageBaseHeader *)this->xr.swapchain_images[0] .a_imgs) != XR_SUCCESS) { wlr_log( WLR_ERROR, "Failed to enumerate color swapchain images."); return false; } } { // Depth this->xr.swapchain_images[1].handle = depth_sc->swapchain; if (xrEnumerateSwapchainImages(depth_sc->swapchain, 0, &this->xr.swapchain_images[1].a_imgs_count, nullptr) != XR_SUCCESS) { wlr_log( WLR_ERROR, "Failed to get depth Swapchain Images count."); return false; } this->xr.swapchain_images[1].a_imgs = malloc(sizeof(*this->xr.swapchain_images[1].a_imgs) * this->xr.swapchain_images[1].a_imgs_count); for (uint32_t i = 0; i < this->xr.swapchain_images[1].a_imgs_count; ++i) { this->xr.swapchain_images[1].a_imgs[i].type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR; this->xr.swapchain_images[1].a_imgs[i].next = NULL; } if (xrEnumerateSwapchainImages(depth_sc->swapchain, this->xr.swapchain_images[1].a_imgs_count, &this->xr.swapchain_images[1].a_imgs_count, (XrSwapchainImageBaseHeader *)this->xr.swapchain_images[1] .a_imgs) != XR_SUCCESS) { wlr_log( WLR_ERROR, "Failed to enumerate depth swapchain images."); return false; } } // Get image views for (uint32_t j = 0; j < this->xr.swapchain_images[0].a_imgs_count; j++) { GLuint framebuffer = 0; glGenFramebuffers(1, &framebuffer); GLenum const attachment = GL_COLOR_ATTACHMENT0; glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment, GL_TEXTURE_2D, LunarWM_xr_get_swapchain_image(this, 0, j), 0); GLenum const result = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER); if (result != GL_FRAMEBUFFER_COMPLETE) { wlr_log(WLR_ERROR, "Failed to create color framebuffer"); return false; } glBindFramebuffer(GL_FRAMEBUFFER, 0); if (!color_sc->v_image_views) { color_sc->v_image_views = vector_create(); } vector_add(&color_sc->v_image_views, framebuffer); } for (uint32_t j = 0; j < this->xr.swapchain_images[1].a_imgs_count; j++) { GLuint framebuffer = 0; glGenFramebuffers(1, &framebuffer); GLenum const attachment = GL_DEPTH_ATTACHMENT; glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment, GL_TEXTURE_2D, LunarWM_xr_get_swapchain_image(this, 1, j), 0); GLenum const result = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER); if (result != GL_FRAMEBUFFER_COMPLETE) { wlr_log(WLR_ERROR, "Failed to create depth framebuffer"); return false; } glBindFramebuffer(GL_FRAMEBUFFER, 0); if (!depth_sc->v_image_views) { depth_sc->v_image_views = vector_create(); } vector_add(&depth_sc->v_image_views, framebuffer); } } assert(this->xr.instance); assert(this->xr.system_id); wlr_log(WLR_DEBUG, "Fetching blend modes..."); XrEnvironmentBlendMode *a_environment_blend_modes; uint32_t a_environment_blend_modes_count = 0; { // Get available blend modes if (xrEnumerateEnvironmentBlendModes(this->xr.instance, this->xr.system_id, XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, 0, &a_environment_blend_modes_count, nullptr) != XR_SUCCESS) { wlr_log( WLR_ERROR, "Failed to get OpenXR environment blend mode count"); return false; } a_environment_blend_modes = malloc(sizeof(*a_environment_blend_modes) * a_environment_blend_modes_count); if (xrEnumerateEnvironmentBlendModes(this->xr.instance, this->xr.system_id, XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, a_environment_blend_modes_count, &a_environment_blend_modes_count, a_environment_blend_modes) != XR_SUCCESS) { wlr_log(WLR_ERROR, "Failed to get XR environment blend modes"); return false; } } XrEnvironmentBlendMode const requested_environment_blend_modes[] = { XR_ENVIRONMENT_BLEND_MODE_OPAQUE, XR_ENVIRONMENT_BLEND_MODE_ADDITIVE, XR_ENVIRONMENT_BLEND_MODE_MAX_ENUM, }; for (size_t i = 0; i < ARRAY_SZ(requested_environment_blend_modes); i++) { this->xr.environment_blend_mode = requested_environment_blend_modes[i]; bool found = false; for (size_t j = 0; j < a_environment_blend_modes_count; j++) { if (requested_environment_blend_modes[i] == a_environment_blend_modes[j]) { found = true; break; } } if (found) { break; } } if (this->xr.environment_blend_mode == XR_ENVIRONMENT_BLEND_MODE_MAX_ENUM) { wlr_log(WLR_INFO, "Failed to find a compatible blend mode. Defaulting to " "XR_ENVIRONMENT_BLEND_MODE_OPAQUE."); this->xr.environment_blend_mode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE; } wlr_log(WLR_DEBUG, "Getting reference space..."); { // Reference space XrReferenceSpaceCreateInfo const ci = { .type = XR_TYPE_REFERENCE_SPACE_CREATE_INFO, .next = nullptr, .referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL, .poseInReferenceSpace = { .orientation = { 0.0f, 0.0f, 0.0f, 1.0f }, .position = { 0.0f, 0.0f, 0.0f } }, }; if (xrCreateReferenceSpace(this->xr.session, &ci, &this->xr.local_space) != XR_SUCCESS) { wlr_log(WLR_ERROR, "Failed to create OpenXR reference space"); return false; } } { // View reference space XrReferenceSpaceCreateInfo const ci = { .type = XR_TYPE_REFERENCE_SPACE_CREATE_INFO, .next = nullptr, .referenceSpaceType = XR_REFERENCE_SPACE_TYPE_VIEW, .poseInReferenceSpace = { .orientation = { 0.0f, 0.0f, 0.0f, 1.0f }, .position = { 0.0f, 0.0f, 0.0f } }, }; if (xrCreateReferenceSpace(this->xr.session, &ci, &this->xr.view_space) != XR_SUCCESS) { wlr_log(WLR_ERROR, "Failed to create OpenXR reference space"); return false; } } if (this->xr.hand_tracking_enabled && this->xr.CreateHandTrackerEXT) { bool hand_trackers_created = true; for (size_t i = 0; i < 2; i++) { auto *hand = &this->xr.hands[i]; XrHandTrackerCreateInfoEXT const ci = { .type = XR_TYPE_HAND_TRACKER_CREATE_INFO_EXT, .hand = i == 0 ? XR_HAND_LEFT_EXT : XR_HAND_RIGHT_EXT, .handJointSet = XR_HAND_JOINT_SET_DEFAULT_EXT, }; res = this->xr.CreateHandTrackerEXT( this->xr.session, &ci, &hand->hand_tracker); if (res != XR_SUCCESS) { wlr_log(WLR_ERROR, "Failed to create hand tracker: %d", res); hand_trackers_created = false; break; } } if (!hand_trackers_created) { if (this->xr.DestroyHandTrackerEXT) { for (size_t i = 0; i < 2; i++) { auto *hand = &this->xr.hands[i]; if (hand->hand_tracker != XR_NULL_HANDLE) { this->xr.DestroyHandTrackerEXT(hand->hand_tracker); hand->hand_tracker = XR_NULL_HANDLE; } } } this->xr.hand_tracking_enabled = false; } } if (!this->xr.hand_tracking_enabled) { for (size_t i = 0; i < 2; i++) { this->xr.hands[i].hand_tracker = XR_NULL_HANDLE; } this->xr.hand_tracking_system_properties.supportsHandTracking = XR_FALSE; } free(extension_properties); free(a_view_config_types); free(a_swapchain_formats); free(a_environment_blend_modes); vector_free(v_active_instance_extensions); wlr_log(WLR_INFO, "OpenXR initialized."); return true; } static void free_swapchain_info(LunarWM_SwapchainInfo *sc) { if (!sc) return; if (sc->v_image_views) { for (size_t i = 0; i < vector_size(sc->v_image_views); ++i) { GLuint fb = sc->v_image_views[i]; if (fb) glDeleteFramebuffers(1, &fb); } vector_free(sc->v_image_views); sc->v_image_views = NULL; } if (sc->swapchain) { xrDestroySwapchain(sc->swapchain); sc->swapchain = XR_NULL_HANDLE; } } void LunarWM_xr_cleanup(LunarWM *this) { for (int i = 0; i < 2; ++i) { if (this->xr.swapchain_images[i].a_imgs) { free(this->xr.swapchain_images[i].a_imgs); this->xr.swapchain_images[i].a_imgs = NULL; this->xr.swapchain_images[i].a_imgs_count = 0; } } if (this->xr.swapchains.v_color) { for (size_t i = 0; i < vector_size(this->xr.swapchains.v_color); ++i) free_swapchain_info(&this->xr.swapchains.v_color[i]); vector_free(this->xr.swapchains.v_color); this->xr.swapchains.v_color = NULL; } if (this->xr.swapchains.v_depth) { for (size_t i = 0; i < vector_size(this->xr.swapchains.v_depth); ++i) free_swapchain_info(&this->xr.swapchains.v_depth[i]); vector_free(this->xr.swapchains.v_depth); this->xr.swapchains.v_depth = NULL; } if (this->renderer.fbo) { glDeleteFramebuffers(1, &this->renderer.fbo); this->renderer.fbo = 0; } this->renderer.tmp_rt = (RenderTexture2D) { 0 }; if (this->xr.view_space) xrDestroySpace(this->xr.view_space), this->xr.view_space = XR_NULL_HANDLE; if (this->xr.local_space) xrDestroySpace(this->xr.local_space), this->xr.local_space = XR_NULL_HANDLE; for (size_t i = 0; i < 2; ++i) { if (this->xr.hands[i].hand_tracker && this->xr.DestroyHandTrackerEXT) this->xr.DestroyHandTrackerEXT(this->xr.hands[i].hand_tracker); this->xr.hands[i].hand_tracker = XR_NULL_HANDLE; } if (this->xr.a_view_configuration_views) { free(this->xr.a_view_configuration_views); this->xr.a_view_configuration_views = NULL; this->xr.view_configuration_views_count = 0; } if (this->xr.session != XR_NULL_HANDLE) { xrDestroySession(this->xr.session); this->xr.session = XR_NULL_HANDLE; this->xr.session_running = false; } if (this->xr.instance) xrDestroyInstance(this->xr.instance), this->xr.instance = XR_NULL_HANDLE; } void LunarWM_xr_poll_events(LunarWM *this) { XrEventDataBuffer event_data = { .type = XR_TYPE_EVENT_DATA_BUFFER, }; while (xrPollEvent(this->xr.instance, &event_data) == XR_SUCCESS) { switch (event_data.type) { case XR_TYPE_EVENT_DATA_EVENTS_LOST: { auto *el = (XrEventDataEventsLost *)&event_data; wlr_log(WLR_INFO, "OPENXR: Events Lost: %d", el->lostEventCount); break; } case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING: { auto *ilp = (XrEventDataInstanceLossPending *)&event_data; wlr_log(WLR_INFO, "OPENXR: Instance Loss Pending at: %ld", ilp->lossTime); this->xr.session_running = false; this->running = false; break; } case XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED: { auto *ipc = (XrEventDataInteractionProfileChanged *)&event_data; wlr_log(WLR_INFO, "OPENXR: Interaction Profile changed for Session: " "%p", ipc->session); if (ipc->session != this->xr.session) { wlr_log(WLR_ERROR, "XrEventDataInteractionProfileChanged for " "unknown Session"); break; } break; } case XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING: { auto *scp = (XrEventDataReferenceSpaceChangePending *)&event_data; wlr_log(WLR_INFO, "OPENXR: Reference Space Change pending for " "Session: %p", scp->session); if (scp->session != this->xr.session) { wlr_log(WLR_ERROR, "XrEventDataReferenceSpaceChangePending for " "unknown " "Session"); break; } break; } case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: { auto *sc = (XrEventDataSessionStateChanged *)&event_data; if (sc->session != this->xr.session) { wlr_log(WLR_ERROR, "XrEventDataSessionStateChanged for unknown " "Session"); break; } if (sc->state == XR_SESSION_STATE_READY) { XrSessionBeginInfo bi = { .type = XR_TYPE_SESSION_BEGIN_INFO }; bi.primaryViewConfigurationType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO; bi.primaryViewConfigurationType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO; if (xrBeginSession(this->xr.session, &bi) != XR_SUCCESS) { wlr_log(WLR_ERROR, "Failed to begin session"); } else { this->xr.session_running = true; } } if (sc->state == XR_SESSION_STATE_STOPPING) { if (xrEndSession(this->xr.session) != XR_SUCCESS) { wlr_log(WLR_ERROR, "Failed to end session"); } this->xr.session_running = false; } if (sc->state == XR_SESSION_STATE_EXITING) { this->xr.session_running = false; this->running = false; } if (sc->state == XR_SESSION_STATE_LOSS_PENDING) { this->xr.session_running = false; this->running = false; } this->xr.session_state = sc->state; break; } default: { break; } } } } bool LunarWM_xr_acquire_wait(XrSwapchain sc, uint32_t *idx) { XrSwapchainImageAcquireInfo ai = { .type = XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO, .next = NULL }; if (xrAcquireSwapchainImage(sc, &ai, idx) != XR_SUCCESS) { return false; } XrSwapchainImageWaitInfo wi = { .type = XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO, .next = NULL, .timeout = XR_INFINITE_DURATION }; return xrWaitSwapchainImage(sc, &wi) == XR_SUCCESS; }