From cecea144e411a0aabb7eda45a9798d2b65db6955 Mon Sep 17 00:00:00 2001 From: Slendi Date: Wed, 9 Jul 2025 04:29:40 +0300 Subject: [PATCH] Get OpenXR fully up and running Signed-off-by: Slendi --- src/LunarWM.cppm | 1511 ++++++++++++++++++++++++++++++++++++++-------- src/Math.cppm | 12 +- 2 files changed, 1281 insertions(+), 242 deletions(-) diff --git a/src/LunarWM.cppm b/src/LunarWM.cppm index f97ad05..44bff64 100644 --- a/src/LunarWM.cppm +++ b/src/LunarWM.cppm @@ -6,6 +6,7 @@ module; #include #include #include +#include #include #include @@ -16,20 +17,533 @@ module; extern "C" { #include #include +#include #include #include #include #include #include -#include } export module LunarWM.LunarWM; import std; +import LunarWM.Math; + +namespace std { +template <> struct formatter { + template constexpr auto parse(ParseContext &ctx) { + return ctx.begin(); + } + + static constexpr std::string_view to_string(XrResult r) { + switch (r) { + case XR_FRAME_DISCARDED: return "XR_FRAME_DISCARDED"; + case XR_ERROR_VALIDATION_FAILURE: + return "XR_ERROR_VALIDATION_FAILURE: The function usage was invalid in " + "some way."; + case XR_ERROR_RUNTIME_FAILURE: + return "XR_ERROR_RUNTIME_FAILURE: The runtime failed to handle the " + "function in an unexpected way that is not covered by another " + "error result."; + case XR_ERROR_OUT_OF_MEMORY: + return "XR_ERROR_OUT_OF_MEMORY: A memory allocation has failed."; + case XR_ERROR_API_VERSION_UNSUPPORTED: + return "XR_ERROR_API_VERSION_UNSUPPORTED: The runtime does not support " + "the requested API version."; + case XR_ERROR_INITIALIZATION_FAILED: + return "XR_ERROR_INITIALIZATION_FAILED: Initialization of object could " + "not be completed."; + case XR_ERROR_FUNCTION_UNSUPPORTED: + return "XR_ERROR_FUNCTION_UNSUPPORTED: The requested function was not " + "found or is otherwise unsupported."; + case XR_ERROR_FEATURE_UNSUPPORTED: + return "XR_ERROR_FEATURE_UNSUPPORTED: The requested feature is not " + "supported."; + case XR_ERROR_EXTENSION_NOT_PRESENT: + return "XR_ERROR_EXTENSION_NOT_PRESENT: A requested extension is not " + "supported."; + case XR_ERROR_LIMIT_REACHED: + return "XR_ERROR_LIMIT_REACHED: The runtime supports no more of the " + "requested resource."; + case XR_ERROR_SIZE_INSUFFICIENT: + return "XR_ERROR_SIZE_INSUFFICIENT: The supplied size was smaller than " + "required."; + case XR_ERROR_HANDLE_INVALID: + return "XR_ERROR_HANDLE_INVALID: A supplied object handle was invalid."; + case XR_ERROR_INSTANCE_LOST: + return "XR_ERROR_INSTANCE_LOST: The XrInstance was lost or could not be " + "found. It will need to be destroyed and optionally recreated."; + case XR_ERROR_SESSION_RUNNING: + return "XR_ERROR_SESSION_RUNNING: The session is already running."; + case XR_ERROR_SESSION_NOT_RUNNING: + return "XR_ERROR_SESSION_NOT_RUNNING: The session is not yet running."; + case XR_ERROR_SESSION_LOST: + return "XR_ERROR_SESSION_LOST: The XrSession was lost. It will need to " + "be destroyed and optionally recreated."; + case XR_ERROR_SYSTEM_INVALID: + return "XR_ERROR_SYSTEM_INVALID: The provided XrSystemId was invalid."; + case XR_ERROR_PATH_INVALID: + return "XR_ERROR_PATH_INVALID: The provided XrPath was not valid."; + case XR_ERROR_PATH_COUNT_EXCEEDED: + return "XR_ERROR_PATH_COUNT_EXCEEDED: The maximum number of supported " + "semantic paths has been reached."; + case XR_ERROR_PATH_FORMAT_INVALID: + return "XR_ERROR_PATH_FORMAT_INVALID: The semantic path character format " + "is invalid."; + case XR_ERROR_PATH_UNSUPPORTED: + return "XR_ERROR_PATH_UNSUPPORTED: The semantic path is unsupported."; + case XR_ERROR_LAYER_INVALID: + return "XR_ERROR_LAYER_INVALID: The layer was NULL or otherwise invalid."; + case XR_ERROR_LAYER_LIMIT_EXCEEDED: + return "XR_ERROR_LAYER_LIMIT_EXCEEDED: The number of specified layers is " + "greater than the supported number."; + case XR_ERROR_SWAPCHAIN_RECT_INVALID: + return "XR_ERROR_SWAPCHAIN_RECT_INVALID: The image rect was negatively " + "sized or otherwise invalid."; + case XR_ERROR_SWAPCHAIN_FORMAT_UNSUPPORTED: + return "XR_ERROR_SWAPCHAIN_FORMAT_UNSUPPORTED: The image format is not " + "supported by the runtime or platform."; + case XR_ERROR_ACTION_TYPE_MISMATCH: + return "XR_ERROR_ACTION_TYPE_MISMATCH: The API used to retrieve an " + "action’s state does not match the action’s type."; + case XR_ERROR_SESSION_NOT_READY: + return "XR_ERROR_SESSION_NOT_READY: The session is not in the ready " + "state."; + case XR_ERROR_SESSION_NOT_STOPPING: + return "XR_ERROR_SESSION_NOT_STOPPING: The session is not in the " + "stopping state."; + case XR_ERROR_TIME_INVALID: + return "XR_ERROR_TIME_INVALID: The provided XrTime was zero, negative, " + "or out of range."; + case XR_ERROR_REFERENCE_SPACE_UNSUPPORTED: + return "XR_ERROR_REFERENCE_SPACE_UNSUPPORTED: The specified reference " + "space is not supported by the runtime or system."; + case XR_ERROR_FILE_ACCESS_ERROR: + return "XR_ERROR_FILE_ACCESS_ERROR: The file could not be accessed."; + case XR_ERROR_FILE_CONTENTS_INVALID: + return "XR_ERROR_FILE_CONTENTS_INVALID: The file’s contents were " + "invalid."; + case XR_ERROR_FORM_FACTOR_UNSUPPORTED: + return "XR_ERROR_FORM_FACTOR_UNSUPPORTED: The specified form factor is " + "not supported by the current runtime or platform."; + case XR_ERROR_FORM_FACTOR_UNAVAILABLE: + return "XR_ERROR_FORM_FACTOR_UNAVAILABLE: The specified form factor is " + "supported, but the device is currently not available, e.g. not " + "plugged in or powered off."; + case XR_ERROR_API_LAYER_NOT_PRESENT: + return "XR_ERROR_API_LAYER_NOT_PRESENT: A requested API layer is not " + "present or could not be loaded."; + case XR_ERROR_CALL_ORDER_INVALID: + return "XR_ERROR_CALL_ORDER_INVALID: The call was made without having " + "made a previously required call."; + case XR_ERROR_GRAPHICS_DEVICE_INVALID: + return "XR_ERROR_GRAPHICS_DEVICE_INVALID: The given graphics device is " + "not in a valid state. The graphics device could be lost or " + "initialized without meeting graphics requirements."; + case XR_ERROR_POSE_INVALID: + return "XR_ERROR_POSE_INVALID: The supplied pose was invalid with " + "respect to the requirements."; + case XR_ERROR_INDEX_OUT_OF_RANGE: + return "XR_ERROR_INDEX_OUT_OF_RANGE: The supplied index was outside the " + "range of valid indices."; + case XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED: + return "XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED: The specified view " + "configuration type is not supported by the runtime or platform."; + case XR_ERROR_ENVIRONMENT_BLEND_MODE_UNSUPPORTED: + return "XR_ERROR_ENVIRONMENT_BLEND_MODE_UNSUPPORTED: The specified " + "environment blend mode is not supported by the runtime or " + "platform."; + case XR_ERROR_NAME_DUPLICATED: + return "XR_ERROR_NAME_DUPLICATED: The name provided was a duplicate of " + "an already-existing resource."; + case XR_ERROR_NAME_INVALID: + return "XR_ERROR_NAME_INVALID: The name provided was invalid."; + case XR_ERROR_ACTIONSET_NOT_ATTACHED: + return "XR_ERROR_ACTIONSET_NOT_ATTACHED: A referenced action set is not " + "attached to the session."; + case XR_ERROR_ACTIONSETS_ALREADY_ATTACHED: + return "XR_ERROR_ACTIONSETS_ALREADY_ATTACHED: The session already has " + "attached action sets."; + case XR_ERROR_LOCALIZED_NAME_DUPLICATED: + return "XR_ERROR_LOCALIZED_NAME_DUPLICATED: The localized name provided " + "was a duplicate of an already-existing resource."; + case XR_ERROR_LOCALIZED_NAME_INVALID: + return "XR_ERROR_LOCALIZED_NAME_INVALID: The localized name provided was " + "invalid."; + case XR_ERROR_GRAPHICS_REQUIREMENTS_CALL_MISSING: + return "XR_ERROR_GRAPHICS_REQUIREMENTS_CALL_MISSING: The " + "xrGetGraphicsRequirements* call was not made before calling " + "xrCreateSession."; + case XR_ERROR_RUNTIME_UNAVAILABLE: + return "XR_ERROR_RUNTIME_UNAVAILABLE: The loader was unable to find or " + "load a runtime."; + case XR_ERROR_EXTENSION_DEPENDENCY_NOT_ENABLED: + return "XR_ERROR_EXTENSION_DEPENDENCY_NOT_ENABLED: One or more of the " + "extensions being enabled has dependency on extensions that are " + "not enabled."; + case XR_ERROR_PERMISSION_INSUFFICIENT: + return "XR_ERROR_PERMISSION_INSUFFICIENT: Insufficient permissions. This " + "error is included for use by vendor extensions. The precise " + "definition of XR_ERROR_PERMISSION_INSUFFICIENT and actions " + "possible by the developer or user to resolve it can vary by " + "platform, extension or function. The developer should refer to " + "the documentation of the function that returned the error code " + "and extension it was defined."; + case XR_ERROR_ANDROID_THREAD_SETTINGS_ID_INVALID_KHR: + return "XR_ERROR_ANDROID_THREAD_SETTINGS_ID_INVALID_KHR: " + "xrSetAndroidApplicationThreadKHR failed as thread id is invalid. " + "(Added by the XR_KHR_android_thread_settings extension)"; + case XR_ERROR_ANDROID_THREAD_SETTINGS_FAILURE_KHR: + return "XR_ERROR_ANDROID_THREAD_SETTINGS_FAILURE_KHR: " + "xrSetAndroidApplicationThreadKHR failed setting the thread " + "attributes/priority. (Added by the " + "XR_KHR_android_thread_settings extension)"; + case XR_ERROR_CREATE_SPATIAL_ANCHOR_FAILED_MSFT: + return "XR_ERROR_CREATE_SPATIAL_ANCHOR_FAILED_MSFT: Spatial anchor could " + "not be created at that location. (Added by the " + "XR_MSFT_spatial_anchor extension)"; + case XR_ERROR_SECONDARY_VIEW_CONFIGURATION_TYPE_NOT_ENABLED_MSFT: + return "XR_ERROR_SECONDARY_VIEW_CONFIGURATION_TYPE_NOT_ENABLED_MSFT: The " + "secondary view configuration was not enabled when creating the " + "session. (Added by the XR_MSFT_secondary_view_configuration " + "extension)"; + case XR_ERROR_CONTROLLER_MODEL_KEY_INVALID_MSFT: + return "XR_ERROR_CONTROLLER_MODEL_KEY_INVALID_MSFT: The controller model " + "key is invalid. (Added by the XR_MSFT_controller_model " + "extension)"; + case XR_ERROR_REPROJECTION_MODE_UNSUPPORTED_MSFT: + return "XR_ERROR_REPROJECTION_MODE_UNSUPPORTED_MSFT: The reprojection " + "mode is not supported. (Added by the " + "XR_MSFT_composition_layer_reprojection extension)"; + case XR_ERROR_COMPUTE_NEW_SCENE_NOT_COMPLETED_MSFT: + return "XR_ERROR_COMPUTE_NEW_SCENE_NOT_COMPLETED_MSFT: Compute new scene " + "not completed. (Added by the XR_MSFT_scene_understanding " + "extension)"; + case XR_ERROR_SCENE_COMPONENT_ID_INVALID_MSFT: + return "XR_ERROR_SCENE_COMPONENT_ID_INVALID_MSFT: Scene component id " + "invalid. (Added by the XR_MSFT_scene_understanding extension)"; + case XR_ERROR_SCENE_COMPONENT_TYPE_MISMATCH_MSFT: + return "XR_ERROR_SCENE_COMPONENT_TYPE_MISMATCH_MSFT: Scene component " + "type mismatch. (Added by the XR_MSFT_scene_understanding " + "extension)"; + case XR_ERROR_SCENE_MESH_BUFFER_ID_INVALID_MSFT: + return "XR_ERROR_SCENE_MESH_BUFFER_ID_INVALID_MSFT: Scene mesh buffer id " + "invalid. (Added by the XR_MSFT_scene_understanding extension)"; + case XR_ERROR_SCENE_COMPUTE_FEATURE_INCOMPATIBLE_MSFT: + return "XR_ERROR_SCENE_COMPUTE_FEATURE_INCOMPATIBLE_MSFT: Scene compute " + "feature incompatible. (Added by the XR_MSFT_scene_understanding " + "extension)"; + case XR_ERROR_SCENE_COMPUTE_CONSISTENCY_MISMATCH_MSFT: + return "XR_ERROR_SCENE_COMPUTE_CONSISTENCY_MISMATCH_MSFT: Scene compute " + "consistency mismatch. (Added by the XR_MSFT_scene_understanding " + "extension)"; + case XR_ERROR_DISPLAY_REFRESH_RATE_UNSUPPORTED_FB: + return "XR_ERROR_DISPLAY_REFRESH_RATE_UNSUPPORTED_FB: The display " + "refresh rate is not supported by the platform. (Added by the " + "XR_FB_display_refresh_rate extension)"; + case XR_ERROR_COLOR_SPACE_UNSUPPORTED_FB: + return "XR_ERROR_COLOR_SPACE_UNSUPPORTED_FB: The color space is not " + "supported by the runtime. (Added by the XR_FB_color_space " + "extension)"; + case XR_ERROR_SPACE_COMPONENT_NOT_SUPPORTED_FB: + return "XR_ERROR_SPACE_COMPONENT_NOT_SUPPORTED_FB: The component type is " + "not supported for this space. (Added by the XR_FB_spatial_entity " + "extension)"; + case XR_ERROR_SPACE_COMPONENT_NOT_ENABLED_FB: + return "XR_ERROR_SPACE_COMPONENT_NOT_ENABLED_FB: The required component " + "is not enabled for this space. (Added by the " + "XR_FB_spatial_entity extension)"; + case XR_ERROR_SPACE_COMPONENT_STATUS_PENDING_FB: + return "XR_ERROR_SPACE_COMPONENT_STATUS_PENDING_FB: A request to set the " + "component’s status is currently pending. (Added by the " + "XR_FB_spatial_entity extension)"; + case XR_ERROR_SPACE_COMPONENT_STATUS_ALREADY_SET_FB: + return "XR_ERROR_SPACE_COMPONENT_STATUS_ALREADY_SET_FB: The component is " + "already set to the requested value. (Added by the " + "XR_FB_spatial_entity extension)"; + case XR_ERROR_UNEXPECTED_STATE_PASSTHROUGH_FB: + return "XR_ERROR_UNEXPECTED_STATE_PASSTHROUGH_FB: The object state is " + "unexpected for the issued command. (Added by the " + "XR_FB_passthrough extension)"; + case XR_ERROR_FEATURE_ALREADY_CREATED_PASSTHROUGH_FB: + return "XR_ERROR_FEATURE_ALREADY_CREATED_PASSTHROUGH_FB: Trying to " + "create an MR feature when one was already created and only one " + "instance is allowed. (Added by the XR_FB_passthrough extension)"; + case XR_ERROR_FEATURE_REQUIRED_PASSTHROUGH_FB: + return "XR_ERROR_FEATURE_REQUIRED_PASSTHROUGH_FB: Requested " + "functionality requires a feature to be created first. (Added by " + "the XR_FB_passthrough extension)"; + case XR_ERROR_NOT_PERMITTED_PASSTHROUGH_FB: + return "XR_ERROR_NOT_PERMITTED_PASSTHROUGH_FB: Requested functionality " + "is not permitted - application is not allowed to perform the " + "requested operation. (Added by the XR_FB_passthrough extension)"; + case XR_ERROR_INSUFFICIENT_RESOURCES_PASSTHROUGH_FB: + return "XR_ERROR_INSUFFICIENT_RESOURCES_PASSTHROUGH_FB: There were " + "insufficient resources available to perform an operation. (Added " + "by the XR_FB_passthrough extension)"; + case XR_ERROR_UNKNOWN_PASSTHROUGH_FB: + return "XR_ERROR_UNKNOWN_PASSTHROUGH_FB: Unknown Passthrough error (no " + "further details provided). (Added by the XR_FB_passthrough " + "extension)"; + case XR_ERROR_RENDER_MODEL_KEY_INVALID_FB: + return "XR_ERROR_RENDER_MODEL_KEY_INVALID_FB: The model key is invalid. " + "(Added by the XR_FB_render_model extension)"; + case XR_ERROR_MARKER_NOT_TRACKED_VARJO: + return "XR_ERROR_MARKER_NOT_TRACKED_VARJO: Marker tracking is disabled " + "or the specified marker is not currently tracked. (Added by the " + "XR_VARJO_marker_tracking extension)"; + case XR_ERROR_MARKER_ID_INVALID_VARJO: + return "XR_ERROR_MARKER_ID_INVALID_VARJO: The specified marker ID is not " + "valid. (Added by the XR_VARJO_marker_tracking extension)"; + case XR_ERROR_MARKER_DETECTOR_PERMISSION_DENIED_ML: + return "XR_ERROR_MARKER_DETECTOR_PERMISSION_DENIED_ML: The " + "com.magicleap.permission.MARKER_TRACKING permission was denied. " + "(Added by the XR_ML_marker_understanding extension)"; + case XR_ERROR_MARKER_DETECTOR_LOCATE_FAILED_ML: + return "XR_ERROR_MARKER_DETECTOR_LOCATE_FAILED_ML: The specified marker " + "could not be located spatially. (Added by the " + "XR_ML_marker_understanding extension)"; + case XR_ERROR_MARKER_DETECTOR_INVALID_DATA_QUERY_ML: + return "XR_ERROR_MARKER_DETECTOR_INVALID_DATA_QUERY_ML: The marker " + "queried does not contain data of the requested type. (Added by " + "the XR_ML_marker_understanding extension)"; + case XR_ERROR_MARKER_DETECTOR_INVALID_CREATE_INFO_ML: + return "XR_ERROR_MARKER_DETECTOR_INVALID_CREATE_INFO_ML: createInfo " + "contains mutually exclusive parameters, such as setting " + "XR_MARKER_DETECTOR_CORNER_REFINE_METHOD_APRIL_TAG_ML with " + "XR_MARKER_TYPE_ARUCO_ML. (Added by the " + "XR_ML_marker_understanding extension)"; + case XR_ERROR_MARKER_INVALID_ML: + return "XR_ERROR_MARKER_INVALID_ML: The marker id passed to the function " + "was invalid. (Added by the XR_ML_marker_understanding extension)"; + case XR_ERROR_LOCALIZATION_MAP_INCOMPATIBLE_ML: + return "XR_ERROR_LOCALIZATION_MAP_INCOMPATIBLE_ML: The localization map " + "being imported is not compatible with current OS or mode. (Added " + "by the XR_ML_localization_map extension)"; + case XR_ERROR_LOCALIZATION_MAP_UNAVAILABLE_ML: + return "XR_ERROR_LOCALIZATION_MAP_UNAVAILABLE_ML: The localization map " + "requested is not available. (Added by the XR_ML_localization_map " + "extension)"; + case XR_ERROR_LOCALIZATION_MAP_FAIL_ML: + return "XR_ERROR_LOCALIZATION_MAP_FAIL_ML: The map localization service " + "failed to fulfill the request, retry later. (Added by the " + "XR_ML_localization_map extension)"; + case XR_ERROR_LOCALIZATION_MAP_IMPORT_EXPORT_PERMISSION_DENIED_ML: + return "XR_ERROR_LOCALIZATION_MAP_IMPORT_EXPORT_PERMISSION_DENIED_ML: " + "The com.magicleap.permission.SPACE_IMPORT_EXPORT permission was " + "denied. (Added by the XR_ML_localization_map extension)"; + case XR_ERROR_LOCALIZATION_MAP_PERMISSION_DENIED_ML: + return "XR_ERROR_LOCALIZATION_MAP_PERMISSION_DENIED_ML: The " + "com.magicleap.permission.SPACE_MANAGER permission was denied. " + "(Added by the XR_ML_localization_map extension)"; + case XR_ERROR_LOCALIZATION_MAP_ALREADY_EXISTS_ML: + return "XR_ERROR_LOCALIZATION_MAP_ALREADY_EXISTS_ML: The map being " + "imported already exists in the system. (Added by the " + "XR_ML_localization_map extension)"; + case XR_ERROR_LOCALIZATION_MAP_CANNOT_EXPORT_CLOUD_MAP_ML: + return "XR_ERROR_LOCALIZATION_MAP_CANNOT_EXPORT_CLOUD_MAP_ML: The map " + "localization service cannot export cloud based maps. (Added by " + "the XR_ML_localization_map extension)"; + case XR_ERROR_SPATIAL_ANCHORS_PERMISSION_DENIED_ML: + return "XR_ERROR_SPATIAL_ANCHORS_PERMISSION_DENIED_ML: The " + "com.magicleap.permission.SPATIAL_ANCHOR permission was not " + "granted. (Added by the XR_ML_spatial_anchors extension)"; + case XR_ERROR_SPATIAL_ANCHORS_NOT_LOCALIZED_ML: + return "XR_ERROR_SPATIAL_ANCHORS_NOT_LOCALIZED_ML: Operation failed " + "because the system is not localized into a localization map. " + "(Added by the XR_ML_spatial_anchors extension)"; + case XR_ERROR_SPATIAL_ANCHORS_OUT_OF_MAP_BOUNDS_ML: + return "XR_ERROR_SPATIAL_ANCHORS_OUT_OF_MAP_BOUNDS_ML: Operation failed " + "because it is performed outside of the localization map. (Added " + "by the XR_ML_spatial_anchors extension)"; + case XR_ERROR_SPATIAL_ANCHORS_SPACE_NOT_LOCATABLE_ML: + return "XR_ERROR_SPATIAL_ANCHORS_SPACE_NOT_LOCATABLE_ML: Operation " + "failed because the space referenced cannot be located. (Added by " + "the XR_ML_spatial_anchors extension)"; + case XR_ERROR_SPATIAL_ANCHORS_ANCHOR_NOT_FOUND_ML: + return "XR_ERROR_SPATIAL_ANCHORS_ANCHOR_NOT_FOUND_ML: The anchor " + "references was not found. (Added by the " + "XR_ML_spatial_anchors_storage extension)"; + case XR_ERROR_SPATIAL_ANCHOR_NAME_NOT_FOUND_MSFT: + return "XR_ERROR_SPATIAL_ANCHOR_NAME_NOT_FOUND_MSFT: A spatial anchor " + "was not found associated with the spatial anchor name provided " + "(Added by the XR_MSFT_spatial_anchor_persistence extension)"; + case XR_ERROR_SPATIAL_ANCHOR_NAME_INVALID_MSFT: + return "XR_ERROR_SPATIAL_ANCHOR_NAME_INVALID_MSFT: The spatial anchor " + "name provided was not valid (Added by the " + "XR_MSFT_spatial_anchor_persistence extension)"; + case XR_ERROR_SPACE_MAPPING_INSUFFICIENT_FB: + return "XR_ERROR_SPACE_MAPPING_INSUFFICIENT_FB: Anchor import from cloud " + "or export from device failed. (Added by the " + "XR_FB_spatial_entity_sharing extension)"; + case XR_ERROR_SPACE_LOCALIZATION_FAILED_FB: + return "XR_ERROR_SPACE_LOCALIZATION_FAILED_FB: Anchors were downloaded " + "from the cloud but failed to be imported/aligned on the device. " + "(Added by the XR_FB_spatial_entity_sharing extension)"; + case XR_ERROR_SPACE_NETWORK_TIMEOUT_FB: + return "XR_ERROR_SPACE_NETWORK_TIMEOUT_FB: Timeout occurred while " + "waiting for network request to complete. (Added by the " + "XR_FB_spatial_entity_sharing extension)"; + case XR_ERROR_SPACE_NETWORK_REQUEST_FAILED_FB: + return "XR_ERROR_SPACE_NETWORK_REQUEST_FAILED_FB: The network request " + "failed. (Added by the XR_FB_spatial_entity_sharing extension)"; + case XR_ERROR_SPACE_CLOUD_STORAGE_DISABLED_FB: + return "XR_ERROR_SPACE_CLOUD_STORAGE_DISABLED_FB: Cloud storage is " + "required for this operation but is currently disabled. (Added by " + "the XR_FB_spatial_entity_sharing extension)"; + case XR_ERROR_PASSTHROUGH_COLOR_LUT_BUFFER_SIZE_MISMATCH_META: + return "XR_ERROR_PASSTHROUGH_COLOR_LUT_BUFFER_SIZE_MISMATCH_META: The " + "provided data buffer did not match the required size. (Added by " + "the XR_META_passthrough_color_lut extension)"; + case XR_ERROR_RENDER_MODEL_ID_INVALID_EXT: + return "XR_ERROR_RENDER_MODEL_ID_INVALID_EXT: The render model ID is " + "invalid. (Added by the XR_EXT_render_model extension)"; + case XR_ERROR_RENDER_MODEL_ASSET_UNAVAILABLE_EXT: + return "XR_ERROR_RENDER_MODEL_ASSET_UNAVAILABLE_EXT: The render model " + "asset is unavailable. (Added by the XR_EXT_render_model " + "extension)"; + case XR_ERROR_RENDER_MODEL_GLTF_EXTENSION_REQUIRED_EXT: + return "XR_ERROR_RENDER_MODEL_GLTF_EXTENSION_REQUIRED_EXT: A glTF " + "extension is required. (Added by the XR_EXT_render_model " + "extension)"; + case XR_ERROR_NOT_INTERACTION_RENDER_MODEL_EXT: + return "XR_ERROR_NOT_INTERACTION_RENDER_MODEL_EXT: The provided " + "XrRenderModelEXT was not created from a XrRenderModelIdEXT from " + "[XR_EXT_interaction_render_model] (Added by the " + "XR_EXT_interaction_render_model extension)"; + case XR_ERROR_HINT_ALREADY_SET_QCOM: + return "XR_ERROR_HINT_ALREADY_SET_QCOM: Tracking optimization hint is " + "already set for the domain. (Added by the " + "XR_QCOM_tracking_optimization_settings extension)"; + case XR_ERROR_NOT_AN_ANCHOR_HTC: + return "XR_ERROR_NOT_AN_ANCHOR_HTC: The provided space is valid but not " + "an anchor. (Added by the XR_HTC_anchor extension)"; + case XR_ERROR_SPATIAL_ENTITY_ID_INVALID_BD: + return "XR_ERROR_SPATIAL_ENTITY_ID_INVALID_BD: The spatial entity id is " + "invalid. (Added by the XR_BD_spatial_sensing extension)"; + case XR_ERROR_SPATIAL_SENSING_SERVICE_UNAVAILABLE_BD: + return "XR_ERROR_SPATIAL_SENSING_SERVICE_UNAVAILABLE_BD: The spatial " + "sensing service is unavailable. (Added by the " + "XR_BD_spatial_sensing extension)"; + case XR_ERROR_ANCHOR_NOT_SUPPORTED_FOR_ENTITY_BD: + return "XR_ERROR_ANCHOR_NOT_SUPPORTED_FOR_ENTITY_BD: The spatial entity " + "does not support anchor. (Added by the XR_BD_spatial_sensing " + "extension)"; + case XR_ERROR_SCENE_CAPTURE_FAILURE_BD: + return "XR_ERROR_SCENE_CAPTURE_FAILURE_BD: The scene capture is failed, " + "for example exiting abnormally. (Added by the " + "XR_BD_spatial_scene extension)"; + case XR_ERROR_SPACE_NOT_LOCATABLE_EXT: + return "XR_ERROR_SPACE_NOT_LOCATABLE_EXT: The space passed to the " + "function was not locatable. (Added by the XR_EXT_plane_detection " + "extension)"; + case XR_ERROR_PLANE_DETECTION_PERMISSION_DENIED_EXT: + return "XR_ERROR_PLANE_DETECTION_PERMISSION_DENIED_EXT: The permission " + "for this resource was not granted. (Added by the " + "XR_EXT_plane_detection extension)"; + case XR_ERROR_FUTURE_PENDING_EXT: + return "XR_ERROR_FUTURE_PENDING_EXT: Returned by completion function to " + "indicate future is not ready. (Added by the XR_EXT_future " + "extension)"; + case XR_ERROR_FUTURE_INVALID_EXT: + return "XR_ERROR_FUTURE_INVALID_EXT: Returned by completion function to " + "indicate future is not valid. (Added by the XR_EXT_future " + "extension)"; + case XR_ERROR_SYSTEM_NOTIFICATION_PERMISSION_DENIED_ML: + return "XR_ERROR_SYSTEM_NOTIFICATION_PERMISSION_DENIED_ML: The " + "com.magicleap.permission.SYSTEM_NOTIFICATION permission was not " + "granted. (Added by the XR_ML_system_notifications extension)"; + case XR_ERROR_SYSTEM_NOTIFICATION_INCOMPATIBLE_SKU_ML: + return "XR_ERROR_SYSTEM_NOTIFICATION_INCOMPATIBLE_SKU_ML: Incompatible " + "SKU detected. (Added by the XR_ML_system_notifications " + "extension)"; + case XR_ERROR_WORLD_MESH_DETECTOR_PERMISSION_DENIED_ML: + return "XR_ERROR_WORLD_MESH_DETECTOR_PERMISSION_DENIED_ML: The world " + "mesh detector permission was not granted. (Added by the " + "XR_ML_world_mesh_detection extension)"; + case XR_ERROR_WORLD_MESH_DETECTOR_SPACE_NOT_LOCATABLE_ML: + return "XR_ERROR_WORLD_MESH_DETECTOR_SPACE_NOT_LOCATABLE_ML: At the time " + "of the call the runtime was unable to locate the space and " + "cannot fulfill your request. (Added by the " + "XR_ML_world_mesh_detection extension)"; + case XR_ERROR_COLOCATION_DISCOVERY_NETWORK_FAILED_META: + return "XR_ERROR_COLOCATION_DISCOVERY_NETWORK_FAILED_META: The network " + "request failed. (Added by the XR_META_colocation_discovery " + "extension)"; + case XR_ERROR_COLOCATION_DISCOVERY_NO_DISCOVERY_METHOD_META: + return "XR_ERROR_COLOCATION_DISCOVERY_NO_DISCOVERY_METHOD_META: The " + "runtime does not have any methods available to perform " + "discovery. (Added by the XR_META_colocation_discovery extension)"; + case XR_ERROR_SPACE_GROUP_NOT_FOUND_META: + return "XR_ERROR_SPACE_GROUP_NOT_FOUND_META: The group UUID was not " + "found within the runtime (Added by the " + "XR_META_spatial_entity_group_sharing extension)"; + case XR_ERROR_SPATIAL_CAPABILITY_UNSUPPORTED_EXT: + return "XR_ERROR_SPATIAL_CAPABILITY_UNSUPPORTED_EXT: The specified " + "spatial capability is not supported by the runtime or the " + "system. (Added by the XR_EXT_spatial_entity extension)"; + case XR_ERROR_SPATIAL_ENTITY_ID_INVALID_EXT: + return "XR_ERROR_SPATIAL_ENTITY_ID_INVALID_EXT: The specified spatial " + "entity id is invalid or an entity with that id does not exist in " + "the environment. (Added by the XR_EXT_spatial_entity extension)"; + case XR_ERROR_SPATIAL_BUFFER_ID_INVALID_EXT: + return "XR_ERROR_SPATIAL_BUFFER_ID_INVALID_EXT: The specified spatial " + "buffer id is invalid or does not exist in the spatial snapshot " + "being used to query for the buffer data. (Added by the " + "XR_EXT_spatial_entity extension)"; + case XR_ERROR_SPATIAL_COMPONENT_UNSUPPORTED_FOR_CAPABILITY_EXT: + return "XR_ERROR_SPATIAL_COMPONENT_UNSUPPORTED_FOR_CAPABILITY_EXT: The " + "specified spatial component is not supported by the runtime or " + "the system for the given capability. (Added by the " + "XR_EXT_spatial_entity extension)"; + case XR_ERROR_SPATIAL_CAPABILITY_CONFIGURATION_INVALID_EXT: + return "XR_ERROR_SPATIAL_CAPABILITY_CONFIGURATION_INVALID_EXT: The " + "specified spatial capability configuration is invalid. (Added by " + "the XR_EXT_spatial_entity extension)"; + case XR_ERROR_SPATIAL_COMPONENT_NOT_ENABLED_EXT: + return "XR_ERROR_SPATIAL_COMPONENT_NOT_ENABLED_EXT: The specified " + "spatial component is not enabled for the spatial context. (Added " + "by the XR_EXT_spatial_entity extension)"; + case XR_ERROR_SPATIAL_PERSISTENCE_SCOPE_UNSUPPORTED_EXT: + return "XR_ERROR_SPATIAL_PERSISTENCE_SCOPE_UNSUPPORTED_EXT: The " + "specified spatial persistence scope is not supported by the " + "runtime or the system. (Added by the XR_EXT_spatial_persistence " + "extension)"; + case XR_ERROR_SPATIAL_PERSISTENCE_SCOPE_INCOMPATIBLE_EXT: + return "XR_ERROR_SPATIAL_PERSISTENCE_SCOPE_INCOMPATIBLE_EXT: THe scope " + "configured for the persistence context is incompatible for the " + "current spatial entity. (Added by the " + "XR_EXT_spatial_persistence_operations extension)"; + default: + return ""; + } + } + + template auto format(XrResult r, FmtCtx &ctx) const { + if (spec == 'd') + return format_to(ctx.out(), "{}", static_cast(r)); + + std::string_view str = this->to_string(r); + if (!str.empty()) + return format_to(ctx.out(), "{}", str); + + return format_to(ctx.out(), "<0x{:x}>", static_cast(r)); + } + +private: + char spec{'s'}; +}; +} // namespace std + namespace LunarWM { +enum class SwapchainType { + Color, + Depth, +}; + export struct LunarWM { LunarWM() = default; ~LunarWM(); @@ -40,10 +554,13 @@ export struct LunarWM { void terminate(); private: + struct RenderLayerInfo; + void init_wayland(); void init_xr(); - void poll_events_xr(); + void poll_events_xr(); + bool render_layer(RenderLayerInfo &info); bool m_initialized{}; @@ -70,12 +587,83 @@ private: } m_wayland; struct { + struct SwapchainInfo { + XrSwapchain swapchain{XR_NULL_HANDLE}; + std::int64_t swapchain_format{}; + std::vector image_views; + }; + std::optional instance; - std::optional system_id; - XrSession session = XR_NULL_HANDLE; - XrSessionState session_state = XR_SESSION_STATE_UNKNOWN; + std::optional system_id; + XrSession session{XR_NULL_HANDLE}; + XrSessionState session_state{XR_SESSION_STATE_UNKNOWN}; + struct { + std::vector color; + std::vector depth; + } swapchains; + + std::unordered_map< + XrSwapchain, + std::pair>> + swapchain_images_map; + + std::vector view_configuration_views; + XrEnvironmentBlendMode environment_blend_mode; + + XrSpace local_space{XR_NULL_HANDLE}; + + std::uint32_t get_swapchain_image(XrSwapchain swapchain, uint32_t index) { + return swapchain_images_map[swapchain].second[index].image; + } } m_xr; + struct { + GLuint vertex_array{}; + GLuint set_framebuffer{}; + + void begin_rendering() { + glGenVertexArrays(1, &vertex_array); + glBindVertexArray(vertex_array); + + glGenFramebuffers(1, &set_framebuffer); + glBindFramebuffer(GL_FRAMEBUFFER, set_framebuffer); + } + + void end_rendering() { + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glDeleteFramebuffers(1, &set_framebuffer); + set_framebuffer = 0; + + glBindVertexArray(0); + glDeleteVertexArrays(1, &vertex_array); + vertex_array = 0; + } + + void clear_color(GLuint image_view, float r, float g, float b, float a) { + glBindFramebuffer(GL_FRAMEBUFFER, (GLuint)(uint64_t)image_view); + glClearColor(r, g, b, a); + glClear(GL_COLOR_BUFFER_BIT); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + } + + void clear_depth(GLuint image_view, float d) { + glBindFramebuffer(GL_FRAMEBUFFER, (GLuint)(uint64_t)image_view); + glClearDepthf(d); + glClear(GL_DEPTH_BUFFER_BIT); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + } + } m_renderer; + + struct RenderLayerInfo { + XrTime predicted_display_time; + std::vector layers; + XrCompositionLayerProjection layer_projection{ + .type = XR_TYPE_COMPOSITION_LAYER_PROJECTION, + .next = nullptr, + }; + std::vector layer_projection_views; + }; + bool m_running{}; bool m_session_running{}; bool m_session_state{}; @@ -83,8 +671,14 @@ private: void LunarWM::init() { this->init_wayland(); + if (eglMakeCurrent(m_wayland.egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, + m_wayland.egl_context) == EGL_FALSE) { + throw std::runtime_error("Failed to eglMakeCurrent"); + } this->init_xr(); + wlr_log(WLR_INFO, "OpenGL ES version: %s", glGetString(GL_VERSION)); + m_initialized = true; } @@ -106,7 +700,7 @@ void LunarWM::init_wayland() { throw std::runtime_error("Failed to create wlroots backend"); } - setenv("WLR_RENDERER", "gles2", 1); + setenv("WLR_RENDERER", "gles2", 1); m_wayland.renderer = wlr_renderer_autocreate(m_wayland.backend); if (!m_wayland.renderer) { throw std::runtime_error("Failed to create wlroots renderer"); @@ -179,264 +773,657 @@ void LunarWM::init_wayland() { void LunarWM::init_xr() { XrApplicationInfo app_info{ - .applicationVersion = 1, - .engineVersion = 1, - .apiVersion = XR_CURRENT_API_VERSION, + .applicationVersion = 1, + .engineVersion = 1, + .apiVersion = XR_CURRENT_API_VERSION, }; - strncpy(app_info.applicationName, "LunarWM", XR_MAX_APPLICATION_NAME_SIZE); - strncpy(app_info.engineName, "LunarWM Engine", XR_MAX_ENGINE_NAME_SIZE); + strncpy(app_info.applicationName, "LunarWM", XR_MAX_APPLICATION_NAME_SIZE); + strncpy(app_info.engineName, "LunarWM Engine", XR_MAX_ENGINE_NAME_SIZE); - std::vector instance_extensions { - XR_EXT_DEBUG_UTILS_EXTENSION_NAME, - XR_MNDX_EGL_ENABLE_EXTENSION_NAME, - XR_KHR_OPENGL_ES_ENABLE_EXTENSION_NAME, + std::vector instance_extensions{ + XR_EXT_DEBUG_UTILS_EXTENSION_NAME, + XR_MNDX_EGL_ENABLE_EXTENSION_NAME, + XR_KHR_OPENGL_ES_ENABLE_EXTENSION_NAME, + }; + std::vector apiLayers; + std::vector active_instance_extensions; + std::vector activeAPILayers; + + uint32_t apiLayerCount = 0; + std::vector apiLayerProperties; + if (xrEnumerateApiLayerProperties(0, &apiLayerCount, nullptr) != XR_SUCCESS) { + throw std::runtime_error("Failed to enumerate API layer properties"); + } + apiLayerProperties.resize(apiLayerCount, {XR_TYPE_API_LAYER_PROPERTIES}); + if (xrEnumerateApiLayerProperties(apiLayerCount, &apiLayerCount, + apiLayerProperties.data()) != XR_SUCCESS) { + throw std::runtime_error("Failed to enumerate API layer properties"); + } + + for (auto &requestLayer : apiLayers) { + for (auto &layerProperty : apiLayerProperties) { + if (strcmp(requestLayer.c_str(), layerProperty.layerName) != 0) { + continue; + } else { + activeAPILayers.push_back(requestLayer.c_str()); + break; + } + } + } + + uint32_t extensionCount = 0; + std::vector extensionProperties; + if (xrEnumerateInstanceExtensionProperties(nullptr, 0, &extensionCount, + nullptr) != XR_SUCCESS) { + throw std::runtime_error( + "Failed to enumerate OpenXR instance extension properties"); + } + extensionProperties.resize(extensionCount, {XR_TYPE_EXTENSION_PROPERTIES}); + if (xrEnumerateInstanceExtensionProperties( + nullptr, extensionCount, &extensionCount, + extensionProperties.data()) != XR_SUCCESS) { + throw std::runtime_error( + "Failed to enumerate OpenXR instance extension properties"); + } + + for (auto &requestedInstanceExtension : instance_extensions) { + bool found = false; + for (auto &extensionProperty : extensionProperties) { + if (strcmp(requestedInstanceExtension.c_str(), + extensionProperty.extensionName) != 0) { + continue; + } else { + active_instance_extensions.push_back( + requestedInstanceExtension.c_str()); + found = true; + break; + } + } + if (!found) { + throw std::runtime_error( + std::format("Failed to find OpenXR instance extension: {}", + requestedInstanceExtension)); + } + } + + { + XrInstanceCreateInfo ci{ + .type = XR_TYPE_INSTANCE_CREATE_INFO, + .next = NULL, + .createFlags = 0, + .applicationInfo = app_info, + .enabledApiLayerCount = + static_cast(activeAPILayers.size()), + .enabledApiLayerNames = activeAPILayers.data(), + .enabledExtensionCount = + static_cast(active_instance_extensions.size()), + .enabledExtensionNames = active_instance_extensions.data(), + }; + + XrInstance instance; + if (xrCreateInstance(&ci, &instance) != XR_SUCCESS) { + throw std::runtime_error("Failed to create OpenXR instance"); + } + m_xr.instance = instance; + } + + { + XrSystemGetInfo gi{ + .type = XR_TYPE_SYSTEM_GET_INFO, + .next = nullptr, + }; + + XrFormFactor factors[]{ + XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY, + XR_FORM_FACTOR_HANDHELD_DISPLAY, + }; + for (auto const factor : factors) { + gi.formFactor = factor; + XrSystemId system_id; + if (xrGetSystem(*m_xr.instance, &gi, &system_id) == XR_SUCCESS) { + m_xr.system_id = system_id; + break; + } + } + + if (!m_xr.system_id) { + throw std::runtime_error("Failed to find valid form factor"); + } + } + + XrGraphicsRequirementsOpenGLESKHR reqs{ + .type = XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_ES_KHR, + .next = nullptr, + }; + PFN_xrGetOpenGLESGraphicsRequirementsKHR xrGetOpenGLESGraphicsRequirementsKHR; + xrGetInstanceProcAddr( + *m_xr.instance, "xrGetOpenGLESGraphicsRequirementsKHR", + (PFN_xrVoidFunction *)&xrGetOpenGLESGraphicsRequirementsKHR); + if (xrGetOpenGLESGraphicsRequirementsKHR(*m_xr.instance, *m_xr.system_id, + &reqs) != XR_SUCCESS) { + throw std::runtime_error("Failed to get GLES graphics requirements"); + } + printf("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)); + + 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 = m_wayland.egl_display, + .config = m_wayland.egl_config, + .context = m_wayland.egl_context, + }; + + XrSessionCreateInfo ci{ + .type = XR_TYPE_SESSION_CREATE_INFO, + .next = &gbind, + .createFlags = 0, + .systemId = *m_xr.system_id, + }; + + if (xrCreateSession(*m_xr.instance, &ci, &m_xr.session) != XR_SUCCESS) { + throw std::runtime_error("Failed to create OpenXR session"); + } + } + + // Swapchain time! + std::vector view_config_types; + { + std::uint32_t count; + if (xrEnumerateViewConfigurations(*m_xr.instance, *m_xr.system_id, 0, + &count, nullptr) != XR_SUCCESS) { + throw std::runtime_error( + "Failed to get amount of OpenXR view configurations"); + } + view_config_types.resize(count); + if (xrEnumerateViewConfigurations(*m_xr.instance, *m_xr.system_id, count, + &count, + view_config_types.data()) != XR_SUCCESS) { + throw std::runtime_error( + "Failed to enumerate OpenXR view configurations"); + } + } + + if (!std::ranges::contains(view_config_types, + XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO)) { + throw std::runtime_error( + "XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO not present"); + } + + { + uint32_t count = 0; + if (xrEnumerateViewConfigurationViews( + *m_xr.instance, *m_xr.system_id, + XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, 0, &count, + nullptr) != XR_SUCCESS) { + throw std::runtime_error( + "Failed to get amount of OpenXR view configuration views"); + } + m_xr.view_configuration_views.resize(count, {XR_TYPE_VIEW_CONFIGURATION_VIEW}); + if (xrEnumerateViewConfigurationViews( + *m_xr.instance, *m_xr.system_id, + XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, count, &count, + m_xr.view_configuration_views.data()) != XR_SUCCESS) { + throw std::runtime_error( + "Failed to enumerate OpenXR view configuration views"); + } + } + + std::vector swapchain_formats; + { + std::uint32_t count; + if (xrEnumerateSwapchainFormats(m_xr.session, 0, &count, nullptr) != + XR_SUCCESS) { + throw std::runtime_error( + "Failed to get amount of OpenXR swapchain formats"); + } + swapchain_formats.resize(count); + if (xrEnumerateSwapchainFormats(m_xr.session, count, &count, + swapchain_formats.data()) != XR_SUCCESS) { + throw std::runtime_error("Failed to enumerate OpenXR swapchain formats"); + } + } + + std::vector swapchain_format_depth_needles{ + GL_DEPTH_COMPONENT32F, + GL_DEPTH_COMPONENT24, + GL_DEPTH_COMPONENT16, + }; + const std::vector::const_iterator &swapchain_format_depth_it = + std::find_first_of(swapchain_formats.begin(), swapchain_formats.end(), + std::begin(swapchain_format_depth_needles), + std::end(swapchain_format_depth_needles)); + if (swapchain_format_depth_it == swapchain_formats.end()) { + throw std::runtime_error( + "Failed to find satisfying depth swapchain format"); + } + auto const swapchain_format_depth = *swapchain_format_depth_it; + + std::vector swapchain_format_color_needles{GL_RGBA8, + GL_RGBA8_SNORM}; + const std::vector::const_iterator &swapchain_format_color_it = + std::find_first_of(swapchain_formats.begin(), swapchain_formats.end(), + std::begin(swapchain_format_color_needles), + std::end(swapchain_format_color_needles)); + if (swapchain_format_color_it == swapchain_formats.end()) { + throw std::runtime_error( + "Failed to find satisfying color swapchain format"); + } + auto const swapchain_format_color = *swapchain_format_color_it; + + m_xr.swapchains.color.resize(m_xr.view_configuration_views.size()); + m_xr.swapchains.depth.resize(m_xr.view_configuration_views.size()); + + auto const AllocateSwapchainImageData = + [&](XrSwapchain swapchain, SwapchainType type, uint32_t count) { + m_xr.swapchain_images_map[swapchain].first = type; + m_xr.swapchain_images_map[swapchain].second.resize( + count, {XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR}); + return reinterpret_cast( + m_xr.swapchain_images_map[swapchain].second.data()); + }; + for (std::size_t i = 0; i < m_xr.view_configuration_views.size(); i++) { + auto &color_sc = m_xr.swapchains.color[i]; + auto &depth_sc = m_xr.swapchains.depth[i]; + auto &vcv = m_xr.view_configuration_views[i]; + + { // Create color swapchain + XrSwapchainCreateInfo ci{ + .type = XR_TYPE_SWAPCHAIN_CREATE_INFO, + .next = nullptr, + .createFlags = 0, + .usageFlags = XR_SWAPCHAIN_USAGE_SAMPLED_BIT | + XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT, + .format = swapchain_format_color, + .sampleCount = vcv.recommendedSwapchainSampleCount, + .width = vcv.recommendedImageRectWidth, + .height = vcv.recommendedImageRectHeight, + .faceCount = 1, + .arraySize = 1, + .mipCount = 1, + }; + color_sc.swapchain_format = ci.format; + + if (xrCreateSwapchain(m_xr.session, &ci, &color_sc.swapchain) != + XR_SUCCESS) { + throw std::runtime_error("Failed to create OpenXR color swapchain"); + } + } + + { // Create depth swapchain + XrSwapchainCreateInfo ci{ + .type = XR_TYPE_SWAPCHAIN_CREATE_INFO, + .next = nullptr, + .createFlags = 0, + .usageFlags = XR_SWAPCHAIN_USAGE_SAMPLED_BIT | + XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, + .format = swapchain_format_depth, + .sampleCount = vcv.recommendedSwapchainSampleCount, + .width = vcv.recommendedImageRectWidth, + .height = vcv.recommendedImageRectHeight, + .faceCount = 1, + .arraySize = 1, + .mipCount = 1, + }; + depth_sc.swapchain_format = ci.format; + + if (xrCreateSwapchain(m_xr.session, &ci, &depth_sc.swapchain) != + XR_SUCCESS) { + throw std::runtime_error("Failed to create OpenXR color swapchain"); + } + } + + XrResult res; + + // Enumerate swapchain images + uint32_t color_swapchain_image_count = 0; + if (xrEnumerateSwapchainImages(color_sc.swapchain, 0, + &color_swapchain_image_count, + nullptr) != XR_SUCCESS) { + throw std::runtime_error("Failed to get Color Swapchain Images count."); + } + XrSwapchainImageBaseHeader *color_swapchain_images = + AllocateSwapchainImageData(color_sc.swapchain, SwapchainType::Color, + color_swapchain_image_count); + if (xrEnumerateSwapchainImages(color_sc.swapchain, + color_swapchain_image_count, + &color_swapchain_image_count, + color_swapchain_images) != XR_SUCCESS) { + throw std::runtime_error("Failed to enumerate Color Swapchain Images."); + } + + uint32_t depth_swapchain_image_count = 0; + if ((res = xrEnumerateSwapchainImages(depth_sc.swapchain, 0, + &depth_swapchain_image_count, + nullptr)) != XR_SUCCESS) { + throw std::runtime_error( + std::format("Failed to get Depth Swapchain Images count. {}", res)); + } + XrSwapchainImageBaseHeader *depth_swapchain_images = + AllocateSwapchainImageData(depth_sc.swapchain, SwapchainType::Depth, + depth_swapchain_image_count); + if ((res = xrEnumerateSwapchainImages( + depth_sc.swapchain, depth_swapchain_image_count, + &depth_swapchain_image_count, depth_swapchain_images)) != + XR_SUCCESS) { + throw std::runtime_error("Failed to enumerate Depth Swapchain Images."); + } + + // Get image views + for (uint32_t j = 0; j < color_swapchain_image_count; j++) { + GLuint framebuffer = 0; + glGenFramebuffers(1, &framebuffer); + + GLenum attachment = GL_COLOR_ATTACHMENT0; + + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment, GL_TEXTURE_2D, + m_xr.get_swapchain_image(color_sc.swapchain, j), + 0); + + GLenum result = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER); + if (result != GL_FRAMEBUFFER_COMPLETE) { + throw std::runtime_error("Failed to create color framebuffer"); + } + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + color_sc.image_views.push_back(framebuffer); + } + for (uint32_t j = 0; j < depth_swapchain_image_count; j++) { + GLuint framebuffer = 0; + glGenFramebuffers(1, &framebuffer); + + GLenum attachment = GL_DEPTH_ATTACHMENT; + + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment, GL_TEXTURE_2D, + m_xr.get_swapchain_image(depth_sc.swapchain, j), + 0); + + GLenum result = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER); + if (result != GL_FRAMEBUFFER_COMPLETE) { + throw std::runtime_error("Failed to create depth framebuffer"); + } + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + depth_sc.image_views.push_back(framebuffer); + } + } + + std::vector environment_blend_modes; + { // Get available blend modes + std::uint32_t count{}; + if (xrEnumerateEnvironmentBlendModes( + *m_xr.instance, *m_xr.system_id, + XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, 0, &count, + nullptr) != XR_SUCCESS) { + throw std::runtime_error( + "Failed to get OpenXR environment blend mode count"); + } + environment_blend_modes.resize(count); + if (xrEnumerateEnvironmentBlendModes( + *m_xr.instance, *m_xr.system_id, + XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, count, &count, + environment_blend_modes.data()) != XR_SUCCESS) { + throw std::runtime_error("Failed to get XR environment blend modes"); + } + } + std::vector requested_environment_blend_modes { + XR_ENVIRONMENT_BLEND_MODE_OPAQUE, + XR_ENVIRONMENT_BLEND_MODE_ADDITIVE, + XR_ENVIRONMENT_BLEND_MODE_MAX_ENUM, }; - std::vector apiLayers; - std::vector active_instance_extensions; - std::vector activeAPILayers; - uint32_t apiLayerCount = 0; - std::vector apiLayerProperties; - if (xrEnumerateApiLayerProperties(0, &apiLayerCount, nullptr) != XR_SUCCESS) { - throw std::runtime_error("Failed to enumerate API layer properties"); - } - apiLayerProperties.resize(apiLayerCount, {XR_TYPE_API_LAYER_PROPERTIES}); - if (xrEnumerateApiLayerProperties(apiLayerCount, &apiLayerCount, apiLayerProperties.data()) != XR_SUCCESS) { - throw std::runtime_error("Failed to enumerate API layer properties"); - } + for (auto const &bm : requested_environment_blend_modes) { + m_xr.environment_blend_mode = bm; + if (std::ranges::contains(environment_blend_modes, bm)) { + break; + } + } + if (m_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."); + m_xr.environment_blend_mode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE; + } - for (auto &requestLayer : apiLayers) { - for (auto &layerProperty : apiLayerProperties) { - if (strcmp(requestLayer.c_str(), layerProperty.layerName) != 0) { - continue; - } else { - activeAPILayers.push_back(requestLayer.c_str()); - break; - } - } - } + { // Reference space + XrReferenceSpaceCreateInfo 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}}, + }; - uint32_t extensionCount = 0; - std::vector extensionProperties; - if (xrEnumerateInstanceExtensionProperties(nullptr, 0, &extensionCount, nullptr) != XR_SUCCESS) { - throw std::runtime_error("Failed to enumerate OpenXR instance extension properties"); - } - extensionProperties.resize(extensionCount, {XR_TYPE_EXTENSION_PROPERTIES}); - if (xrEnumerateInstanceExtensionProperties(nullptr, extensionCount, &extensionCount, extensionProperties.data()) != XR_SUCCESS) { - throw std::runtime_error("Failed to enumerate OpenXR instance extension properties"); - } - - for (auto &requestedInstanceExtension : instance_extensions) { - bool found = false; - for (auto &extensionProperty : extensionProperties) { - if (strcmp(requestedInstanceExtension.c_str(), extensionProperty.extensionName) != 0) { - continue; - } else { - active_instance_extensions.push_back(requestedInstanceExtension.c_str()); - found = true; - break; - } - } - if (!found) { - throw std::runtime_error(std::format("Failed to find OpenXR instance extension: {}", requestedInstanceExtension)); - } - } - - { - XrInstanceCreateInfo ci { - .type = XR_TYPE_INSTANCE_CREATE_INFO, - .next = NULL, - .createFlags = 0, - .applicationInfo = app_info, - .enabledApiLayerCount = static_cast(activeAPILayers.size()), - .enabledApiLayerNames = activeAPILayers.data(), - .enabledExtensionCount = static_cast(active_instance_extensions.size()), - .enabledExtensionNames = active_instance_extensions.data(), - }; - - XrInstance instance; - if (xrCreateInstance(&ci, &instance) != XR_SUCCESS) { - throw std::runtime_error("Failed to create OpenXR instance"); - } - m_xr.instance = instance; - } - - { - XrSystemGetInfo gi { - .type = XR_TYPE_SYSTEM_GET_INFO, - .next = nullptr, - }; - - XrFormFactor factors[] { - XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY, - XR_FORM_FACTOR_HANDHELD_DISPLAY, - }; - for (auto const factor : factors) { - gi.formFactor = factor; - XrSystemId system_id; - if (xrGetSystem(*m_xr.instance, &gi, &system_id) == XR_SUCCESS) { - m_xr.system_id = system_id; - break; - } - } - - if (!m_xr.system_id) { - throw std::runtime_error("Failed to find valid form factor"); - } - } - - XrGraphicsRequirementsOpenGLESKHR reqs { - .type = XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_ES_KHR, - .next = nullptr, - }; - PFN_xrGetOpenGLESGraphicsRequirementsKHR xrGetOpenGLESGraphicsRequirementsKHR; - xrGetInstanceProcAddr(*m_xr.instance, "xrGetOpenGLESGraphicsRequirementsKHR", (PFN_xrVoidFunction*)&xrGetOpenGLESGraphicsRequirementsKHR); - if (xrGetOpenGLESGraphicsRequirementsKHR(*m_xr.instance, *m_xr.system_id, &reqs) != XR_SUCCESS) { - throw std::runtime_error("Failed to get GLES graphics requirements"); - } - printf("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)); - - 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 = m_wayland.egl_display, - .config = m_wayland.egl_config, - .context = m_wayland.egl_context, - }; - - XrSessionCreateInfo ci { - .type = XR_TYPE_SESSION_CREATE_INFO, - .next = &gbind, - .createFlags = 0, - .systemId = *m_xr.system_id, - }; - - if (xrCreateSession(*m_xr.instance, &ci, &m_xr.session) != XR_SUCCESS) { - throw std::runtime_error("Failed to create OpenXR session"); - } - } + if (xrCreateReferenceSpace(m_xr.session, &ci, &m_xr.local_space) != + XR_SUCCESS) { + throw std::runtime_error("Failed to create OpenXR reference space"); + } + } } void LunarWM::poll_events_xr() { - XrEventDataBuffer event_data{XR_TYPE_EVENT_DATA_BUFFER}; - auto XrPollEvents = [&]() -> bool { - event_data = {XR_TYPE_EVENT_DATA_BUFFER}; - return xrPollEvent(*m_xr.instance, &event_data) == XR_SUCCESS; + XrEventDataBuffer event_data{XR_TYPE_EVENT_DATA_BUFFER}; + auto XrPollEvents = [&]() -> bool { + event_data = {XR_TYPE_EVENT_DATA_BUFFER}; + return xrPollEvent(*m_xr.instance, &event_data) == XR_SUCCESS; + }; + + while (XrPollEvents()) { + switch (event_data.type) { + case XR_TYPE_EVENT_DATA_EVENTS_LOST: { + XrEventDataEventsLost *el = + reinterpret_cast(&event_data); + wlr_log(WLR_INFO, "OPENXR: Events Lost: %d", el->lostEventCount); + break; + } + case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING: { + XrEventDataInstanceLossPending *ilp = + reinterpret_cast(&event_data); + wlr_log(WLR_INFO, "OPENXR: Instance Loss Pending at: %ld", ilp->lossTime); + m_session_running = false; + m_running = false; + break; + } + case XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED: { + XrEventDataInteractionProfileChanged *ipc = + reinterpret_cast(&event_data); + wlr_log(WLR_INFO, "OPENXR: Interaction Profile changed for Session: %p", + ipc->session); + if (ipc->session != m_xr.session) { + wlr_log(WLR_ERROR, + "XrEventDataInteractionProfileChanged for unknown Session"); + break; + } + break; + } + case XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING: { + XrEventDataReferenceSpaceChangePending *scp = + reinterpret_cast( + &event_data); + wlr_log(WLR_INFO, + "OPENXR: Reference Space Change pending for Session: %p", + scp->session); + if (scp->session != m_xr.session) { + wlr_log(WLR_ERROR, + "XrEventDataReferenceSpaceChangePending for unknown Session"); + break; + } + break; + } + case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: { + XrEventDataSessionStateChanged *sc = + reinterpret_cast(&event_data); + if (sc->session != m_xr.session) { + wlr_log(WLR_ERROR, + "XrEventDataSessionStateChanged for unknown Session"); + break; + } + + if (sc->state == XR_SESSION_STATE_READY) { + XrSessionBeginInfo bi{XR_TYPE_SESSION_BEGIN_INFO}; + bi.primaryViewConfigurationType = + XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO; + bi.primaryViewConfigurationType = + XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO; + if (xrBeginSession(m_xr.session, &bi) != XR_SUCCESS) { + throw std::runtime_error("Failed to begin session"); + } + m_session_running = true; + } + if (sc->state == XR_SESSION_STATE_STOPPING) { + if (xrEndSession(m_xr.session) != XR_SUCCESS) { + throw std::runtime_error("Failed to end session"); + } + m_session_running = false; + } + if (sc->state == XR_SESSION_STATE_EXITING) { + m_session_running = false; + m_running = false; + } + if (sc->state == XR_SESSION_STATE_LOSS_PENDING) { + m_session_running = false; + m_running = false; + } + m_xr.session_state = sc->state; + break; + } + default: { + break; + } + } + } +} + +bool LunarWM::render_layer(RenderLayerInfo &info) { + std::vector views(m_xr.view_configuration_views.size(), {XR_TYPE_VIEW}); + + XrViewState view_state{XR_TYPE_VIEW_STATE}; + XrViewLocateInfo view_locate{XR_TYPE_VIEW_LOCATE_INFO}; + view_locate.viewConfigurationType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO; + view_locate.displayTime = info.predicted_display_time; + view_locate.space = m_xr.local_space; + + uint32_t view_count = 0; + if (xrLocateViews(m_xr.session, &view_locate, &view_state, + (uint32_t)views.size(), &view_count, views.data()) != XR_SUCCESS) { + wlr_log(WLR_ERROR, "Failed to locate views"); + return false; + } + + info.layer_projection_views.resize(view_count, {XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW}); + + auto acquire_wait = [](XrSwapchain sc, uint32_t &idx) -> bool { + XrSwapchainImageAcquireInfo ai{XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO}; + if (xrAcquireSwapchainImage(sc, &ai, &idx) != XR_SUCCESS) return false; + + XrSwapchainImageWaitInfo wi{XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO}; + wi.timeout = XR_INFINITE_DURATION; + return xrWaitSwapchainImage(sc, &wi) == XR_SUCCESS; + }; + auto release = [](XrSwapchain sc) { + XrSwapchainImageReleaseInfo ri{XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO}; + xrReleaseSwapchainImage(sc, &ri); }; - while (XrPollEvents()) { - switch (event_data.type) { - case XR_TYPE_EVENT_DATA_EVENTS_LOST: { - XrEventDataEventsLost *eventsLost = reinterpret_cast(&event_data); - wlr_log(WLR_INFO, "OPENXR: Events Lost: %d", eventsLost->lostEventCount); - break; - } - case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING: { - XrEventDataInstanceLossPending *instanceLossPending = reinterpret_cast(&event_data); - wlr_log(WLR_INFO, "OPENXR: Instance Loss Pending at: %ld", instanceLossPending->lossTime); - m_session_running = false; - m_running = false; - break; - } - case XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED: { - XrEventDataInteractionProfileChanged *interactionProfileChanged = reinterpret_cast(&event_data); - wlr_log(WLR_INFO, "OPENXR: Interaction Profile changed for Session: %p", interactionProfileChanged->session); - if (interactionProfileChanged->session != m_xr.session) { - wlr_log(WLR_ERROR, "XrEventDataInteractionProfileChanged for unknown Session"); - break; - } - break; - } - case XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING: { - XrEventDataReferenceSpaceChangePending *referenceSpaceChangePending = reinterpret_cast(&event_data); - wlr_log(WLR_INFO, "OPENXR: Reference Space Change pending for Session: %p", referenceSpaceChangePending->session); - if (referenceSpaceChangePending->session != m_xr.session) { - wlr_log(WLR_ERROR, "XrEventDataReferenceSpaceChangePending for unknown Session"); - break; - } - break; - } - case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: { - XrEventDataSessionStateChanged *sessionStateChanged = reinterpret_cast(&event_data); - if (sessionStateChanged->session != m_xr.session) { - wlr_log(WLR_ERROR, "XrEventDataSessionStateChanged for unknown Session"); - break; - } + for (uint32_t i = 0; i < view_count; ++i) { + auto &color_sc = m_xr.swapchains.color[i]; + auto &depth_sc = m_xr.swapchains.depth[i]; - if (sessionStateChanged->state == XR_SESSION_STATE_READY) { - XrSessionBeginInfo sessionBeginInfo{XR_TYPE_SESSION_BEGIN_INFO}; - sessionBeginInfo.primaryViewConfigurationType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO; - if (xrBeginSession(m_xr.session, &sessionBeginInfo) != XR_SUCCESS) { - throw std::runtime_error("Failed to begin session"); - } - m_session_running = true; - } - if (sessionStateChanged->state == XR_SESSION_STATE_STOPPING) { - if (xrEndSession(m_xr.session) != XR_SUCCESS) { - throw std::runtime_error("Failed to end session"); - } - m_session_running = false; - } - if (sessionStateChanged->state == XR_SESSION_STATE_EXITING) { - m_session_running = false; - m_running = false; - } - if (sessionStateChanged->state == XR_SESSION_STATE_LOSS_PENDING) { - m_session_running = false; - m_running = false; - } - m_xr.session_state = sessionStateChanged->state; - break; - } - default: { - break; + uint32_t color_idx = 0, depth_idx = 0; + if (!acquire_wait(color_sc.swapchain, color_idx) || + !acquire_wait(depth_sc.swapchain, depth_idx)) { + wlr_log(WLR_ERROR, "Failed to acquire swapchain images"); + continue; } + + const uint32_t w = m_xr.view_configuration_views[i].recommendedImageRectWidth; + const uint32_t h = m_xr.view_configuration_views[i].recommendedImageRectHeight; + + m_renderer.begin_rendering(); + { + if (m_xr.environment_blend_mode == XR_ENVIRONMENT_BLEND_MODE_OPAQUE) + m_renderer.clear_color(color_sc.image_views[color_idx], 0.17f, 0.17f, 0.17f, 1.0f); + else + m_renderer.clear_color(color_sc.image_views[color_idx], 0.0f, 0.0f, 0.0f, 1.0f); + + m_renderer.clear_depth(depth_sc.image_views[depth_idx], 1.0f); } + m_renderer.end_rendering(); + + auto &pv = info.layer_projection_views[i]; + pv.pose = views[i].pose; + pv.fov = views[i].fov; + pv.subImage.swapchain = color_sc.swapchain; + pv.subImage.imageRect = {{0, 0}, {(int32_t)w, (int32_t)h}}; + pv.subImage.imageArrayIndex = 0; + + release(color_sc.swapchain); + release(depth_sc.swapchain); } + + info.layer_projection.layerFlags = + XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT | + XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT; + info.layer_projection.space = m_xr.local_space; + info.layer_projection.viewCount = (uint32_t)info.layer_projection_views.size(); + info.layer_projection.views = info.layer_projection_views.data(); + + return true; } LunarWM::~LunarWM() { assert(m_initialized); - puts("1"); + if (m_xr.local_space == XR_NULL_HANDLE) { + xrDestroySpace(m_xr.local_space); + } - if (m_xr.session != XR_NULL_HANDLE) { - xrDestroySession(m_xr.session); - } - puts("2"); + for (size_t i = 0; i < m_xr.swapchains.color.size(); i++) { + auto &color_sc = m_xr.swapchains.color[i]; + auto &depth_sc = m_xr.swapchains.depth[i]; - if (m_xr.instance) { - xrDestroyInstance(*m_xr.instance); - } - puts("3"); + for (auto &iv : color_sc.image_views) { + glDeleteFramebuffers(1, &iv); + } + for (auto &iv : depth_sc.image_views) { + glDeleteFramebuffers(1, &iv); + } + + xrDestroySwapchain(color_sc.swapchain); + xrDestroySwapchain(depth_sc.swapchain); + } + + if (m_xr.session != XR_NULL_HANDLE) { + xrDestroySession(m_xr.session); + } + + if (m_xr.instance) { + xrDestroyInstance(*m_xr.instance); + } wl_list_remove(&m_wayland.keyboards); - puts("4"); wl_display_destroy_clients(m_wayland.display); if (!m_wayland.allocator) { wlr_allocator_destroy(m_wayland.allocator); } - puts("5"); if (!m_wayland.renderer) { wlr_renderer_destroy(m_wayland.renderer); } - puts("6"); if (!m_wayland.backend) { wlr_backend_destroy(m_wayland.backend); } - puts("7"); if (!m_wayland.display) { wl_display_destroy(m_wayland.display); } @@ -448,25 +1435,69 @@ void LunarWM::run() { } auto const *socket = wl_display_add_socket_auto(m_wayland.display); - if (!socket) { - throw std::runtime_error("Failed to add wayland socket to display"); - } + if (!socket) { + throw std::runtime_error("Failed to add wayland socket to display"); + } - setenv("WAYLAND_DISPLAY", socket, true); - wlr_log(WLR_INFO, "Running compositor on WAYLAND_DISPLAY=%s", socket); + setenv("WAYLAND_DISPLAY", socket, true); + wlr_log(WLR_INFO, "Running compositor on WAYLAND_DISPLAY=%s", socket); - m_running = true; - while (m_running) { - wl_display_flush_clients(m_wayland.display); - wl_event_loop_dispatch(m_wayland.event_loop, 0); + m_running = true; + while (m_running) { + wl_display_flush_clients(m_wayland.display); + wl_event_loop_dispatch(m_wayland.event_loop, 0); poll_events_xr(); + + XrFrameState frame_state{ + .type = XR_TYPE_FRAME_STATE, + }; + XrFrameWaitInfo frame_wait_info{ + .type = XR_TYPE_FRAME_WAIT_INFO, + .next = nullptr, + }; + if (xrWaitFrame(m_xr.session, &frame_wait_info, &frame_state) != + XR_SUCCESS) { + throw std::runtime_error("Failed to wait for OpenXR frame"); + } + + XrFrameBeginInfo frame_begin_info{ + .type = XR_TYPE_FRAME_BEGIN_INFO, + .next = nullptr, + }; + XrResult res = xrBeginFrame(m_xr.session, &frame_begin_info); + if (res != XR_FRAME_DISCARDED && res != XR_SUCCESS) { + throw std::runtime_error( + std::format("Failed to begin the OpenXR Frame: {}", res)); + } + + RenderLayerInfo render_layer_info{ + .predicted_display_time = frame_state.predictedDisplayTime, + }; + bool session_active = (m_xr.session_state == XR_SESSION_STATE_SYNCHRONIZED || m_xr.session_state == XR_SESSION_STATE_VISIBLE || m_xr.session_state == XR_SESSION_STATE_FOCUSED); + if (session_active && frame_state.shouldRender) { + auto rendered = this->render_layer(render_layer_info); + if (rendered) { + render_layer_info.layers.push_back(reinterpret_cast(&render_layer_info.layer_projection)); + } + } + + XrFrameEndInfo frame_end_info { + .type = XR_TYPE_FRAME_END_INFO, + .displayTime = frame_state.predictedDisplayTime, + .environmentBlendMode = m_xr.environment_blend_mode, + .layerCount = static_cast(render_layer_info.layers.size()), + .layers = render_layer_info.layers.data(), + }; + if (xrEndFrame(m_xr.session, &frame_end_info) != XR_SUCCESS) { + throw std::runtime_error("Failed to end OpenXR frame"); + } } } void LunarWM::terminate() { - wlr_log(WLR_INFO, "Stopping compositor"); - m_running = false; + wlr_log(WLR_INFO, "Stopping compositor"); + m_running = false; } } // namespace LunarWM diff --git a/src/Math.cppm b/src/Math.cppm index 7feeb57..9876b35 100644 --- a/src/Math.cppm +++ b/src/Math.cppm @@ -61,6 +61,9 @@ Vec2 operator*(T scalar, Vec2 const &v) { template requires std::is_arithmetic_v struct Rect { + Rect(Vec2 pos, Vec2 size) : pos(pos), size(size) {} + Rect(T x, T y, T w, T h) : pos({x, y}), size({w, h}) {} + T &x() { return pos.x; } T &y() { return pos.y; } T &w() { return size.x; } @@ -79,8 +82,6 @@ struct Rect { T &left() { return x(); } T &top() { return y(); } - Rect(T x, T y, T w, T h) : pos({x, y}), size({w, h}) {} - Vec2 pos, size; }; @@ -190,5 +191,12 @@ std::vector> subtract_rect(Math::Rect const &src, return result; } +template + requires std::is_arithmetic_v +struct Viewport { + Rect rect; + Vec2 depth_limits; +}; + } // namespace Math } // namespace LunarWM