From e14717bb2e1687093d4ac6f9bc06ea79ae0bd2fb Mon Sep 17 00:00:00 2001 From: Bastiaan Olij Date: Mon, 10 Oct 2022 18:51:53 +1100 Subject: [PATCH] Adding support for the OpenXR Display Refresh Rate extension --- modules/openxr/SCsub | 1 + .../openxr/doc_classes/OpenXRInterface.xml | 13 ++ ...enxr_fb_display_refresh_rate_extension.cpp | 123 ++++++++++++++++++ ...openxr_fb_display_refresh_rate_extension.h | 70 ++++++++++ modules/openxr/openxr_api.cpp | 27 ++++ modules/openxr/openxr_api.h | 5 + modules/openxr/openxr_interface.cpp | 37 ++++++ modules/openxr/openxr_interface.h | 4 + 8 files changed, 280 insertions(+) create mode 100644 modules/openxr/extensions/openxr_fb_display_refresh_rate_extension.cpp create mode 100644 modules/openxr/extensions/openxr_fb_display_refresh_rate_extension.h diff --git a/modules/openxr/SCsub b/modules/openxr/SCsub index 5ac167ad98a..b5978ab134c 100644 --- a/modules/openxr/SCsub +++ b/modules/openxr/SCsub @@ -96,6 +96,7 @@ env_openxr.add_source_files(module_obj, "extensions/openxr_composition_layer_dep 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/doc_classes/OpenXRInterface.xml b/modules/openxr/doc_classes/OpenXRInterface.xml index 25bf496de9e..f089fd066e5 100644 --- a/modules/openxr/doc_classes/OpenXRInterface.xml +++ b/modules/openxr/doc_classes/OpenXRInterface.xml @@ -10,6 +10,19 @@ $DOCS_URL/tutorials/xr/setting_up_xr.html + + + + + 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. + + + + + + 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. + + 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 00000000000..c0bbaea5b45 --- /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 00000000000..dcd52fe4d1d --- /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/openxr_api.cpp b/modules/openxr/openxr_api.cpp index 01107ebcc97..5a2681e4b80 100644 --- a/modules/openxr/openxr_api.cpp +++ b/modules/openxr/openxr_api.cpp @@ -50,6 +50,7 @@ #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" @@ -1787,6 +1788,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; @@ -1856,6 +1882,7 @@ OpenXRAPI::OpenXRAPI() { 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() { diff --git a/modules/openxr/openxr_api.h b/modules/openxr/openxr_api.h index bd69432dcba..e6781cc112d 100644 --- a/modules/openxr/openxr_api.h +++ b/modules/openxr/openxr_api.h @@ -336,6 +336,11 @@ public: 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 68414ae84e5..e4c4e3edec6 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 { @@ -571,6 +578,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(); diff --git a/modules/openxr/openxr_interface.h b/modules/openxr/openxr_interface.h index 72935b039cf..720426f19c9 100644 --- a/modules/openxr/openxr_interface.h +++ b/modules/openxr/openxr_interface.h @@ -120,6 +120,10 @@ 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;