Files
lunarwm/src/LunarWM_xr.c
Slendi f74937bb6a Initial commit
Signed-off-by: Slendi <slendi@socopon.com>
2025-10-01 20:29:18 +03:00

910 lines
27 KiB
C

#include "LunarWM_xr.h"
#include "common.h"
#include "vec.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
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;
}