diff options
Diffstat (limited to 'modules/openxr')
20 files changed, 781 insertions, 273 deletions
diff --git a/modules/openxr/SCsub b/modules/openxr/SCsub index d4469b110b..b5978ab134 100644 --- a/modules/openxr/SCsub +++ b/modules/openxr/SCsub @@ -92,9 +92,11 @@ if env["vulkan"]: env_openxr.add_source_files(module_obj, "extensions/openxr_vulkan_extension.cpp") env_openxr.add_source_files(module_obj, "extensions/openxr_palm_pose_extension.cpp") +env_openxr.add_source_files(module_obj, "extensions/openxr_composition_layer_depth_extension.cpp") env_openxr.add_source_files(module_obj, "extensions/openxr_htc_vive_tracker_extension.cpp") env_openxr.add_source_files(module_obj, "extensions/openxr_hand_tracking_extension.cpp") env_openxr.add_source_files(module_obj, "extensions/openxr_fb_passthrough_extension_wrapper.cpp") +env_openxr.add_source_files(module_obj, "extensions/openxr_fb_display_refresh_rate_extension.cpp") env.modules_sources += module_obj diff --git a/modules/openxr/action_map/openxr_action.cpp b/modules/openxr/action_map/openxr_action.cpp index 359975a480..0fb4f0773f 100644 --- a/modules/openxr/action_map/openxr_action.cpp +++ b/modules/openxr/action_map/openxr_action.cpp @@ -64,13 +64,13 @@ Ref<OpenXRAction> OpenXRAction::new_action(const char *p_name, const char *p_loc } String OpenXRAction::get_name_with_set() const { - String name = get_name(); + String action_name = get_name(); if (action_set != nullptr) { - name = action_set->get_name() + "/" + name; + action_name = action_set->get_name() + "/" + action_name; } - return name; + return action_name; } void OpenXRAction::set_localized_name(const String p_localized_name) { diff --git a/modules/openxr/action_map/openxr_action_map.cpp b/modules/openxr/action_map/openxr_action_map.cpp index 185e44c29d..123f860ce9 100644 --- a/modules/openxr/action_map/openxr_action_map.cpp +++ b/modules/openxr/action_map/openxr_action_map.cpp @@ -224,7 +224,7 @@ void OpenXRActionMap::create_default_action_sets() { // Create our interaction profiles Ref<OpenXRInteractionProfile> profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/khr/simple_controller"); - profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); + profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose"); @@ -236,7 +236,7 @@ void OpenXRActionMap::create_default_action_sets() { // Create our Vive controller profile profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/htc/vive_controller"); - profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); + profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose"); @@ -257,7 +257,7 @@ void OpenXRActionMap::create_default_action_sets() { // Create our WMR controller profile profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/microsoft/motion_controller"); - profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); + profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose"); @@ -280,7 +280,7 @@ void OpenXRActionMap::create_default_action_sets() { // Create our Meta touch controller profile profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/oculus/touch_controller"); - profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); + profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose"); @@ -305,7 +305,7 @@ void OpenXRActionMap::create_default_action_sets() { // Create our Valve index controller profile profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/valve/index_controller"); - profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); + profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose"); @@ -333,7 +333,7 @@ void OpenXRActionMap::create_default_action_sets() { // Create our HP MR controller profile profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/hp/mixed_reality_controller"); - profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); + profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose"); @@ -356,7 +356,7 @@ void OpenXRActionMap::create_default_action_sets() { // Create our Samsung Odyssey controller profile, // Note that this controller is only identified specifically on WMR, on SteamVR this is identified as a normal WMR controller. profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/samsung/odyssey_controller"); - profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); + profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose"); @@ -379,7 +379,7 @@ void OpenXRActionMap::create_default_action_sets() { // Create our Vive Cosmos controller profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/htc/vive_cosmos_controller"); - profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); + profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose"); @@ -403,7 +403,7 @@ void OpenXRActionMap::create_default_action_sets() { // Note, Vive Focus 3 currently is not yet supported as a stand alone device // however HTC currently has a beta OpenXR runtime in testing we may support in the near future profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/htc/vive_focus3_controller"); - profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); + profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose"); @@ -427,7 +427,7 @@ void OpenXRActionMap::create_default_action_sets() { // Create our Huawei controller profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/huawei/controller"); - profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); + profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose"); diff --git a/modules/openxr/doc_classes/OpenXRInterface.xml b/modules/openxr/doc_classes/OpenXRInterface.xml index 25bf496de9..f089fd066e 100644 --- a/modules/openxr/doc_classes/OpenXRInterface.xml +++ b/modules/openxr/doc_classes/OpenXRInterface.xml @@ -10,6 +10,19 @@ <tutorials> <link title="Setting up XR">$DOCS_URL/tutorials/xr/setting_up_xr.html</link> </tutorials> + <methods> + <method name="get_available_display_refresh_rates" qualifiers="const"> + <return type="Array" /> + <description> + Returns display refresh rates supported by the current HMD. Only returned if this feature is supported by the OpenXR runtime and after the interface has been initialized. + </description> + </method> + </methods> + <members> + <member name="display_refresh_rate" type="float" setter="set_display_refresh_rate" getter="get_display_refresh_rate" default="0.0"> + The display refresh rate for the current HMD. Only functional if this feature is supported by the OpenXR runtime and after the interface has been initialized. + </member> + </members> <signals> <signal name="pose_recentered"> <description> diff --git a/modules/openxr/editor/openxr_action_map_editor.cpp b/modules/openxr/editor/openxr_action_map_editor.cpp index 51c402d746..b5223e5903 100644 --- a/modules/openxr/editor/openxr_action_map_editor.cpp +++ b/modules/openxr/editor/openxr_action_map_editor.cpp @@ -128,7 +128,7 @@ void OpenXRActionMapEditor::_update_interaction_profiles() { interaction_profiles.remove_at(0); tabs->remove_child(interaction_profile); - interaction_profile->queue_delete(); + interaction_profile->queue_free(); } // in with the new... @@ -205,7 +205,7 @@ void OpenXRActionMapEditor::_on_remove_action_set(Object *p_action_set_editor) { action_map->remove_action_set(action_set); actionsets_vb->remove_child(action_set_editor); - action_set_editor->queue_delete(); + action_set_editor->queue_free(); } void OpenXRActionMapEditor::_on_action_removed() { @@ -290,7 +290,7 @@ void OpenXRActionMapEditor::_on_tab_button_pressed(int p_tab) { action_map->remove_interaction_profile(interaction_profile); tabs->remove_child(profile_editor); - profile_editor->queue_delete(); + profile_editor->queue_free(); } void OpenXRActionMapEditor::open_action_map(String p_path) { @@ -364,7 +364,7 @@ OpenXRActionMapEditor::OpenXRActionMapEditor() { select_interaction_profile_dialog->connect("interaction_profile_selected", callable_mp(this, &OpenXRActionMapEditor::_on_interaction_profile_selected)); add_child(select_interaction_profile_dialog); - _load_action_map(ProjectSettings::get_singleton()->get("xr/openxr/default_action_map")); + _load_action_map(GLOBAL_GET("xr/openxr/default_action_map")); } OpenXRActionMapEditor::~OpenXRActionMapEditor() { diff --git a/modules/openxr/editor/openxr_action_set_editor.cpp b/modules/openxr/editor/openxr_action_set_editor.cpp index 804808a6b9..3869146e8e 100644 --- a/modules/openxr/editor/openxr_action_set_editor.cpp +++ b/modules/openxr/editor/openxr_action_set_editor.cpp @@ -140,7 +140,7 @@ void OpenXRActionSetEditor::_on_remove_action(Object *p_action_editor) { // And remove it.... action_map->remove_action(action->get_name_with_set()); // remove it from the set and any interaction profile it relates to actions_vb->remove_child(action_editor); - action_editor->queue_delete(); + action_editor->queue_free(); // Let action map editor know so we can update our interaction profiles emit_signal("action_removed"); diff --git a/modules/openxr/extensions/openxr_composition_layer_depth_extension.cpp b/modules/openxr/extensions/openxr_composition_layer_depth_extension.cpp new file mode 100644 index 0000000000..0f6aaf8afb --- /dev/null +++ b/modules/openxr/extensions/openxr_composition_layer_depth_extension.cpp @@ -0,0 +1,58 @@ +/*************************************************************************/ +/* openxr_composition_layer_depth_extension.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "openxr_composition_layer_depth_extension.h" + +OpenXRCompositionLayerDepthExtension *OpenXRCompositionLayerDepthExtension::singleton = nullptr; + +OpenXRCompositionLayerDepthExtension *OpenXRCompositionLayerDepthExtension::get_singleton() { + return singleton; +} + +OpenXRCompositionLayerDepthExtension::OpenXRCompositionLayerDepthExtension(OpenXRAPI *p_openxr_api) : + OpenXRExtensionWrapper(p_openxr_api) { + singleton = this; + + request_extensions[XR_KHR_COMPOSITION_LAYER_DEPTH_EXTENSION_NAME] = &available; +} + +OpenXRCompositionLayerDepthExtension::~OpenXRCompositionLayerDepthExtension() { + singleton = nullptr; +} + +bool OpenXRCompositionLayerDepthExtension::is_available() { + return available; +} + +XrCompositionLayerBaseHeader *OpenXRCompositionLayerDepthExtension::get_composition_layer() { + // Seems this is all done in our base layer... Just in case this changes... + + return nullptr; +} diff --git a/modules/openxr/extensions/openxr_composition_layer_depth_extension.h b/modules/openxr/extensions/openxr_composition_layer_depth_extension.h new file mode 100644 index 0000000000..9533783d83 --- /dev/null +++ b/modules/openxr/extensions/openxr_composition_layer_depth_extension.h @@ -0,0 +1,53 @@ +/*************************************************************************/ +/* openxr_composition_layer_depth_extension.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef OPENXR_COMPOSITION_LAYER_DEPTH_EXTENSION_H +#define OPENXR_COMPOSITION_LAYER_DEPTH_EXTENSION_H + +#include "openxr_composition_layer_provider.h" +#include "openxr_extension_wrapper.h" + +class OpenXRCompositionLayerDepthExtension : public OpenXRExtensionWrapper, public OpenXRCompositionLayerProvider { +public: + static OpenXRCompositionLayerDepthExtension *get_singleton(); + + OpenXRCompositionLayerDepthExtension(OpenXRAPI *p_openxr_api); + virtual ~OpenXRCompositionLayerDepthExtension() override; + + bool is_available(); + virtual XrCompositionLayerBaseHeader *get_composition_layer() override; + +private: + static OpenXRCompositionLayerDepthExtension *singleton; + + bool available = false; +}; + +#endif // OPENXR_COMPOSITION_LAYER_DEPTH_EXTENSION_H diff --git a/modules/openxr/extensions/openxr_composition_layer_provider.h b/modules/openxr/extensions/openxr_composition_layer_provider.h index ba51389f7d..a4c4cbe0c6 100644 --- a/modules/openxr/extensions/openxr_composition_layer_provider.h +++ b/modules/openxr/extensions/openxr_composition_layer_provider.h @@ -31,6 +31,7 @@ #ifndef OPENXR_COMPOSITION_LAYER_PROVIDER_H #define OPENXR_COMPOSITION_LAYER_PROVIDER_H +#include "openxr_extension_wrapper.h" #include <openxr/openxr.h> // Interface for OpenXR extensions that provide a composition layer. diff --git a/modules/openxr/extensions/openxr_extension_wrapper.h b/modules/openxr/extensions/openxr_extension_wrapper.h index 623c264e6e..c417c90d11 100644 --- a/modules/openxr/extensions/openxr_extension_wrapper.h +++ b/modules/openxr/extensions/openxr_extension_wrapper.h @@ -100,11 +100,12 @@ public: class OpenXRGraphicsExtensionWrapper : public OpenXRExtensionWrapper { public: virtual void get_usable_swapchain_formats(Vector<int64_t> &p_usable_swap_chains) = 0; + virtual void get_usable_depth_formats(Vector<int64_t> &p_usable_swap_chains) = 0; virtual String get_swapchain_format_name(int64_t p_swapchain_format) const = 0; virtual bool get_swapchain_image_data(XrSwapchain p_swapchain, int64_t p_swapchain_format, uint32_t p_width, uint32_t p_height, uint32_t p_sample_count, uint32_t p_array_size, void **r_swapchain_graphics_data) = 0; virtual void cleanup_swapchain_graphics_data(void **p_swapchain_graphics_data) = 0; virtual bool create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, Projection &r_camera_matrix) = 0; - virtual bool copy_render_target_to_image(RID p_from_render_target, void *p_swapchain_graphics_data, int p_image_index) = 0; + virtual RID get_texture(void *p_swapchain_graphics_data, int p_image_index) = 0; OpenXRGraphicsExtensionWrapper(OpenXRAPI *p_openxr_api) : OpenXRExtensionWrapper(p_openxr_api){}; diff --git a/modules/openxr/extensions/openxr_fb_display_refresh_rate_extension.cpp b/modules/openxr/extensions/openxr_fb_display_refresh_rate_extension.cpp new file mode 100644 index 0000000000..c0bbaea5b4 --- /dev/null +++ b/modules/openxr/extensions/openxr_fb_display_refresh_rate_extension.cpp @@ -0,0 +1,123 @@ +/*************************************************************************/ +/* openxr_fb_display_refresh_rate_extension.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "openxr_fb_display_refresh_rate_extension.h" + +OpenXRDisplayRefreshRateExtension *OpenXRDisplayRefreshRateExtension::singleton = nullptr; + +OpenXRDisplayRefreshRateExtension *OpenXRDisplayRefreshRateExtension::get_singleton() { + return singleton; +} + +OpenXRDisplayRefreshRateExtension::OpenXRDisplayRefreshRateExtension(OpenXRAPI *p_openxr_api) : + OpenXRExtensionWrapper(p_openxr_api) { + singleton = this; + + // Extensions we use for our hand tracking. + request_extensions[XR_FB_DISPLAY_REFRESH_RATE_EXTENSION_NAME] = &display_refresh_rate_ext; +} + +OpenXRDisplayRefreshRateExtension::~OpenXRDisplayRefreshRateExtension() { + display_refresh_rate_ext = false; +} + +void OpenXRDisplayRefreshRateExtension::on_instance_created(const XrInstance p_instance) { + if (display_refresh_rate_ext) { + EXT_INIT_XR_FUNC(xrEnumerateDisplayRefreshRatesFB); + EXT_INIT_XR_FUNC(xrGetDisplayRefreshRateFB); + EXT_INIT_XR_FUNC(xrRequestDisplayRefreshRateFB); + } +} + +void OpenXRDisplayRefreshRateExtension::on_instance_destroyed() { + display_refresh_rate_ext = false; +} + +float OpenXRDisplayRefreshRateExtension::get_refresh_rate() const { + float refresh_rate = 0.0; + + if (display_refresh_rate_ext) { + float rate; + XrResult result = xrGetDisplayRefreshRateFB(openxr_api->get_session(), &rate); + if (XR_FAILED(result)) { + print_line("OpenXR: Failed to obtain refresh rate [", openxr_api->get_error_string(result), "]"); + } else { + refresh_rate = rate; + } + } + + return refresh_rate; +} + +void OpenXRDisplayRefreshRateExtension::set_refresh_rate(float p_refresh_rate) { + if (display_refresh_rate_ext) { + XrResult result = xrRequestDisplayRefreshRateFB(openxr_api->get_session(), p_refresh_rate); + if (XR_FAILED(result)) { + print_line("OpenXR: Failed to set refresh rate [", openxr_api->get_error_string(result), "]"); + } + } +} + +Array OpenXRDisplayRefreshRateExtension::get_available_refresh_rates() const { + Array arr; + XrResult result; + + if (display_refresh_rate_ext) { + uint32_t display_refresh_rate_count = 0; + result = xrEnumerateDisplayRefreshRatesFB(openxr_api->get_session(), 0, &display_refresh_rate_count, nullptr); + if (XR_FAILED(result)) { + print_line("OpenXR: Failed to obtain refresh rates count [", openxr_api->get_error_string(result), "]"); + } + + if (display_refresh_rate_count > 0) { + float *display_refresh_rates = (float *)memalloc(sizeof(float) * display_refresh_rate_count); + if (display_refresh_rates == nullptr) { + print_line("OpenXR: Failed to obtain refresh rates memory buffer [", openxr_api->get_error_string(result), "]"); + return arr; + } + + result = xrEnumerateDisplayRefreshRatesFB(openxr_api->get_session(), display_refresh_rate_count, &display_refresh_rate_count, display_refresh_rates); + if (XR_FAILED(result)) { + print_line("OpenXR: Failed to obtain refresh rates count [", openxr_api->get_error_string(result), "]"); + memfree(display_refresh_rates); + return arr; + } + + for (uint32_t i = 0; i < display_refresh_rate_count; i++) { + float refresh_rate = display_refresh_rates[i]; + arr.push_back(Variant(refresh_rate)); + } + + memfree(display_refresh_rates); + } + } + + return arr; +} diff --git a/modules/openxr/extensions/openxr_fb_display_refresh_rate_extension.h b/modules/openxr/extensions/openxr_fb_display_refresh_rate_extension.h new file mode 100644 index 0000000000..dcd52fe4d1 --- /dev/null +++ b/modules/openxr/extensions/openxr_fb_display_refresh_rate_extension.h @@ -0,0 +1,70 @@ +/*************************************************************************/ +/* openxr_fb_display_refresh_rate_extension.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef OPENXR_FB_DISPLAY_REFRESH_RATE_EXTENSION_H +#define OPENXR_FB_DISPLAY_REFRESH_RATE_EXTENSION_H + +// This extension gives us access to the possible display refresh rates +// supported by the HMD. +// While this is an FB extension it has been adopted by most runtimes and +// will likely become core in the near future. + +#include "../openxr_api.h" +#include "../util.h" + +#include "openxr_extension_wrapper.h" + +class OpenXRDisplayRefreshRateExtension : public OpenXRExtensionWrapper { +public: + static OpenXRDisplayRefreshRateExtension *get_singleton(); + + OpenXRDisplayRefreshRateExtension(OpenXRAPI *p_openxr_api); + virtual ~OpenXRDisplayRefreshRateExtension() override; + + virtual void on_instance_created(const XrInstance p_instance) override; + virtual void on_instance_destroyed() override; + + float get_refresh_rate() const; + void set_refresh_rate(float p_refresh_rate); + + Array get_available_refresh_rates() const; + +private: + static OpenXRDisplayRefreshRateExtension *singleton; + + bool display_refresh_rate_ext = false; + + // OpenXR API call wrappers + EXT_PROTO_XRRESULT_FUNC4(xrEnumerateDisplayRefreshRatesFB, (XrSession), session, (uint32_t), displayRefreshRateCapacityInput, (uint32_t *), displayRefreshRateCountOutput, (float *), displayRefreshRates); + EXT_PROTO_XRRESULT_FUNC2(xrGetDisplayRefreshRateFB, (XrSession), session, (float *), display_refresh_rate); + EXT_PROTO_XRRESULT_FUNC2(xrRequestDisplayRefreshRateFB, (XrSession), session, (float), display_refresh_rate); +}; + +#endif // OPENXR_FB_DISPLAY_REFRESH_RATE_EXTENSION_H diff --git a/modules/openxr/extensions/openxr_htc_vive_tracker_extension.cpp b/modules/openxr/extensions/openxr_htc_vive_tracker_extension.cpp index 88cc7c061c..4d996e6283 100644 --- a/modules/openxr/extensions/openxr_htc_vive_tracker_extension.cpp +++ b/modules/openxr/extensions/openxr_htc_vive_tracker_extension.cpp @@ -69,6 +69,34 @@ bool OpenXRHTCViveTrackerExtension::on_event_polled(const XrEventDataBuffer &eve bool OpenXRHTCViveTrackerExtension::is_path_supported(const String &p_path) { if (p_path == "/interaction_profiles/htc/vive_tracker_htcx") { return available; + } else if (p_path == "/user/vive_tracker_htcx/role/handheld_object") { + return available; + } else if (p_path == "/user/vive_tracker_htcx/role/left_foot") { + return available; + } else if (p_path == "/user/vive_tracker_htcx/role/right_foot") { + return available; + } else if (p_path == "/user/vive_tracker_htcx/role/left_shoulder") { + return available; + } else if (p_path == "/user/vive_tracker_htcx/role/right_shoulder") { + return available; + } else if (p_path == "/user/vive_tracker_htcx/role/left_elbow") { + return available; + } else if (p_path == "/user/vive_tracker_htcx/role/right_elbow") { + return available; + } else if (p_path == "/user/vive_tracker_htcx/role/left_knee") { + return available; + } else if (p_path == "/user/vive_tracker_htcx/role/right_knee") { + return available; + } else if (p_path == "/user/vive_tracker_htcx/role/waist") { + return available; + } else if (p_path == "/user/vive_tracker_htcx/role/chest") { + return available; + } else if (p_path == "/user/vive_tracker_htcx/role/chest") { + return available; + } else if (p_path == "/user/vive_tracker_htcx/role/camera") { + return available; + } else if (p_path == "/user/vive_tracker_htcx/role/keyboard") { + return available; } // Not a path under this extensions control, so we return true; diff --git a/modules/openxr/extensions/openxr_vulkan_extension.cpp b/modules/openxr/extensions/openxr_vulkan_extension.cpp index f9e771c934..ee5bda2881 100644 --- a/modules/openxr/extensions/openxr_vulkan_extension.cpp +++ b/modules/openxr/extensions/openxr_vulkan_extension.cpp @@ -228,6 +228,12 @@ void OpenXRVulkanExtension::get_usable_swapchain_formats(Vector<int64_t> &p_usab p_usable_swap_chains.push_back(VK_FORMAT_B8G8R8A8_UINT); } +void OpenXRVulkanExtension::get_usable_depth_formats(Vector<int64_t> &p_usable_swap_chains) { + p_usable_swap_chains.push_back(VK_FORMAT_R32_SFLOAT); + p_usable_swap_chains.push_back(VK_FORMAT_D24_UNORM_S8_UINT); + p_usable_swap_chains.push_back(VK_FORMAT_D32_SFLOAT_S8_UINT); +} + bool OpenXRVulkanExtension::get_swapchain_image_data(XrSwapchain p_swapchain, int64_t p_swapchain_format, uint32_t p_width, uint32_t p_height, uint32_t p_sample_count, uint32_t p_array_size, void **r_swapchain_graphics_data) { XrSwapchainImageVulkanKHR *images = nullptr; @@ -271,7 +277,7 @@ bool OpenXRVulkanExtension::get_swapchain_image_data(XrSwapchain p_swapchain, in RenderingDevice::DataFormat format = RenderingDevice::DATA_FORMAT_R8G8B8A8_SRGB; RenderingDevice::TextureSamples samples = RenderingDevice::TEXTURE_SAMPLES_1; - uint64_t usage_flags = RenderingDevice::TEXTURE_USAGE_SAMPLING_BIT | RenderingDevice::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; + uint64_t usage_flags = RenderingDevice::TEXTURE_USAGE_SAMPLING_BIT; switch (p_swapchain_format) { case VK_FORMAT_R8G8B8A8_SRGB: @@ -283,16 +289,32 @@ bool OpenXRVulkanExtension::get_swapchain_image_data(XrSwapchain p_swapchain, in // will thus do an sRGB -> Linear conversion as expected. // format = RenderingDevice::DATA_FORMAT_R8G8B8A8_SRGB; format = RenderingDevice::DATA_FORMAT_R8G8B8A8_UNORM; + usage_flags |= RenderingDevice::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; break; case VK_FORMAT_B8G8R8A8_SRGB: // format = RenderingDevice::DATA_FORMAT_B8G8R8A8_SRGB; format = RenderingDevice::DATA_FORMAT_B8G8R8A8_UNORM; + usage_flags |= RenderingDevice::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; break; case VK_FORMAT_R8G8B8A8_UINT: format = RenderingDevice::DATA_FORMAT_R8G8B8A8_UINT; + usage_flags |= RenderingDevice::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; break; case VK_FORMAT_B8G8R8A8_UINT: format = RenderingDevice::DATA_FORMAT_B8G8R8A8_UINT; + usage_flags |= RenderingDevice::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; + break; + case VK_FORMAT_R32_SFLOAT: + format = RenderingDevice::DATA_FORMAT_R32_SFLOAT; + usage_flags |= RenderingDevice::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + break; + case VK_FORMAT_D24_UNORM_S8_UINT: + format = RenderingDevice::DATA_FORMAT_D24_UNORM_S8_UINT; + usage_flags |= RenderingDevice::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + break; + case VK_FORMAT_D32_SFLOAT_S8_UINT: + format = RenderingDevice::DATA_FORMAT_D32_SFLOAT_S8_UINT; + usage_flags |= RenderingDevice::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; break; default: // continue with our default value @@ -328,8 +350,7 @@ bool OpenXRVulkanExtension::get_swapchain_image_data(XrSwapchain p_swapchain, in break; } - Vector<RID> image_rids; - Vector<RID> framebuffers; + Vector<RID> texture_rids; // create Godot texture objects for each entry in our swapchain for (uint64_t i = 0; i < swapchain_length; i++) { @@ -344,19 +365,10 @@ bool OpenXRVulkanExtension::get_swapchain_image_data(XrSwapchain p_swapchain, in 1, p_array_size); - image_rids.push_back(image_rid); - - { - Vector<RID> fb; - fb.push_back(image_rid); - - RID fb_rid = rendering_device->framebuffer_create(fb, RenderingDevice::INVALID_ID, p_array_size); - framebuffers.push_back(fb_rid); - } + texture_rids.push_back(image_rid); } - data->image_rids = image_rids; - data->framebuffers = framebuffers; + data->texture_rids = texture_rids; memfree(images); @@ -370,33 +382,19 @@ bool OpenXRVulkanExtension::create_projection_fov(const XrFovf p_fov, double p_z for (int j = 0; j < 4; j++) { for (int i = 0; i < 4; i++) { - r_camera_matrix.matrix[j][i] = matrix.m[j * 4 + i]; + r_camera_matrix.columns[j][i] = matrix.m[j * 4 + i]; } } return true; } -bool OpenXRVulkanExtension::copy_render_target_to_image(RID p_from_render_target, void *p_swapchain_graphics_data, int p_image_index) { +RID OpenXRVulkanExtension::get_texture(void *p_swapchain_graphics_data, int p_image_index) { SwapchainGraphicsData *data = (SwapchainGraphicsData *)p_swapchain_graphics_data; - ERR_FAIL_NULL_V(data, false); - ERR_FAIL_COND_V(p_from_render_target.is_null(), false); + ERR_FAIL_NULL_V(data, RID()); - RID source_image = RendererRD::TextureStorage::get_singleton()->render_target_get_rd_texture(p_from_render_target); - ERR_FAIL_COND_V(source_image.is_null(), false); - - RID depth_image; // TODO implement - - ERR_FAIL_INDEX_V(p_image_index, data->framebuffers.size(), false); - RID fb = data->framebuffers[p_image_index]; - ERR_FAIL_COND_V(fb.is_null(), false); - - // Our vulkan extension can only be used in conjunction with our vulkan renderer. - RendererRD::CopyEffects *copy_effects = RendererRD::CopyEffects::get_singleton(); - ERR_FAIL_NULL_V(copy_effects, false); - copy_effects->copy_to_fb_rect(source_image, fb, Rect2i(), false, false, false, false, depth_image, data->is_multiview); - - return true; + ERR_FAIL_INDEX_V(p_image_index, data->texture_rids.size(), RID()); + return data->texture_rids[p_image_index]; } void OpenXRVulkanExtension::cleanup_swapchain_graphics_data(void **p_swapchain_graphics_data) { @@ -411,17 +409,11 @@ void OpenXRVulkanExtension::cleanup_swapchain_graphics_data(void **p_swapchain_g RenderingDevice *rendering_device = rendering_server->get_rendering_device(); ERR_FAIL_NULL(rendering_device); - for (int i = 0; i < data->image_rids.size(); i++) { - // This should clean up our RIDs and associated texture objects but shouldn't destroy the images, they are owned by our XrSwapchain - rendering_device->free(data->image_rids[i]); - } - data->image_rids.clear(); - - for (int i = 0; i < data->framebuffers.size(); i++) { + for (int i = 0; i < data->texture_rids.size(); i++) { // This should clean up our RIDs and associated texture objects but shouldn't destroy the images, they are owned by our XrSwapchain - rendering_device->free(data->framebuffers[i]); + rendering_device->free(data->texture_rids[i]); } - data->framebuffers.clear(); + data->texture_rids.clear(); memdelete(data); *p_swapchain_graphics_data = nullptr; diff --git a/modules/openxr/extensions/openxr_vulkan_extension.h b/modules/openxr/extensions/openxr_vulkan_extension.h index d6e9917900..71abe3b166 100644 --- a/modules/openxr/extensions/openxr_vulkan_extension.h +++ b/modules/openxr/extensions/openxr_vulkan_extension.h @@ -69,11 +69,12 @@ public: virtual bool create_vulkan_device(const VkDeviceCreateInfo *p_device_create_info, VkDevice *r_device) override; virtual void get_usable_swapchain_formats(Vector<int64_t> &p_usable_swap_chains) override; + virtual void get_usable_depth_formats(Vector<int64_t> &p_usable_swap_chains) override; virtual String get_swapchain_format_name(int64_t p_swapchain_format) const override; virtual bool get_swapchain_image_data(XrSwapchain p_swapchain, int64_t p_swapchain_format, uint32_t p_width, uint32_t p_height, uint32_t p_sample_count, uint32_t p_array_size, void **r_swapchain_graphics_data) override; virtual void cleanup_swapchain_graphics_data(void **p_swapchain_graphics_data) override; virtual bool create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, Projection &r_camera_matrix) override; - virtual bool copy_render_target_to_image(RID p_from_render_target, void *p_swapchain_graphics_data, int p_image_index) override; + virtual RID get_texture(void *p_swapchain_graphics_data, int p_image_index) override; private: static OpenXRVulkanExtension *singleton; @@ -81,8 +82,7 @@ private: struct SwapchainGraphicsData { bool is_multiview; - Vector<RID> image_rids; - Vector<RID> framebuffers; + Vector<RID> texture_rids; }; bool check_graphics_api_support(XrVersion p_desired_version); diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp index 16879ac4e5..1ff1dac512 100644 --- a/modules/openxr/openxr_api.cpp +++ b/modules/openxr/openxr_api.cpp @@ -49,6 +49,8 @@ #include "extensions/openxr_vulkan_extension.h" #endif +#include "extensions/openxr_composition_layer_depth_extension.h" +#include "extensions/openxr_fb_display_refresh_rate_extension.h" #include "extensions/openxr_fb_passthrough_extension_wrapper.h" #include "extensions/openxr_hand_tracking_extension.h" #include "extensions/openxr_htc_vive_tracker_extension.h" @@ -130,11 +132,9 @@ bool OpenXRAPI::load_layer_properties() { result = xrEnumerateApiLayerProperties(num_layer_properties, &num_layer_properties, layer_properties); ERR_FAIL_COND_V_MSG(XR_FAILED(result), false, "OpenXR: Failed to enumerate api layer properties"); -#ifdef DEBUG for (uint32_t i = 0; i < num_layer_properties; i++) { - print_line("OpenXR: Found OpenXR layer ", layer_properties[i].layerName); + print_verbose(String("OpenXR: Found OpenXR layer ") + layer_properties[i].layerName); } -#endif return true; } @@ -162,11 +162,9 @@ bool OpenXRAPI::load_supported_extensions() { result = xrEnumerateInstanceExtensionProperties(nullptr, num_supported_extensions, &num_supported_extensions, supported_extensions); ERR_FAIL_COND_V_MSG(XR_FAILED(result), false, "OpenXR: Failed to enumerate extension properties"); -#ifdef DEBUG for (uint32_t i = 0; i < num_supported_extensions; i++) { - print_line("OpenXR: Found OpenXR extension ", supported_extensions[i].extensionName); + print_verbose(String("OpenXR: Found OpenXR extension ") + supported_extensions[i].extensionName); } -#endif return true; } @@ -174,17 +172,10 @@ bool OpenXRAPI::load_supported_extensions() { bool OpenXRAPI::is_extension_supported(const String &p_extension) const { for (uint32_t i = 0; i < num_supported_extensions; i++) { if (supported_extensions[i].extensionName == p_extension) { -#ifdef DEBUG - print_line("OpenXR: requested extension", p_extension, "is supported"); -#endif return true; } } -#ifdef DEBUG - print_line("OpenXR: requested extension", p_extension, "is not supported"); -#endif - return false; } @@ -244,7 +235,7 @@ bool OpenXRAPI::create_instance() { if (!is_extension_supported(requested_extension.key)) { if (requested_extension.value == nullptr) { // nullptr means this is a manditory extension so we fail - ERR_FAIL_V_MSG(false, "OpenXR: OpenXR Runtime does not support OpenGL extension!"); + ERR_FAIL_V_MSG(false, String("OpenXR: OpenXR Runtime does not support ") + requested_extension.key + String(" extension!")); } else { // set this extension as not supported *requested_extension.value = false; @@ -400,11 +391,9 @@ bool OpenXRAPI::load_supported_view_configuration_types() { result = xrEnumerateViewConfigurations(instance, system_id, num_view_configuration_types, &num_view_configuration_types, supported_view_configuration_types); ERR_FAIL_COND_V_MSG(XR_FAILED(result), false, "OpenXR: Failed to enumerateview configurations"); -#ifdef DEBUG for (uint32_t i = 0; i < num_view_configuration_types; i++) { - print_line("OpenXR: Found supported view configuration ", OpenXRUtil::get_view_configuration_name(supported_view_configuration_types[i])); + print_verbose(String("OpenXR: Found supported view configuration ") + OpenXRUtil::get_view_configuration_name(supported_view_configuration_types[i])); } -#endif return true; } @@ -453,17 +442,15 @@ bool OpenXRAPI::load_supported_view_configuration_views(XrViewConfigurationType result = xrEnumerateViewConfigurationViews(instance, system_id, p_configuration_type, view_count, &view_count, view_configuration_views); ERR_FAIL_COND_V_MSG(XR_FAILED(result), false, "OpenXR: Failed to enumerate view configurations"); -#ifdef DEBUG for (uint32_t i = 0; i < view_count; i++) { - print_line("OpenXR: Found supported view configuration view"); - print_line(" - width: ", view_configuration_views[i].maxImageRectWidth); - print_line(" - height: ", view_configuration_views[i].maxImageRectHeight); - print_line(" - sample count: ", view_configuration_views[i].maxSwapchainSampleCount); - print_line(" - recommended render width: ", view_configuration_views[i].recommendedImageRectWidth); - print_line(" - recommended render height: ", view_configuration_views[i].recommendedImageRectHeight); - print_line(" - recommended render sample count: ", view_configuration_views[i].recommendedSwapchainSampleCount); + print_verbose("OpenXR: Found supported view configuration view"); + print_verbose(String(" - width: ") + itos(view_configuration_views[i].maxImageRectWidth)); + print_verbose(String(" - height: ") + itos(view_configuration_views[i].maxImageRectHeight)); + print_verbose(String(" - sample count: ") + itos(view_configuration_views[i].maxSwapchainSampleCount)); + print_verbose(String(" - recommended render width: ") + itos(view_configuration_views[i].recommendedImageRectWidth)); + print_verbose(String(" - recommended render height: ") + itos(view_configuration_views[i].recommendedImageRectHeight)); + print_verbose(String(" - recommended render sample count: ") + itos(view_configuration_views[i].recommendedSwapchainSampleCount)); } -#endif return true; } @@ -545,11 +532,9 @@ bool OpenXRAPI::load_supported_reference_spaces() { result = xrEnumerateReferenceSpaces(session, num_reference_spaces, &num_reference_spaces, supported_reference_spaces); ERR_FAIL_COND_V_MSG(XR_FAILED(result), false, "OpenXR: Failed to enumerate reference spaces"); - // #ifdef DEBUG for (uint32_t i = 0; i < num_reference_spaces; i++) { - print_line("OpenXR: Found supported reference space ", OpenXRUtil::get_reference_space_name(supported_reference_spaces[i])); + print_verbose(String("OpenXR: Found supported reference space ") + OpenXRUtil::get_reference_space_name(supported_reference_spaces[i])); } - // #endif return true; } @@ -642,11 +627,9 @@ bool OpenXRAPI::load_supported_swapchain_formats() { result = xrEnumerateSwapchainFormats(session, num_swapchain_formats, &num_swapchain_formats, supported_swapchain_formats); ERR_FAIL_COND_V_MSG(XR_FAILED(result), false, "OpenXR: Failed to enumerate swapchain formats"); - // #ifdef DEBUG for (uint32_t i = 0; i < num_swapchain_formats; i++) { - print_line("OpenXR: Found supported swapchain format ", get_swapchain_format_name(supported_swapchain_formats[i])); + print_verbose(String("OpenXR: Found supported swapchain format ") + get_swapchain_format_name(supported_swapchain_formats[i])); } - // #endif return true; } @@ -663,7 +646,7 @@ bool OpenXRAPI::is_swapchain_format_supported(int64_t p_swapchain_format) { return false; } -bool OpenXRAPI::create_main_swapchain() { +bool OpenXRAPI::create_swapchains() { ERR_FAIL_NULL_V(graphics_extension, false); ERR_FAIL_COND_V(session == XR_NULL_HANDLE, false); @@ -681,34 +664,36 @@ bool OpenXRAPI::create_main_swapchain() { already rendering the next frame. Finally an area we need to expand upon is that Foveated rendering is only enabled for the swap chain we create, - as we render 3D content into internal buffers that are copied into the swapchain, we don't get any of the performance gains - until such time as we implement VRS. + as we render 3D content into internal buffers that are copied into the swapchain, we do now have (basic) VRS support */ - // Build a vector with swapchain formats we want to use, from best fit to worst - Vector<int64_t> usable_swapchain_formats; - int64_t swapchain_format_to_use = 0; + Size2 recommended_size = get_recommended_target_size(); - graphics_extension->get_usable_swapchain_formats(usable_swapchain_formats); + // We start with our color swapchain... + { + // Build a vector with swapchain formats we want to use, from best fit to worst + Vector<int64_t> usable_swapchain_formats; + int64_t swapchain_format_to_use = 0; - // now find out which one is supported - for (int i = 0; i < usable_swapchain_formats.size() && swapchain_format_to_use == 0; i++) { - if (is_swapchain_format_supported(usable_swapchain_formats[i])) { - swapchain_format_to_use = usable_swapchain_formats[i]; - } - } + graphics_extension->get_usable_swapchain_formats(usable_swapchain_formats); - if (swapchain_format_to_use == 0) { - swapchain_format_to_use = usable_swapchain_formats[0]; // just use the first one and hope for the best... - print_line("Couldn't find usable swap chain format, using", get_swapchain_format_name(swapchain_format_to_use), "instead."); - } else { - print_line("Using swap chain format:", get_swapchain_format_name(swapchain_format_to_use)); - } + // now find out which one is supported + for (int i = 0; i < usable_swapchain_formats.size() && swapchain_format_to_use == 0; i++) { + if (is_swapchain_format_supported(usable_swapchain_formats[i])) { + swapchain_format_to_use = usable_swapchain_formats[i]; + } + } - Size2 recommended_size = get_recommended_target_size(); + if (swapchain_format_to_use == 0) { + swapchain_format_to_use = usable_swapchain_formats[0]; // just use the first one and hope for the best... + print_line("Couldn't find usable color swap chain format, using", get_swapchain_format_name(swapchain_format_to_use), "instead."); + } else { + print_verbose(String("Using color swap chain format:") + get_swapchain_format_name(swapchain_format_to_use)); + } - if (!create_swapchain(swapchain_format_to_use, recommended_size.width, recommended_size.height, view_configuration_views[0].recommendedSwapchainSampleCount, view_count, swapchain, &swapchain_graphics_data)) { - return false; + if (!create_swapchain(XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT, swapchain_format_to_use, recommended_size.width, recommended_size.height, view_configuration_views[0].recommendedSwapchainSampleCount, view_count, swapchains[OPENXR_SWAPCHAIN_COLOR].swapchain, &swapchains[OPENXR_SWAPCHAIN_COLOR].swapchain_graphics_data)) { + return false; + } } views = (XrView *)memalloc(sizeof(XrView) * view_count); @@ -717,18 +702,73 @@ bool OpenXRAPI::create_main_swapchain() { projection_views = (XrCompositionLayerProjectionView *)memalloc(sizeof(XrCompositionLayerProjectionView) * view_count); ERR_FAIL_NULL_V_MSG(projection_views, false, "OpenXR Couldn't allocate memory for projection views"); + // We create our depth swapchain if: + // - we support our depth layer extension + // - we have our spacewarp extension (not yet implemented) + if (OpenXRCompositionLayerDepthExtension::get_singleton()->is_available()) { + // Build a vector with swapchain formats we want to use, from best fit to worst + Vector<int64_t> usable_swapchain_formats; + int64_t swapchain_format_to_use = 0; + + graphics_extension->get_usable_depth_formats(usable_swapchain_formats); + + // now find out which one is supported + for (int i = 0; i < usable_swapchain_formats.size() && swapchain_format_to_use == 0; i++) { + if (is_swapchain_format_supported(usable_swapchain_formats[i])) { + swapchain_format_to_use = usable_swapchain_formats[i]; + } + } + + if (swapchain_format_to_use == 0) { + swapchain_format_to_use = usable_swapchain_formats[0]; // just use the first one and hope for the best... + print_line("Couldn't find usable depth swap chain format, using", get_swapchain_format_name(swapchain_format_to_use), "instead."); + } else { + print_verbose(String("Using depth swap chain format:") + get_swapchain_format_name(swapchain_format_to_use)); + } + + if (!create_swapchain(XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, swapchain_format_to_use, recommended_size.width, recommended_size.height, view_configuration_views[0].recommendedSwapchainSampleCount, view_count, swapchains[OPENXR_SWAPCHAIN_DEPTH].swapchain, &swapchains[OPENXR_SWAPCHAIN_DEPTH].swapchain_graphics_data)) { + return false; + } + + depth_views = (XrCompositionLayerDepthInfoKHR *)memalloc(sizeof(XrCompositionLayerDepthInfoKHR) * view_count); + ERR_FAIL_NULL_V_MSG(depth_views, false, "OpenXR Couldn't allocate memory for depth views"); + } + + // We create our velocity swapchain if: + // - we have our spacewarp extension (not yet implemented) + { + // TBD + } + for (uint32_t i = 0; i < view_count; i++) { views[i].type = XR_TYPE_VIEW; views[i].next = nullptr; projection_views[i].type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW; projection_views[i].next = nullptr; - projection_views[i].subImage.swapchain = swapchain; + projection_views[i].subImage.swapchain = swapchains[OPENXR_SWAPCHAIN_COLOR].swapchain; projection_views[i].subImage.imageArrayIndex = i; projection_views[i].subImage.imageRect.offset.x = 0; projection_views[i].subImage.imageRect.offset.y = 0; projection_views[i].subImage.imageRect.extent.width = recommended_size.width; projection_views[i].subImage.imageRect.extent.height = recommended_size.height; + + if (OpenXRCompositionLayerDepthExtension::get_singleton()->is_available() && depth_views) { + projection_views[i].next = &depth_views[i]; + + depth_views[i].type = XR_TYPE_COMPOSITION_LAYER_DEPTH_INFO_KHR; + depth_views[i].next = nullptr; + depth_views[i].subImage.swapchain = swapchains[OPENXR_SWAPCHAIN_DEPTH].swapchain; + depth_views[i].subImage.imageArrayIndex = 0; + depth_views[i].subImage.imageRect.offset.x = 0; + depth_views[i].subImage.imageRect.offset.y = 0; + depth_views[i].subImage.imageRect.extent.width = recommended_size.width; + depth_views[i].subImage.imageRect.extent.height = recommended_size.height; + depth_views[i].minDepth = 0.0; + depth_views[i].maxDepth = 1.0; + depth_views[i].nearZ = 0.01; // Near and far Z will be set to the correct values in fill_projection_matrix + depth_views[i].farZ = 100.0; + } }; return true; @@ -740,7 +780,7 @@ void OpenXRAPI::destroy_session() { } if (graphics_extension) { - graphics_extension->cleanup_swapchain_graphics_data(&swapchain_graphics_data); + graphics_extension->cleanup_swapchain_graphics_data(&swapchains[OPENXR_SWAPCHAIN_COLOR].swapchain_graphics_data); } if (views != nullptr) { @@ -753,9 +793,16 @@ void OpenXRAPI::destroy_session() { projection_views = nullptr; } - if (swapchain != XR_NULL_HANDLE) { - xrDestroySwapchain(swapchain); - swapchain = XR_NULL_HANDLE; + if (depth_views != nullptr) { + memfree(depth_views); + depth_views = nullptr; + } + + for (int i = 0; i < OPENXR_SWAPCHAIN_MAX; i++) { + if (swapchains[i].swapchain != XR_NULL_HANDLE) { + xrDestroySwapchain(swapchains[i].swapchain); + swapchains[i].swapchain = XR_NULL_HANDLE; + } } if (supported_swapchain_formats != nullptr) { @@ -789,7 +836,7 @@ void OpenXRAPI::destroy_session() { } } -bool OpenXRAPI::create_swapchain(int64_t p_swapchain_format, uint32_t p_width, uint32_t p_height, uint32_t p_sample_count, uint32_t p_array_size, XrSwapchain &r_swapchain, void **r_swapchain_graphics_data) { +bool OpenXRAPI::create_swapchain(XrSwapchainUsageFlags p_usage_flags, int64_t p_swapchain_format, uint32_t p_width, uint32_t p_height, uint32_t p_sample_count, uint32_t p_array_size, XrSwapchain &r_swapchain, void **r_swapchain_graphics_data) { ERR_FAIL_COND_V(session == XR_NULL_HANDLE, false); ERR_FAIL_NULL_V(graphics_extension, false); @@ -807,7 +854,7 @@ bool OpenXRAPI::create_swapchain(int64_t p_swapchain_format, uint32_t p_width, u XR_TYPE_SWAPCHAIN_CREATE_INFO, // type next_pointer, // next 0, // createFlags - XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT, // usageFlags + p_usage_flags, // usageFlags p_swapchain_format, // format p_sample_count, // sampleCount p_width, // width @@ -835,9 +882,7 @@ bool OpenXRAPI::create_swapchain(int64_t p_swapchain_format, uint32_t p_width, u } bool OpenXRAPI::on_state_idle() { -#ifdef DEBUG - print_line("On state idle"); -#endif + print_verbose("On state idle"); for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) { wrapper->on_state_idle(); @@ -847,9 +892,7 @@ bool OpenXRAPI::on_state_idle() { } bool OpenXRAPI::on_state_ready() { -#ifdef DEBUG - print_line("On state ready"); -#endif + print_verbose("On state ready"); // begin session XrSessionBeginInfo session_begin_info = { @@ -871,7 +914,7 @@ bool OpenXRAPI::on_state_ready() { // That will be very very ugly // The other possibility is to create a separate OpenXRViewport type specifically for this goal as part of our OpenXR module - if (!create_main_swapchain()) { + if (!create_swapchains()) { return false; } @@ -892,9 +935,7 @@ bool OpenXRAPI::on_state_ready() { } bool OpenXRAPI::on_state_synchronized() { -#ifdef DEBUG - print_line("On state synchronized"); -#endif + print_verbose("On state synchronized"); // Just in case, see if we already have active trackers... List<RID> trackers; @@ -911,9 +952,7 @@ bool OpenXRAPI::on_state_synchronized() { } bool OpenXRAPI::on_state_visible() { -#ifdef DEBUG - print_line("On state visible"); -#endif + print_verbose("On state visible"); for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) { wrapper->on_state_visible(); @@ -927,9 +966,7 @@ bool OpenXRAPI::on_state_visible() { } bool OpenXRAPI::on_state_focused() { -#ifdef DEBUG - print_line("On state focused"); -#endif + print_verbose("On state focused"); for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) { wrapper->on_state_focused(); @@ -943,9 +980,7 @@ bool OpenXRAPI::on_state_focused() { } bool OpenXRAPI::on_state_stopping() { -#ifdef DEBUG - print_line("On state stopping"); -#endif + print_verbose("On state stopping"); if (xr_interface) { xr_interface->on_state_stopping(); @@ -971,9 +1006,7 @@ bool OpenXRAPI::on_state_stopping() { } bool OpenXRAPI::on_state_loss_pending() { -#ifdef DEBUG - print_line("On state loss pending"); -#endif + print_verbose("On state loss pending"); for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) { wrapper->on_state_loss_pending(); @@ -985,9 +1018,7 @@ bool OpenXRAPI::on_state_loss_pending() { } bool OpenXRAPI::on_state_exiting() { -#ifdef DEBUG - print_line("On state existing"); -#endif + print_verbose("On state existing"); for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) { wrapper->on_state_exiting(); @@ -1259,12 +1290,10 @@ XRPose::TrackingConfidence OpenXRAPI::get_head_center(Transform3D &r_transform, head_pose_confidence = confidence; if (head_pose_confidence == XRPose::XR_TRACKING_CONFIDENCE_NONE) { print_line("OpenXR head space location not valid (check tracking?)"); -#ifdef DEBUG } else if (head_pose_confidence == XRPose::XR_TRACKING_CONFIDENCE_LOW) { - print_line("OpenVR Head pose now tracking with low confidence"); + print_verbose("OpenVR Head pose now tracking with low confidence"); } else { - print_line("OpenVR Head pose now tracking with high confidence"); -#endif + print_verbose("OpenVR Head pose now tracking with high confidence"); } } @@ -1304,6 +1333,15 @@ bool OpenXRAPI::get_view_projection(uint32_t p_view, double p_z_near, double p_z return false; } + // if we're using depth views, make sure we update our near and far there... + if (depth_views != nullptr) { + for (uint32_t i = 0; i < view_count; i++) { + depth_views[i].nearZ = p_z_near; + depth_views[i].farZ = p_z_far; + } + } + + // now update our projection return graphics_extension->create_projection_fov(views[p_view].fov, p_z_near, p_z_far, p_camera_matrix); } @@ -1343,7 +1381,7 @@ bool OpenXRAPI::poll_events() { // TODO We get this event if we're about to loose our OpenXR instance. // We should queue exiting Godot at this point. - print_verbose("OpenXR EVENT: instance loss pending at " + itos(event->lossTime)); + print_verbose(String("OpenXR EVENT: instance loss pending at ") + itos(event->lossTime)); return false; } break; case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: { @@ -1351,9 +1389,9 @@ bool OpenXRAPI::poll_events() { session_state = event->state; if (session_state >= XR_SESSION_STATE_MAX_ENUM) { - print_verbose("OpenXR EVENT: session state changed to UNKNOWN - " + itos(session_state)); + print_verbose(String("OpenXR EVENT: session state changed to UNKNOWN - ") + itos(session_state)); } else { - print_verbose("OpenXR EVENT: session state changed to " + OpenXRUtil::get_session_state_name(session_state)); + print_verbose(String("OpenXR EVENT: session state changed to ") + OpenXRUtil::get_session_state_name(session_state)); switch (session_state) { case XR_SESSION_STATE_IDLE: @@ -1388,7 +1426,7 @@ bool OpenXRAPI::poll_events() { case XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING: { XrEventDataReferenceSpaceChangePending *event = (XrEventDataReferenceSpaceChangePending *)&runtimeEvent; - print_verbose("OpenXR EVENT: reference space type " + OpenXRUtil::get_reference_space_name(event->referenceSpaceType) + " change pending!"); + print_verbose(String("OpenXR EVENT: reference space type ") + OpenXRUtil::get_reference_space_name(event->referenceSpaceType) + " change pending!"); if (event->poseValid && xr_interface) { xr_interface->on_pose_recentered(); } @@ -1407,7 +1445,7 @@ bool OpenXRAPI::poll_events() { } break; default: if (!handled) { - print_verbose("OpenXR Unhandled event type " + OpenXRUtil::get_structure_type_name(runtimeEvent.type)); + print_verbose(String("OpenXR Unhandled event type ") + OpenXRUtil::get_structure_type_name(runtimeEvent.type)); } break; } @@ -1442,15 +1480,15 @@ bool OpenXRAPI::process() { return true; } -bool OpenXRAPI::acquire_image(XrSwapchain p_swapchain, uint32_t &r_image_index) { - ERR_FAIL_COND_V(image_acquired, true); // this was not released when it should be, error out and re-use... +bool OpenXRAPI::acquire_image(OpenXRSwapChainInfo &p_swapchain) { + ERR_FAIL_COND_V(p_swapchain.image_acquired, true); // this was not released when it should be, error out and re-use... XrResult result; XrSwapchainImageAcquireInfo swapchain_image_acquire_info = { XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO, // type nullptr // next }; - result = xrAcquireSwapchainImage(p_swapchain, &swapchain_image_acquire_info, &r_image_index); + result = xrAcquireSwapchainImage(p_swapchain.swapchain, &swapchain_image_acquire_info, &p_swapchain.image_index); if (XR_FAILED(result)) { print_line("OpenXR: failed to acquire swapchain image [", get_error_string(result), "]"); return false; @@ -1462,7 +1500,7 @@ bool OpenXRAPI::acquire_image(XrSwapchain p_swapchain, uint32_t &r_image_index) 17000000 // timeout in nanoseconds }; - result = xrWaitSwapchainImage(p_swapchain, &swapchain_image_wait_info); + result = xrWaitSwapchainImage(p_swapchain.swapchain, &swapchain_image_wait_info); if (XR_FAILED(result)) { print_line("OpenXR: failed to wait for swapchain image [", get_error_string(result), "]"); return false; @@ -1471,12 +1509,12 @@ bool OpenXRAPI::acquire_image(XrSwapchain p_swapchain, uint32_t &r_image_index) return true; } -bool OpenXRAPI::release_image(XrSwapchain p_swapchain) { +bool OpenXRAPI::release_image(OpenXRSwapChainInfo &p_swapchain) { XrSwapchainImageReleaseInfo swapchain_image_release_info = { XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO, // type nullptr // next }; - XrResult result = xrReleaseSwapchainImage(swapchain, &swapchain_image_release_info); + XrResult result = xrReleaseSwapchainImage(p_swapchain.swapchain, &swapchain_image_release_info); if (XR_FAILED(result)) { print_line("OpenXR: failed to release swapchain image! [", get_error_string(result), "]"); return false; @@ -1515,7 +1553,7 @@ void OpenXRAPI::pre_render() { if (frame_state.predictedDisplayPeriod > 500000000) { // display period more then 0.5 seconds? must be wrong data - print_verbose("OpenXR resetting invalid display period " + rtos(frame_state.predictedDisplayPeriod)); + print_verbose(String("OpenXR resetting invalid display period ") + rtos(frame_state.predictedDisplayPeriod)); frame_state.predictedDisplayPeriod = 0; } @@ -1562,13 +1600,11 @@ void OpenXRAPI::pre_render() { } if (view_pose_valid != pose_valid) { view_pose_valid = pose_valid; -#ifdef DEBUG if (!view_pose_valid) { - print_line("OpenXR View pose became invalid"); + print_verbose("OpenXR View pose became invalid"); } else { - print_line("OpenXR View pose became valid"); + print_verbose("OpenXR View pose became valid"); } -#endif } // let's start our frame.. @@ -1590,28 +1626,41 @@ bool OpenXRAPI::pre_draw_viewport(RID p_render_target) { // TODO: at some point in time we may support multiple viewports in which case we need to handle that... + // Acquire our images + for (int i = 0; i < OPENXR_SWAPCHAIN_MAX; i++) { + if (!swapchains[i].image_acquired && swapchains[i].swapchain != XR_NULL_HANDLE) { + if (!acquire_image(swapchains[i])) { + return false; + } + swapchains[i].image_acquired = true; + } + } + return true; } +RID OpenXRAPI::get_color_texture() { + if (swapchains[OPENXR_SWAPCHAIN_COLOR].image_acquired) { + return graphics_extension->get_texture(swapchains[OPENXR_SWAPCHAIN_COLOR].swapchain_graphics_data, swapchains[OPENXR_SWAPCHAIN_COLOR].image_index); + } else { + return RID(); + } +} + +RID OpenXRAPI::get_depth_texture() { + if (swapchains[OPENXR_SWAPCHAIN_DEPTH].image_acquired) { + return graphics_extension->get_texture(swapchains[OPENXR_SWAPCHAIN_DEPTH].swapchain_graphics_data, swapchains[OPENXR_SWAPCHAIN_DEPTH].image_index); + } else { + return RID(); + } +} + void OpenXRAPI::post_draw_viewport(RID p_render_target) { if (!can_render()) { return; } - // TODO: at some point in time we may support multiple viewports in which case we need to handle that... - - // TODO: if we can get PR 51179 to work properly we can change away from this approach and move this into get_external_texture or something - if (!image_acquired) { - if (!acquire_image(swapchain, image_index)) { - return; - } - image_acquired = true; - - // print_line("OpenXR: acquired image " + itos(image_index) + ", copying..."); - - // Copy our buffer into our swap chain (remove once PR 51179 is done) - graphics_extension->copy_render_target_to_image(p_render_target, swapchain_graphics_data, image_index); - } + // Nothing to do here at this point in time... }; void OpenXRAPI::end_frame() { @@ -1623,7 +1672,7 @@ void OpenXRAPI::end_frame() { return; } - if (frame_state.shouldRender && view_pose_valid && !image_acquired) { + if (frame_state.shouldRender && view_pose_valid && !swapchains[OPENXR_SWAPCHAIN_COLOR].image_acquired) { print_line("OpenXR: No viewport was marked with use_xr, there is no rendered output!"); } @@ -1631,7 +1680,7 @@ void OpenXRAPI::end_frame() { // - shouldRender set to true // - a valid view pose for projection_views[eye].pose to submit layer // - an image to render - if (!frame_state.shouldRender || !view_pose_valid || !image_acquired) { + if (!frame_state.shouldRender || !view_pose_valid || !swapchains[OPENXR_SWAPCHAIN_COLOR].image_acquired) { // submit 0 layers when we shouldn't render XrFrameEndInfo frame_end_info = { XR_TYPE_FRAME_END_INFO, // type @@ -1652,10 +1701,12 @@ void OpenXRAPI::end_frame() { } // release our swapchain image if we acquired it - if (image_acquired) { - image_acquired = false; // whether we succeed or not, consider this released. + for (int i = 0; i < OPENXR_SWAPCHAIN_MAX; i++) { + if (swapchains[i].image_acquired) { + swapchains[i].image_acquired = false; // whether we succeed or not, consider this released. - release_image(swapchain); + release_image(swapchains[i]); + } } for (uint32_t eye = 0; eye < view_count; eye++) { @@ -1698,6 +1749,31 @@ void OpenXRAPI::end_frame() { } } +float OpenXRAPI::get_display_refresh_rate() const { + OpenXRDisplayRefreshRateExtension *drrext = OpenXRDisplayRefreshRateExtension::get_singleton(); + if (drrext) { + return drrext->get_refresh_rate(); + } + + return 0.0; +} + +void OpenXRAPI::set_display_refresh_rate(float p_refresh_rate) { + OpenXRDisplayRefreshRateExtension *drrext = OpenXRDisplayRefreshRateExtension::get_singleton(); + if (drrext != nullptr) { + drrext->set_refresh_rate(p_refresh_rate); + } +} + +Array OpenXRAPI::get_available_display_refresh_rates() const { + OpenXRDisplayRefreshRateExtension *drrext = OpenXRDisplayRefreshRateExtension::get_singleton(); + if (drrext != nullptr) { + return drrext->get_available_refresh_rates(); + } + + return Array(); +} + OpenXRAPI::OpenXRAPI() { // OpenXRAPI is only constructed if OpenXR is enabled. singleton = this; @@ -1763,9 +1839,11 @@ OpenXRAPI::OpenXRAPI() { // register our other extensions register_extension_wrapper(memnew(OpenXRPalmPoseExtension(this))); + register_extension_wrapper(memnew(OpenXRCompositionLayerDepthExtension(this))); register_extension_wrapper(memnew(OpenXRHTCViveTrackerExtension(this))); register_extension_wrapper(memnew(OpenXRHandTrackingExtension(this))); register_extension_wrapper(memnew(OpenXRFbPassthroughExtensionWrapper(this))); + register_extension_wrapper(memnew(OpenXRDisplayRefreshRateExtension(this))); } OpenXRAPI::~OpenXRAPI() { @@ -1991,8 +2069,6 @@ RID OpenXRAPI::action_set_create(const String p_name, const String p_localized_n copy_string_to_char_buffer(p_name, action_set_info.actionSetName, XR_MAX_ACTION_SET_NAME_SIZE); copy_string_to_char_buffer(p_localized_name, action_set_info.localizedActionSetName, XR_MAX_LOCALIZED_ACTION_SET_NAME_SIZE); - // print_line("Creating action set ", action_set_info.actionSetName, " - ", action_set_info.localizedActionSetName, " (", itos(action_set_info.priority), ")"); - XrResult result = xrCreateActionSet(instance, &action_set_info, &action_set.handle); if (XR_FAILED(result)) { print_line("OpenXR: failed to create action set ", p_name, "! [", get_error_string(result), "]"); @@ -2146,8 +2222,6 @@ RID OpenXRAPI::action_create(RID p_action_set, const String p_name, const String copy_string_to_char_buffer(p_name, action_info.actionName, XR_MAX_ACTION_NAME_SIZE); copy_string_to_char_buffer(p_localized_name, action_info.localizedActionName, XR_MAX_LOCALIZED_ACTION_NAME_SIZE); - // print_line("Creating action ", action_info.actionName, action_info.localizedActionName, action_info.countSubactionPaths); - XrResult result = xrCreateAction(action_set->handle, &action_info, &action.handle); if (XR_FAILED(result)) { print_line("OpenXR: failed to create action ", p_name, "! [", get_error_string(result), "]"); diff --git a/modules/openxr/openxr_api.h b/modules/openxr/openxr_api.h index 316886239e..5dce749351 100644 --- a/modules/openxr/openxr_api.h +++ b/modules/openxr/openxr_api.h @@ -84,8 +84,6 @@ private: bool ext_vive_focus3_available = false; bool ext_huawei_controller_available = false; - bool is_path_supported(const String &p_path); - // composition layer providers Vector<OpenXRCompositionLayerProvider *> composition_layer_providers; @@ -120,15 +118,28 @@ private: OpenXRGraphicsExtensionWrapper *graphics_extension = nullptr; XrSystemGraphicsProperties graphics_properties; - void *swapchain_graphics_data = nullptr; - uint32_t image_index = 0; - bool image_acquired = false; uint32_t view_count = 0; XrViewConfigurationView *view_configuration_views = nullptr; XrView *views = nullptr; XrCompositionLayerProjectionView *projection_views = nullptr; - XrSwapchain swapchain = XR_NULL_HANDLE; + XrCompositionLayerDepthInfoKHR *depth_views = nullptr; // Only used by Composition Layer Depth Extension if available + + enum OpenXRSwapChainTypes { + OPENXR_SWAPCHAIN_COLOR, + OPENXR_SWAPCHAIN_DEPTH, + // OPENXR_SWAPCHAIN_VELOCITY, + OPENXR_SWAPCHAIN_MAX + }; + + struct OpenXRSwapChainInfo { + XrSwapchain swapchain = XR_NULL_HANDLE; + void *swapchain_graphics_data = nullptr; + uint32_t image_index = 0; + bool image_acquired = false; + }; + + OpenXRSwapChainInfo swapchains[OPENXR_SWAPCHAIN_MAX]; XrSpace play_space = XR_NULL_HANDLE; XrSpace view_space = XR_NULL_HANDLE; @@ -212,13 +223,13 @@ private: bool setup_spaces(); bool load_supported_swapchain_formats(); bool is_swapchain_format_supported(int64_t p_swapchain_format); - bool create_main_swapchain(); + bool create_swapchains(); void destroy_session(); // swapchains - bool create_swapchain(int64_t p_swapchain_format, uint32_t p_width, uint32_t p_height, uint32_t p_sample_count, uint32_t p_array_size, XrSwapchain &r_swapchain, void **r_swapchain_graphics_data); - bool acquire_image(XrSwapchain p_swapchain, uint32_t &r_image_index); - bool release_image(XrSwapchain p_swapchain); + bool create_swapchain(XrSwapchainUsageFlags p_usage_flags, int64_t p_swapchain_format, uint32_t p_width, uint32_t p_height, uint32_t p_sample_count, uint32_t p_array_size, XrSwapchain &r_swapchain, void **r_swapchain_graphics_data); + bool acquire_image(OpenXRSwapChainInfo &p_swapchain); + bool release_image(OpenXRSwapChainInfo &p_swapchain); // action map struct Tracker { // Trackers represent tracked physical objects such as controllers, pucks, etc. @@ -289,6 +300,7 @@ public: void parse_velocities(const XrSpaceVelocity &p_velocity, Vector3 &r_linear_velocity, Vector3 &r_angular_velocity); bool xr_result(XrResult result, const char *format, Array args = Array()) const; + bool is_path_supported(const String &p_path); static bool openxr_is_enabled(bool p_check_run_in_editor = true); static OpenXRAPI *get_singleton(); @@ -318,9 +330,16 @@ public: void pre_render(); bool pre_draw_viewport(RID p_render_target); + RID get_color_texture(); + RID get_depth_texture(); void post_draw_viewport(RID p_render_target); void end_frame(); + // Display refresh rate + float get_display_refresh_rate() const; + void set_display_refresh_rate(float p_refresh_rate); + Array get_available_display_refresh_rates() const; + // action map String get_default_action_map_resource_name(); diff --git a/modules/openxr/openxr_interface.cpp b/modules/openxr/openxr_interface.cpp index 01e148e00f..bdf437b0b7 100644 --- a/modules/openxr/openxr_interface.cpp +++ b/modules/openxr/openxr_interface.cpp @@ -41,6 +41,13 @@ void OpenXRInterface::_bind_methods() { ADD_SIGNAL(MethodInfo("session_focussed")); ADD_SIGNAL(MethodInfo("session_visible")); ADD_SIGNAL(MethodInfo("pose_recentered")); + + // Display refresh rate + ClassDB::bind_method(D_METHOD("get_display_refresh_rate"), &OpenXRInterface::get_display_refresh_rate); + ClassDB::bind_method(D_METHOD("set_display_refresh_rate", "refresh_rate"), &OpenXRInterface::set_display_refresh_rate); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "display_refresh_rate"), "set_display_refresh_rate", "get_display_refresh_rate"); + + ClassDB::bind_method(D_METHOD("get_available_display_refresh_rates"), &OpenXRInterface::get_available_display_refresh_rates); } StringName OpenXRInterface::get_name() const { @@ -132,10 +139,10 @@ void OpenXRInterface::_load_action_map() { if (action_map.is_valid()) { HashMap<Ref<OpenXRAction>, Action *> xr_actions; - Array action_sets = action_map->get_action_sets(); - for (int i = 0; i < action_sets.size(); i++) { + Array action_set_array = action_map->get_action_sets(); + for (int i = 0; i < action_set_array.size(); i++) { // Create our action set - Ref<OpenXRActionSet> xr_action_set = action_sets[i]; + Ref<OpenXRActionSet> xr_action_set = action_set_array[i]; ActionSet *action_set = create_action_set(xr_action_set->get_name(), xr_action_set->get_localized_name(), xr_action_set->get_priority()); if (!action_set) { continue; @@ -147,32 +154,33 @@ void OpenXRInterface::_load_action_map() { Ref<OpenXRAction> xr_action = actions[j]; PackedStringArray toplevel_paths = xr_action->get_toplevel_paths(); - Vector<Tracker *> trackers; + Vector<Tracker *> trackers_for_action; for (int k = 0; k < toplevel_paths.size(); k++) { - Tracker *tracker = find_tracker(toplevel_paths[k], true); - if (tracker) { - trackers.push_back(tracker); + // Only check for our tracker if our path is supported. + if (openxr_api->is_path_supported(toplevel_paths[k])) { + Tracker *tracker = find_tracker(toplevel_paths[k], true); + if (tracker) { + trackers_for_action.push_back(tracker); + } } } - Action *action = create_action(action_set, xr_action->get_name(), xr_action->get_localized_name(), xr_action->get_action_type(), trackers); - if (action) { - // we link our actions back to our trackers so we know which actions to check when we're processing our trackers - for (int t = 0; t < trackers.size(); t++) { - link_action_to_tracker(trackers[t], action); + // Only add our action if we have atleast one valid toplevel path + if (trackers_for_action.size() > 0) { + Action *action = create_action(action_set, xr_action->get_name(), xr_action->get_localized_name(), xr_action->get_action_type(), trackers_for_action); + if (action) { + // add this to our map for creating our interaction profiles + xr_actions[xr_action] = action; } - - // add this to our map for creating our interaction profiles - xr_actions[xr_action] = action; } } } // now do our suggestions - Array interaction_profiles = action_map->get_interaction_profiles(); - for (int i = 0; i < interaction_profiles.size(); i++) { - Ref<OpenXRInteractionProfile> xr_interaction_profile = interaction_profiles[i]; + Array interaction_profile_array = action_map->get_interaction_profiles(); + for (int i = 0; i < interaction_profile_array.size(); i++) { + Ref<OpenXRInteractionProfile> xr_interaction_profile = interaction_profile_array[i]; // Note, we can only have one entry per interaction profile so if it already exists we clear it out RID ip = openxr_api->interaction_profile_create(xr_interaction_profile->get_interaction_profile_path()); @@ -202,8 +210,8 @@ void OpenXRInterface::_load_action_map() { openxr_api->interaction_profile_suggest_bindings(ip); // And record it in our array so we can clean it up later on - if (interaction_profiles.has(ip)) { - interaction_profiles.push_back(ip); + if (interaction_profile_array.has(ip)) { + interaction_profile_array.push_back(ip); } } } @@ -282,6 +290,13 @@ OpenXRInterface::Action *OpenXRInterface::create_action(ActionSet *p_action_set, action->action_rid = openxr_api->action_create(p_action_set->action_set_rid, p_action_name, p_localized_name, p_action_type, tracker_rids); p_action_set->actions.push_back(action); + // we link our actions back to our trackers so we know which actions to check when we're processing our trackers + for (int i = 0; i < p_trackers.size(); i++) { + if (p_trackers[i]->actions.find(action) == -1) { + p_trackers[i]->actions.push_back(action); + } + } + return action; } @@ -330,6 +345,8 @@ OpenXRInterface::Tracker *OpenXRInterface::find_tracker(const String &p_tracker_ return nullptr; } + ERR_FAIL_COND_V(!openxr_api->is_path_supported(p_tracker_name), nullptr); + // Create our RID RID tracker_rid = openxr_api->tracker_create(p_tracker_name); ERR_FAIL_COND_V(tracker_rid.is_null(), nullptr); @@ -389,12 +406,6 @@ void OpenXRInterface::tracker_profile_changed(RID p_tracker, RID p_interaction_p } } -void OpenXRInterface::link_action_to_tracker(Tracker *p_tracker, Action *p_action) { - if (p_tracker->actions.find(p_action) == -1) { - p_tracker->actions.push_back(p_action); - } -} - void OpenXRInterface::handle_tracker(Tracker *p_tracker) { ERR_FAIL_NULL(openxr_api); ERR_FAIL_COND(p_tracker->positional_tracker.is_null()); @@ -447,9 +458,18 @@ void OpenXRInterface::handle_tracker(Tracker *p_tracker) { void OpenXRInterface::trigger_haptic_pulse(const String &p_action_name, const StringName &p_tracker_name, double p_frequency, double p_amplitude, double p_duration_sec, double p_delay_sec) { ERR_FAIL_NULL(openxr_api); + Action *action = find_action(p_action_name); ERR_FAIL_NULL(action); - Tracker *tracker = find_tracker(p_tracker_name); + + // We need to map our tracker name to our OpenXR name for our inbuild names. + String tracker_name = p_tracker_name; + if (tracker_name == "left_hand") { + tracker_name = "/user/hand/left"; + } else if (tracker_name == "right_hand") { + tracker_name = "/user/hand/right"; + } + Tracker *tracker = find_tracker(tracker_name); ERR_FAIL_NULL(tracker); // TODO OpenXR does not support delay, so we may need to add support for that somehow... @@ -571,6 +591,36 @@ bool OpenXRInterface::set_play_area_mode(XRInterface::PlayAreaMode p_mode) { return false; } +float OpenXRInterface::get_display_refresh_rate() const { + if (openxr_api == nullptr) { + return 0.0; + } else if (!openxr_api->is_initialized()) { + return 0.0; + } else { + return openxr_api->get_display_refresh_rate(); + } +} + +void OpenXRInterface::set_display_refresh_rate(float p_refresh_rate) { + if (openxr_api == nullptr) { + return; + } else if (!openxr_api->is_initialized()) { + return; + } else { + openxr_api->set_display_refresh_rate(p_refresh_rate); + } +} + +Array OpenXRInterface::get_available_display_refresh_rates() const { + if (openxr_api == nullptr) { + return Array(); + } else if (!openxr_api->is_initialized()) { + return Array(); + } else { + return openxr_api->get_available_display_refresh_rates(); + } +} + Size2 OpenXRInterface::get_render_target_size() { if (openxr_api == nullptr) { return Size2(); @@ -648,6 +698,22 @@ Projection OpenXRInterface::get_projection_for_view(uint32_t p_view, double p_as return cm; } +RID OpenXRInterface::get_color_texture() { + if (openxr_api) { + return openxr_api->get_color_texture(); + } else { + return RID(); + } +} + +RID OpenXRInterface::get_depth_texture() { + if (openxr_api) { + return openxr_api->get_depth_texture(); + } else { + return RID(); + } +} + void OpenXRInterface::process() { if (openxr_api) { // do our normal process @@ -707,6 +773,7 @@ bool OpenXRInterface::pre_draw_viewport(RID p_render_target) { Vector<BlitToScreen> OpenXRInterface::post_draw_viewport(RID p_render_target, const Rect2 &p_screen_rect) { Vector<BlitToScreen> blit_to_screen; +#ifndef ANDROID_ENABLED // If separate HMD we should output one eye to screen if (p_screen_rect != Rect2()) { BlitToScreen blit; @@ -732,6 +799,7 @@ Vector<BlitToScreen> OpenXRInterface::post_draw_viewport(RID p_render_target, co blit.dst_rect = dst_rect; blit_to_screen.push_back(blit); } +#endif if (openxr_api) { openxr_api->post_draw_viewport(p_render_target); diff --git a/modules/openxr/openxr_interface.h b/modules/openxr/openxr_interface.h index 3765f18637..454612346f 100644 --- a/modules/openxr/openxr_interface.h +++ b/modules/openxr/openxr_interface.h @@ -91,7 +91,6 @@ private: void free_actions(ActionSet *p_action_set); Tracker *find_tracker(const String &p_tracker_name, bool p_create = false); - void link_action_to_tracker(Tracker *p_tracker, Action *p_action); void handle_tracker(Tracker *p_tracker); void free_trackers(); @@ -120,12 +119,19 @@ public: virtual XRInterface::PlayAreaMode get_play_area_mode() const override; virtual bool set_play_area_mode(XRInterface::PlayAreaMode p_mode) override; + float get_display_refresh_rate() const; + void set_display_refresh_rate(float p_refresh_rate); + Array get_available_display_refresh_rates() const; + virtual Size2 get_render_target_size() override; virtual uint32_t get_view_count() override; virtual Transform3D get_camera_transform() override; virtual Transform3D get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) override; virtual Projection get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) override; + virtual RID get_color_texture() override; + virtual RID get_depth_texture() override; + virtual void process() override; virtual void pre_render() override; bool pre_draw_viewport(RID p_render_target) override; diff --git a/modules/openxr/util.h b/modules/openxr/util.h index 5c79890830..1b5e0451dc 100644 --- a/modules/openxr/util.h +++ b/modules/openxr/util.h @@ -53,58 +53,58 @@ #define EXT_INIT_XR_FUNC(name) INIT_XR_FUNC(openxr_api, name) #define OPENXR_API_INIT_XR_FUNC(name) INIT_XR_FUNC(this, name) -#define EXT_PROTO_XRRESULT_FUNC1(func_name, arg1_type, arg1) \ - PFN_##func_name func_name##_ptr = nullptr; \ - XRAPI_ATTR XrResult XRAPI_CALL func_name(UNPACK arg1_type arg1) const { \ - if (!func_name##_ptr) { \ - return XR_ERROR_HANDLE_INVALID; \ - } \ - return (*func_name##_ptr)(arg1); \ +#define EXT_PROTO_XRRESULT_FUNC1(func_name, arg1_type, arg1) \ + PFN_##func_name func_name##_ptr = nullptr; \ + XRAPI_ATTR XrResult XRAPI_CALL func_name(UNPACK arg1_type p_##arg1) const { \ + if (!func_name##_ptr) { \ + return XR_ERROR_HANDLE_INVALID; \ + } \ + return (*func_name##_ptr)(p_##arg1); \ } -#define EXT_PROTO_XRRESULT_FUNC2(func_name, arg1_type, arg1, arg2_type, arg2) \ - PFN_##func_name func_name##_ptr = nullptr; \ - XRAPI_ATTR XrResult XRAPI_CALL func_name(UNPACK arg1_type arg1, UNPACK arg2_type arg2) const { \ - if (!func_name##_ptr) { \ - return XR_ERROR_HANDLE_INVALID; \ - } \ - return (*func_name##_ptr)(arg1, arg2); \ +#define EXT_PROTO_XRRESULT_FUNC2(func_name, arg1_type, arg1, arg2_type, arg2) \ + PFN_##func_name func_name##_ptr = nullptr; \ + XRAPI_ATTR XrResult XRAPI_CALL func_name(UNPACK arg1_type p_##arg1, UNPACK arg2_type p_##arg2) const { \ + if (!func_name##_ptr) { \ + return XR_ERROR_HANDLE_INVALID; \ + } \ + return (*func_name##_ptr)(p_##arg1, p_##arg2); \ } -#define EXT_PROTO_XRRESULT_FUNC3(func_name, arg1_type, arg1, arg2_type, arg2, arg3_type, arg3) \ - PFN_##func_name func_name##_ptr = nullptr; \ - XRAPI_ATTR XrResult XRAPI_CALL func_name(UNPACK arg1_type arg1, UNPACK arg2_type arg2, UNPACK arg3_type arg3) const { \ - if (!func_name##_ptr) { \ - return XR_ERROR_HANDLE_INVALID; \ - } \ - return (*func_name##_ptr)(arg1, arg2, arg3); \ +#define EXT_PROTO_XRRESULT_FUNC3(func_name, arg1_type, arg1, arg2_type, arg2, arg3_type, arg3) \ + PFN_##func_name func_name##_ptr = nullptr; \ + XRAPI_ATTR XrResult XRAPI_CALL func_name(UNPACK arg1_type p_##arg1, UNPACK arg2_type p_##arg2, UNPACK arg3_type p_##arg3) const { \ + if (!func_name##_ptr) { \ + return XR_ERROR_HANDLE_INVALID; \ + } \ + return (*func_name##_ptr)(p_##arg1, p_##arg2, p_##arg3); \ } -#define EXT_PROTO_XRRESULT_FUNC4(func_name, arg1_type, arg1, arg2_type, arg2, arg3_type, arg3, arg4_type, arg4) \ - PFN_##func_name func_name##_ptr = nullptr; \ - XRAPI_ATTR XrResult XRAPI_CALL func_name(UNPACK arg1_type arg1, UNPACK arg2_type arg2, UNPACK arg3_type arg3, UNPACK arg4_type arg4) const { \ - if (!func_name##_ptr) { \ - return XR_ERROR_HANDLE_INVALID; \ - } \ - return (*func_name##_ptr)(arg1, arg2, arg3, arg4); \ +#define EXT_PROTO_XRRESULT_FUNC4(func_name, arg1_type, arg1, arg2_type, arg2, arg3_type, arg3, arg4_type, arg4) \ + PFN_##func_name func_name##_ptr = nullptr; \ + XRAPI_ATTR XrResult XRAPI_CALL func_name(UNPACK arg1_type p_##arg1, UNPACK arg2_type p_##arg2, UNPACK arg3_type p_##arg3, UNPACK arg4_type p_##arg4) const { \ + if (!func_name##_ptr) { \ + return XR_ERROR_HANDLE_INVALID; \ + } \ + return (*func_name##_ptr)(p_##arg1, p_##arg2, p_##arg3, p_##arg4); \ } -#define EXT_PROTO_XRRESULT_FUNC5(func_name, arg1_type, arg1, arg2_type, arg2, arg3_type, arg3, arg4_type, arg4, arg5_type, arg5) \ - PFN_##func_name func_name##_ptr = nullptr; \ - XRAPI_ATTR XrResult XRAPI_CALL func_name(UNPACK arg1_type arg1, UNPACK arg2_type arg2, UNPACK arg3_type arg3, UNPACK arg4_type arg4, UNPACK arg5_type arg5) const { \ - if (!func_name##_ptr) { \ - return XR_ERROR_HANDLE_INVALID; \ - } \ - return (*func_name##_ptr)(arg1, arg2, arg3, arg4, arg5); \ +#define EXT_PROTO_XRRESULT_FUNC5(func_name, arg1_type, arg1, arg2_type, arg2, arg3_type, arg3, arg4_type, arg4, arg5_type, arg5) \ + PFN_##func_name func_name##_ptr = nullptr; \ + XRAPI_ATTR XrResult XRAPI_CALL func_name(UNPACK arg1_type p_##arg1, UNPACK arg2_type p_##arg2, UNPACK arg3_type p_##arg3, UNPACK arg4_type p_##arg4, UNPACK arg5_type p_##arg5) const { \ + if (!func_name##_ptr) { \ + return XR_ERROR_HANDLE_INVALID; \ + } \ + return (*func_name##_ptr)(p_##arg1, p_##arg2, p_##arg3, p_##arg4, p_##arg5); \ } -#define EXT_PROTO_XRRESULT_FUNC6(func_name, arg1_type, arg1, arg2_type, arg2, arg3_type, arg3, arg4_type, arg4, arg5_type, arg5, arg6_type, arg6) \ - PFN_##func_name func_name##_ptr = nullptr; \ - XRAPI_ATTR XrResult XRAPI_CALL func_name(UNPACK arg1_type arg1, UNPACK arg2_type arg2, UNPACK arg3_type arg3, UNPACK arg4_type arg4, UNPACK arg5_type arg5, UNPACK arg6_type arg6) const { \ - if (!func_name##_ptr) { \ - return XR_ERROR_HANDLE_INVALID; \ - } \ - return (*func_name##_ptr)(arg1, arg2, arg3, arg4, arg5, arg6); \ +#define EXT_PROTO_XRRESULT_FUNC6(func_name, arg1_type, arg1, arg2_type, arg2, arg3_type, arg3, arg4_type, arg4, arg5_type, arg5, arg6_type, arg6) \ + PFN_##func_name func_name##_ptr = nullptr; \ + XRAPI_ATTR XrResult XRAPI_CALL func_name(UNPACK arg1_type p_##arg1, UNPACK arg2_type p_##arg2, UNPACK arg3_type p_##arg3, UNPACK arg4_type p_##arg4, UNPACK arg5_type p_##arg5, UNPACK arg6_type p_##arg6) const { \ + if (!func_name##_ptr) { \ + return XR_ERROR_HANDLE_INVALID; \ + } \ + return (*func_name##_ptr)(p_##arg1, p_##arg2, p_##arg3, p_##arg4, p_##arg5, p_##arg6); \ } #endif // UTIL_H |