diff options
Diffstat (limited to 'modules/openxr/openxr_api.cpp')
-rw-r--r-- | modules/openxr/openxr_api.cpp | 280 |
1 files changed, 188 insertions, 92 deletions
diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp index 32512070d6..c67be5a2b3 100644 --- a/modules/openxr/openxr_api.cpp +++ b/modules/openxr/openxr_api.cpp @@ -54,6 +54,7 @@ #endif #include "extensions/openxr_composition_layer_depth_extension.h" +#include "extensions/openxr_debug_utils_extension.h" #include "extensions/openxr_eye_gaze_interaction.h" #include "extensions/openxr_fb_display_refresh_rate_extension.h" #include "extensions/openxr_fb_foveation_extension.h" @@ -270,17 +271,16 @@ OpenXRAPI *OpenXRAPI::singleton = nullptr; Vector<OpenXRExtensionWrapper *> OpenXRAPI::registered_extension_wrappers; bool OpenXRAPI::openxr_is_enabled(bool p_check_run_in_editor) { - // @TODO we need an overrule switch so we can force enable openxr, i.e run "godot --openxr_enabled" - - if (Engine::get_singleton()->is_editor_hint() && p_check_run_in_editor) { - // Disabled for now, using XR inside of the editor we'll be working on during the coming months. - return false; - } else { - if (XRServer::get_xr_mode() == XRServer::XRMODE_DEFAULT) { - return GLOBAL_GET("xr/openxr/enabled"); + if (XRServer::get_xr_mode() == XRServer::XRMODE_DEFAULT) { + if (Engine::get_singleton()->is_editor_hint() && p_check_run_in_editor) { + // For now, don't start OpenXR when the editor starts up. In the future, this may change + // if we want to integrate more XR features into the editor experience. + return false; } else { - return XRServer::get_xr_mode() == XRServer::XRMODE_ON; + return GLOBAL_GET("xr/openxr/enabled"); } + } else { + return XRServer::get_xr_mode() == XRServer::XRMODE_ON; } } @@ -316,6 +316,46 @@ String OpenXRAPI::get_swapchain_format_name(int64_t p_swapchain_format) const { return String("Swapchain format ") + String::num_int64(int64_t(p_swapchain_format)); } +void OpenXRAPI::set_object_name(XrObjectType p_object_type, uint64_t p_object_handle, const String &p_object_name) { + OpenXRDebugUtilsExtension *debug_utils = OpenXRDebugUtilsExtension::get_singleton(); + if (!debug_utils || !debug_utils->get_active()) { + // Not enabled/active? Ignore. + return; + } + + debug_utils->set_object_name(p_object_type, p_object_handle, p_object_name.utf8().get_data()); +} + +void OpenXRAPI::begin_debug_label_region(const String &p_label_name) { + OpenXRDebugUtilsExtension *debug_utils = OpenXRDebugUtilsExtension::get_singleton(); + if (!debug_utils || !debug_utils->get_active()) { + // Not enabled/active? Ignore. + return; + } + + debug_utils->begin_debug_label_region(p_label_name.utf8().get_data()); +} + +void OpenXRAPI::end_debug_label_region() { + OpenXRDebugUtilsExtension *debug_utils = OpenXRDebugUtilsExtension::get_singleton(); + if (!debug_utils || !debug_utils->get_active()) { + // Not enabled/active? Ignore. + return; + } + + debug_utils->end_debug_label_region(); +} + +void OpenXRAPI::insert_debug_label(const String &p_label_name) { + OpenXRDebugUtilsExtension *debug_utils = OpenXRDebugUtilsExtension::get_singleton(); + if (!debug_utils || !debug_utils->get_active()) { + // Not enabled/active? Ignore. + return; + } + + debug_utils->insert_debug_label(p_label_name.utf8().get_data()); +} + bool OpenXRAPI::load_layer_properties() { // This queries additional layers that are available and can be initialized when we create our OpenXR instance if (layer_properties != nullptr) { @@ -516,16 +556,13 @@ bool OpenXRAPI::create_instance() { extension_ptrs.push_back(enabled_extensions[i].get_data()); } - // Get our project name - String project_name = GLOBAL_GET("application/config/name"); - // Create our OpenXR instance XrApplicationInfo application_info{ - "", // applicationName, we'll set this down below + "Godot Engine", // applicationName, if we're running a game we'll update this down below. 1, // applicationVersion, we don't currently have this - "Godot Game Engine", // engineName + "Godot Engine", // engineName VERSION_MAJOR * 10000 + VERSION_MINOR * 100 + VERSION_PATCH, // engineVersion 4.0 -> 40000, 4.0.1 -> 40001, 4.1 -> 40100, etc. - XR_CURRENT_API_VERSION // apiVersion + XR_API_VERSION_1_0 // apiVersion }; void *next_pointer = nullptr; @@ -547,7 +584,11 @@ bool OpenXRAPI::create_instance() { extension_ptrs.ptr() // enabledExtensionNames }; - copy_string_to_char_buffer(project_name, instance_create_info.applicationInfo.applicationName, XR_MAX_APPLICATION_NAME_SIZE); + // Get our project name + String project_name = GLOBAL_GET("application/config/name"); + if (!project_name.is_empty()) { + copy_string_to_char_buffer(project_name, instance_create_info.applicationInfo.applicationName, XR_MAX_APPLICATION_NAME_SIZE); + } XrResult result = xrCreateInstance(&instance_create_info, &instance); ERR_FAIL_COND_V_MSG(XR_FAILED(result), false, "Failed to create XR instance."); @@ -707,13 +748,6 @@ bool OpenXRAPI::load_supported_environmental_blend_modes() { print_verbose(String("OpenXR: Found environmental blend mode ") + OpenXRUtil::get_environment_blend_mode_name(supported_environment_blend_modes[i])); } - // Check value we loaded at startup... - if (!is_environment_blend_mode_supported(environment_blend_mode)) { - print_verbose(String("OpenXR: ") + OpenXRUtil::get_environment_blend_mode_name(environment_blend_mode) + String(" isn't supported, defaulting to ") + OpenXRUtil::get_environment_blend_mode_name(supported_environment_blend_modes[0])); - - environment_blend_mode = supported_environment_blend_modes[0]; - } - return true; } @@ -833,10 +867,21 @@ bool OpenXRAPI::create_session() { return false; } + set_object_name(XR_OBJECT_TYPE_SESSION, uint64_t(session), "Main Godot OpenXR Session"); + + begin_debug_label_region("Godot session active"); + for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) { wrapper->on_session_created(session); } + // Check our environment blend mode. This needs to happen after we call `on_session_created()` + // on the extension wrappers, so they can emulate alpha blend mode. + if (!set_environment_blend_mode(environment_blend_mode)) { + print_verbose(String("OpenXR: ") + OpenXRUtil::get_environment_blend_mode_name(environment_blend_mode) + String(" isn't supported, defaulting to ") + OpenXRUtil::get_environment_blend_mode_name(supported_environment_blend_modes[0])); + set_environment_blend_mode(supported_environment_blend_modes[0]); + } + return true; } @@ -902,6 +947,51 @@ bool OpenXRAPI::setup_play_space() { new_reference_space = XR_REFERENCE_SPACE_TYPE_LOCAL; will_emulate_local_floor = true; + + if (local_floor_emulation.local_space == XR_NULL_HANDLE) { + XrReferenceSpaceCreateInfo create_info = { + XR_TYPE_REFERENCE_SPACE_CREATE_INFO, // type + nullptr, // next + XR_REFERENCE_SPACE_TYPE_LOCAL, // referenceSpaceType + identityPose, // poseInReferenceSpace + }; + + XrResult result = xrCreateReferenceSpace(session, &create_info, &local_floor_emulation.local_space); + if (XR_FAILED(result)) { + print_line("OpenXR: Failed to create LOCAL space in order to emulate LOCAL_FLOOR [", get_error_string(result), "]"); + will_emulate_local_floor = false; + } + + set_object_name(XR_OBJECT_TYPE_SPACE, uint64_t(local_floor_emulation.local_space), "Emulation local space"); + } + + if (local_floor_emulation.stage_space == XR_NULL_HANDLE) { + XrReferenceSpaceCreateInfo create_info = { + XR_TYPE_REFERENCE_SPACE_CREATE_INFO, // type + nullptr, // next + XR_REFERENCE_SPACE_TYPE_STAGE, // referenceSpaceType + identityPose, // poseInReferenceSpace + }; + + XrResult result = xrCreateReferenceSpace(session, &create_info, &local_floor_emulation.stage_space); + if (XR_FAILED(result)) { + print_line("OpenXR: Failed to create STAGE space in order to emulate LOCAL_FLOOR [", get_error_string(result), "]"); + will_emulate_local_floor = false; + } + + set_object_name(XR_OBJECT_TYPE_SPACE, uint64_t(local_floor_emulation.stage_space), "Emulation stage space"); + } + + if (!will_emulate_local_floor) { + if (local_floor_emulation.local_space != XR_NULL_HANDLE) { + xrDestroySpace(local_floor_emulation.local_space); + local_floor_emulation.local_space = XR_NULL_HANDLE; + } + if (local_floor_emulation.stage_space != XR_NULL_HANDLE) { + xrDestroySpace(local_floor_emulation.stage_space); + local_floor_emulation.stage_space = XR_NULL_HANDLE; + } + } } else { // Fallback on LOCAL, which all OpenXR runtimes are required to support. print_verbose(String("OpenXR: ") + OpenXRUtil::get_reference_space_name(requested_reference_space) + String(" isn't supported, defaulting to LOCAL space.")); @@ -931,16 +1021,13 @@ bool OpenXRAPI::setup_play_space() { play_space = new_play_space; reference_space = new_reference_space; - emulating_local_floor = will_emulate_local_floor; - if (emulating_local_floor) { - // We'll use the STAGE space to get the floor height, but we can't do that until - // after xrWaitFrame(), so just set this flag for now. - // Render state will be updated then. - should_reset_emulated_floor_height = true; - } else { - // Update render state so this play space is used rendering the upcoming frame. - set_render_play_space(play_space); - } + set_object_name(XR_OBJECT_TYPE_SPACE, uint64_t(play_space), "Play space"); + + local_floor_emulation.enabled = will_emulate_local_floor; + local_floor_emulation.should_reset_floor_height = will_emulate_local_floor; + + // Update render state so this play space is used rendering the upcoming frame. + set_render_play_space(play_space); return true; } @@ -971,67 +1058,45 @@ bool OpenXRAPI::setup_view_space() { return false; } + set_object_name(XR_OBJECT_TYPE_SPACE, uint64_t(view_space), "View space"); + return true; } bool OpenXRAPI::reset_emulated_floor_height() { - ERR_FAIL_COND_V(!emulating_local_floor, false); - - // This is based on the example code in the OpenXR spec which shows how to - // emulate LOCAL_FLOOR if it's not supported. - // See: https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XR_EXT_local_floor + ERR_FAIL_COND_V(!local_floor_emulation.enabled, false); + ERR_FAIL_COND_V(local_floor_emulation.local_space == XR_NULL_HANDLE, false); + ERR_FAIL_COND_V(local_floor_emulation.stage_space == XR_NULL_HANDLE, false); XrResult result; - XrPosef identityPose = { - { 0.0, 0.0, 0.0, 1.0 }, - { 0.0, 0.0, 0.0 } - }; - - XrSpace local_space = XR_NULL_HANDLE; - XrSpace stage_space = XR_NULL_HANDLE; - - XrReferenceSpaceCreateInfo create_info = { - XR_TYPE_REFERENCE_SPACE_CREATE_INFO, // type - nullptr, // next - XR_REFERENCE_SPACE_TYPE_LOCAL, // referenceSpaceType - identityPose, // poseInReferenceSpace - }; - - result = xrCreateReferenceSpace(session, &create_info, &local_space); - if (XR_FAILED(result)) { - print_line("OpenXR: Failed to create LOCAL space in order to emulate LOCAL_FLOOR [", get_error_string(result), "]"); - return false; - } - - create_info.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_STAGE; - result = xrCreateReferenceSpace(session, &create_info, &stage_space); - if (XR_FAILED(result)) { - print_line("OpenXR: Failed to create STAGE space in order to emulate LOCAL_FLOOR [", get_error_string(result), "]"); - xrDestroySpace(local_space); - return false; - } - XrSpaceLocation stage_location = { XR_TYPE_SPACE_LOCATION, // type nullptr, // next 0, // locationFlags - identityPose, // pose + { { 0.0, 0.0, 0.0, 1.0 }, { 0.0, 0.0, 0.0 } }, // pose }; - result = xrLocateSpace(stage_space, local_space, get_predicted_display_time(), &stage_location); - - xrDestroySpace(local_space); - xrDestroySpace(stage_space); + result = xrLocateSpace(local_floor_emulation.stage_space, local_floor_emulation.local_space, get_predicted_display_time(), &stage_location); if (XR_FAILED(result)) { print_line("OpenXR: Failed to locate STAGE space in LOCAL space, in order to emulate LOCAL_FLOOR [", get_error_string(result), "]"); return false; } + XrPosef pose = { + { 0.0, 0.0, 0.0, 1.0 }, + { 0.0, stage_location.pose.position.y, 0.0 } + }; + + XrReferenceSpaceCreateInfo create_info = { + XR_TYPE_REFERENCE_SPACE_CREATE_INFO, // type + nullptr, // next + XR_REFERENCE_SPACE_TYPE_LOCAL, // referenceSpaceType + pose, // poseInReferenceSpace + }; + XrSpace new_play_space; - create_info.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL; - create_info.poseInReferenceSpace.position.y = stage_location.pose.position.y; result = xrCreateReferenceSpace(session, &create_info, &new_play_space); if (XR_FAILED(result)) { print_line("OpenXR: Failed to recreate emulated LOCAL_FLOOR play space with latest floor estimate [", get_error_string(result), "]"); @@ -1169,6 +1234,8 @@ bool OpenXRAPI::create_main_swapchains(Size2i p_size) { if (!render_state.main_swapchains[OPENXR_SWAPCHAIN_COLOR].create(0, XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT | XR_SWAPCHAIN_USAGE_MUTABLE_FORMAT_BIT, color_swapchain_format, render_state.main_swapchain_size.width, render_state.main_swapchain_size.height, sample_count, view_count)) { return false; } + + set_object_name(XR_OBJECT_TYPE_SWAPCHAIN, uint64_t(render_state.main_swapchains[OPENXR_SWAPCHAIN_COLOR].get_swapchain()), "Main color swapchain"); } // We create our depth swapchain if: @@ -1179,6 +1246,8 @@ bool OpenXRAPI::create_main_swapchains(Size2i p_size) { if (!render_state.main_swapchains[OPENXR_SWAPCHAIN_DEPTH].create(0, XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, depth_swapchain_format, render_state.main_swapchain_size.width, render_state.main_swapchain_size.height, sample_count, view_count)) { return false; } + + set_object_name(XR_OBJECT_TYPE_SWAPCHAIN, uint64_t(render_state.main_swapchains[OPENXR_SWAPCHAIN_DEPTH].get_swapchain()), "Main depth swapchain"); } // We create our velocity swapchain if: @@ -1220,6 +1289,10 @@ bool OpenXRAPI::create_main_swapchains(Size2i p_size) { } }; + for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) { + wrapper->on_main_swapchains_created(); + } + return true; }; @@ -1271,6 +1344,16 @@ void OpenXRAPI::destroy_session() { xrDestroySpace(view_space); view_space = XR_NULL_HANDLE; } + if (local_floor_emulation.local_space != XR_NULL_HANDLE) { + xrDestroySpace(local_floor_emulation.local_space); + local_floor_emulation.local_space = XR_NULL_HANDLE; + } + if (local_floor_emulation.stage_space != XR_NULL_HANDLE) { + xrDestroySpace(local_floor_emulation.stage_space); + local_floor_emulation.stage_space = XR_NULL_HANDLE; + } + local_floor_emulation.enabled = false; + local_floor_emulation.should_reset_floor_height = false; if (supported_reference_spaces != nullptr) { // free previous results @@ -1283,6 +1366,8 @@ void OpenXRAPI::destroy_session() { wrapper->on_session_destroyed(); } + end_debug_label_region(); + xrDestroySession(session); session = XR_NULL_HANDLE; } @@ -1433,6 +1518,10 @@ void OpenXRAPI::set_form_factor(XrFormFactor p_form_factor) { form_factor = p_form_factor; } +uint32_t OpenXRAPI::get_view_count() { + return view_count; +} + void OpenXRAPI::set_view_configuration(XrViewConfigurationType p_view_configuration) { ERR_FAIL_COND(is_initialized()); @@ -1707,8 +1796,12 @@ void OpenXRAPI::cleanup_extension_wrappers() { XrHandTrackerEXT OpenXRAPI::get_hand_tracker(int p_hand_index) { ERR_FAIL_INDEX_V(p_hand_index, OpenXRHandTrackingExtension::HandTrackedHands::OPENXR_MAX_TRACKED_HANDS, XR_NULL_HANDLE); + + OpenXRHandTrackingExtension *hand_tracking = OpenXRHandTrackingExtension::get_singleton(); + ERR_FAIL_NULL_V(hand_tracking, XR_NULL_HANDLE); + OpenXRHandTrackingExtension::HandTrackedHands hand = static_cast<OpenXRHandTrackingExtension::HandTrackedHands>(p_hand_index); - return OpenXRHandTrackingExtension::get_singleton()->get_hand_tracker(hand)->hand_tracker; + return hand_tracking->get_hand_tracker(hand)->hand_tracker; } Size2 OpenXRAPI::get_recommended_target_size() { @@ -1888,15 +1981,6 @@ bool OpenXRAPI::poll_events() { // We probably didn't poll fast enough, just output warning WARN_PRINT("OpenXR EVENT: " + itos(event->lostEventCount) + " event data lost!"); } break; - case XR_TYPE_EVENT_DATA_VISIBILITY_MASK_CHANGED_KHR: { - // XrEventDataVisibilityMaskChangedKHR *event = (XrEventDataVisibilityMaskChangedKHR *)&runtimeEvent; - - // TODO implement this in the future, we should call xrGetVisibilityMaskKHR to obtain a mask, - // this will allow us to prevent rendering the part of our view which is never displayed giving us - // a decent performance improvement. - - print_verbose("OpenXR EVENT: STUB: visibility mask changed"); - } break; case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING: { XrEventDataInstanceLossPending *event = (XrEventDataInstanceLossPending *)&runtimeEvent; @@ -1949,8 +2033,8 @@ bool OpenXRAPI::poll_events() { XrEventDataReferenceSpaceChangePending *event = (XrEventDataReferenceSpaceChangePending *)&runtimeEvent; print_verbose(String("OpenXR EVENT: reference space type ") + OpenXRUtil::get_reference_space_name(event->referenceSpaceType) + " change pending!"); - if (emulating_local_floor) { - should_reset_emulated_floor_height = true; + if (local_floor_emulation.enabled) { + local_floor_emulation.should_reset_floor_height = true; } if (event->poseValid && xr_interface) { xr_interface->on_pose_recentered(); @@ -2093,16 +2177,18 @@ bool OpenXRAPI::process() { set_render_display_info(frame_state.predictedDisplayTime, frame_state.shouldRender); + // This is before setup_play_space() to ensure that it happens on the frame after + // the play space has been created. + if (unlikely(local_floor_emulation.should_reset_floor_height && !play_space_is_dirty)) { + reset_emulated_floor_height(); + local_floor_emulation.should_reset_floor_height = false; + } + if (unlikely(play_space_is_dirty)) { setup_play_space(); play_space_is_dirty = false; } - if (unlikely(should_reset_emulated_floor_height)) { - reset_emulated_floor_height(); - should_reset_emulated_floor_height = false; - } - for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) { wrapper->on_process(); } @@ -2188,6 +2274,9 @@ void OpenXRAPI::pre_render() { } } + // We should get our frame no from the rendering server, but this will do. + begin_debug_label_region(String("Session Frame ") + String::num_uint64(++render_state.frame)); + // let's start our frame.. XrFrameBeginInfo frame_begin_info = { XR_TYPE_FRAME_BEGIN_INFO, // type @@ -2306,6 +2395,8 @@ void OpenXRAPI::end_frame() { return; } + end_debug_label_region(); // Session frame # + // neither eye is rendered return; } @@ -2380,6 +2471,8 @@ void OpenXRAPI::end_frame() { print_line("OpenXR: failed to end frame! [", get_error_string(result), "]"); return; } + + end_debug_label_region(); // Session frame # } float OpenXRAPI::get_display_refresh_rate() const { @@ -2490,7 +2583,6 @@ OpenXRAPI::OpenXRAPI() { if (Engine::get_singleton()->is_editor_hint()) { // Enabled OpenXR in the editor? Adjust our settings for the editor - } else { // Load settings from project settings int form_factor_setting = GLOBAL_GET("xr/openxr/form_factor"); @@ -2795,6 +2887,8 @@ RID OpenXRAPI::action_set_create(const String p_name, const String p_localized_n return RID(); } + set_object_name(XR_OBJECT_TYPE_ACTION_SET, uint64_t(action_set.handle), p_name); + return action_set_owner.make_rid(action_set); } @@ -2970,6 +3064,8 @@ RID OpenXRAPI::action_create(RID p_action_set, const String p_name, const String return RID(); } + set_object_name(XR_OBJECT_TYPE_ACTION, uint64_t(action.handle), p_name); + return action_owner.make_rid(action); } |