From d7d334158a0bc86ff5b04ffd4085f092bbe50840 Mon Sep 17 00:00:00 2001 From: Bastiaan Olij Date: Mon, 21 Aug 2023 17:45:28 +1000 Subject: [PATCH] Implement OpenXR Foveated rendering support --- doc/classes/ProjectSettings.xml | 6 + main/main.cpp | 2 + modules/openxr/SCsub | 2 + .../openxr/doc_classes/OpenXRInterface.xml | 13 ++ .../openxr_fb_foveation_extension.cpp | 168 ++++++++++++++++++ .../openxr_fb_foveation_extension.h | 96 ++++++++++ .../openxr_fb_update_swapchain_extension.cpp | 102 +++++++++++ .../openxr_fb_update_swapchain_extension.h | 73 ++++++++ .../extensions/openxr_opengl_extension.h | 34 +--- .../extensions/openxr_vulkan_extension.h | 19 +- modules/openxr/openxr_api.cpp | 84 ++++++--- modules/openxr/openxr_api.h | 11 +- modules/openxr/openxr_interface.cpp | 53 ++++++ modules/openxr/openxr_interface.h | 8 + modules/openxr/openxr_platform_inc.h | 78 ++++++++ 15 files changed, 675 insertions(+), 74 deletions(-) create mode 100644 modules/openxr/extensions/openxr_fb_foveation_extension.cpp create mode 100644 modules/openxr/extensions/openxr_fb_foveation_extension.h create mode 100644 modules/openxr/extensions/openxr_fb_update_swapchain_extension.cpp create mode 100644 modules/openxr/extensions/openxr_fb_update_swapchain_extension.h create mode 100644 modules/openxr/openxr_platform_inc.h diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index b1189eeadaa..16d4bbd5c29 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -2717,6 +2717,12 @@ Specify whether OpenXR should be configured for an HMD or a hand held device. + + If true and foveation is supported, will automatically adjust foveation level based on framerate up to the level set on [code]xr/openxr/foveation_level[/code]. + + + Applied foveation level if supported: 0 = off, 1 = low, 2 = medium, 3 = high. + Specify the default reference space. diff --git a/main/main.cpp b/main/main.cpp index 837f8c1d07d..cd9bd6d1d34 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -2087,6 +2087,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "xr/openxr/view_configuration", PROPERTY_HINT_ENUM, "Mono,Stereo"), "1"); // "Mono,Stereo,Quad,Observer" GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "xr/openxr/reference_space", PROPERTY_HINT_ENUM, "Local,Stage"), "1"); GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "xr/openxr/environment_blend_mode", PROPERTY_HINT_ENUM, "Opaque,Additive,Alpha"), "0"); + GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "xr/openxr/foveation_level", PROPERTY_HINT_ENUM, "Off,Low,Medium,High"), "0"); + GLOBAL_DEF_BASIC("xr/openxr/foveation_dynamic", false); GLOBAL_DEF_BASIC("xr/openxr/submit_depth_buffer", false); GLOBAL_DEF_BASIC("xr/openxr/startup_alert", true); diff --git a/modules/openxr/SCsub b/modules/openxr/SCsub index 1bd10f10095..c3a5d82fc43 100644 --- a/modules/openxr/SCsub +++ b/modules/openxr/SCsub @@ -110,6 +110,8 @@ env_openxr.add_source_files(module_obj, "extensions/openxr_htc_controller_extens env_openxr.add_source_files(module_obj, "extensions/openxr_htc_vive_tracker_extension.cpp") env_openxr.add_source_files(module_obj, "extensions/openxr_huawei_controller_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_foveation_extension.cpp") +env_openxr.add_source_files(module_obj, "extensions/openxr_fb_update_swapchain_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_openxr.add_source_files(module_obj, "extensions/openxr_pico_controller_extension.cpp") diff --git a/modules/openxr/doc_classes/OpenXRInterface.xml b/modules/openxr/doc_classes/OpenXRInterface.xml index 8d8cbf1a293..d0630626e6e 100644 --- a/modules/openxr/doc_classes/OpenXRInterface.xml +++ b/modules/openxr/doc_classes/OpenXRInterface.xml @@ -77,6 +77,13 @@ Returns [code]true[/code] if the given action set is active. + + + + Returns [code]true[/code] if OpenXRs foveation extension is supported, the interface must be initialised before this returns a valid value. + [b]Note:[/b] This feature is only available on the compatibility renderer and currently only available on some stand alone headsets. For Vulkan set [member Viewport.vrs_mode] to [code]VRS_XR[/code] on desktop. + + @@ -98,6 +105,12 @@ 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. + + Enable dynamic foveation adjustment, the interface must be initialised before this is accessible. If enabled foveation will automatically adjusted between low and [member foveation_level]. + + + Set foveation level from 0 (off) to 3 (high), the interface must be initialised before this is accessible. + The render size multiplier for the current HMD. Must be set before the interface has been initialized. diff --git a/modules/openxr/extensions/openxr_fb_foveation_extension.cpp b/modules/openxr/extensions/openxr_fb_foveation_extension.cpp new file mode 100644 index 00000000000..7277f85b122 --- /dev/null +++ b/modules/openxr/extensions/openxr_fb_foveation_extension.cpp @@ -0,0 +1,168 @@ +/**************************************************************************/ +/* openxr_fb_foveation_extension.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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_foveation_extension.h" +#include "core/config/project_settings.h" + +OpenXRFBFoveationExtension *OpenXRFBFoveationExtension::singleton = nullptr; + +OpenXRFBFoveationExtension *OpenXRFBFoveationExtension::get_singleton() { + return singleton; +} + +OpenXRFBFoveationExtension::OpenXRFBFoveationExtension(const String &p_rendering_driver) { + singleton = this; + rendering_driver = p_rendering_driver; + swapchain_update_state_ext = OpenXRFBUpdateSwapchainExtension::get_singleton(); + int fov_level = GLOBAL_GET("xr/openxr/foveation_level"); + if (fov_level >= 0 && fov_level < 4) { + foveation_level = XrFoveationLevelFB(fov_level); + } + bool fov_dyn = GLOBAL_GET("xr/openxr/foveation_dynamic"); + foveation_dynamic = fov_dyn ? XR_FOVEATION_DYNAMIC_LEVEL_ENABLED_FB : XR_FOVEATION_DYNAMIC_DISABLED_FB; + + swapchain_create_info_foveation_fb.type = XR_TYPE_SWAPCHAIN_CREATE_INFO_FOVEATION_FB; + swapchain_create_info_foveation_fb.next = nullptr; + swapchain_create_info_foveation_fb.flags = 0; +} + +OpenXRFBFoveationExtension::~OpenXRFBFoveationExtension() { + singleton = nullptr; + swapchain_update_state_ext = nullptr; +} + +HashMap OpenXRFBFoveationExtension::get_requested_extensions() { + HashMap request_extensions; + + if (rendering_driver == "vulkan") { + // This is currently only supported on OpenGL, but we may add Vulkan support in the future... + + } else if (rendering_driver == "opengl3") { + request_extensions[XR_FB_FOVEATION_EXTENSION_NAME] = &fb_foveation_ext; + request_extensions[XR_FB_FOVEATION_CONFIGURATION_EXTENSION_NAME] = &fb_foveation_configuration_ext; + } + + return request_extensions; +} + +void OpenXRFBFoveationExtension::on_instance_created(const XrInstance p_instance) { + if (fb_foveation_ext) { + EXT_INIT_XR_FUNC(xrCreateFoveationProfileFB); + EXT_INIT_XR_FUNC(xrDestroyFoveationProfileFB); + } + + if (fb_foveation_configuration_ext) { + // nothing to register here... + } +} + +void OpenXRFBFoveationExtension::on_instance_destroyed() { + fb_foveation_ext = false; + fb_foveation_configuration_ext = false; +} + +bool OpenXRFBFoveationExtension::is_enabled() const { + return swapchain_update_state_ext != nullptr && swapchain_update_state_ext->is_enabled() && fb_foveation_ext && fb_foveation_configuration_ext; +} + +void *OpenXRFBFoveationExtension::set_swapchain_create_info_and_get_next_pointer(void *p_next_pointer) { + if (is_enabled()) { + swapchain_create_info_foveation_fb.next = p_next_pointer; + return &swapchain_create_info_foveation_fb; + } else { + return p_next_pointer; + } +} + +void OpenXRFBFoveationExtension::on_state_ready() { + update_profile(); +} + +XrFoveationLevelFB OpenXRFBFoveationExtension::get_foveation_level() const { + return foveation_level; +} + +void OpenXRFBFoveationExtension::set_foveation_level(XrFoveationLevelFB p_foveation_level) { + foveation_level = p_foveation_level; + + // Update profile will do nothing if we're not yet initialised + update_profile(); +} + +XrFoveationDynamicFB OpenXRFBFoveationExtension::get_foveation_dynamic() const { + return foveation_dynamic; +} + +void OpenXRFBFoveationExtension::set_foveation_dynamic(XrFoveationDynamicFB p_foveation_dynamic) { + foveation_dynamic = p_foveation_dynamic; + + // Update profile will do nothing if we're not yet initialised + update_profile(); +} + +void OpenXRFBFoveationExtension::update_profile() { + if (!is_enabled()) { + return; + } + + XrFoveationLevelProfileCreateInfoFB level_profile_create_info; + level_profile_create_info.type = XR_TYPE_FOVEATION_LEVEL_PROFILE_CREATE_INFO_FB; + level_profile_create_info.next = nullptr; + level_profile_create_info.level = foveation_level; + level_profile_create_info.verticalOffset = 0.0f; + level_profile_create_info.dynamic = foveation_dynamic; + + XrFoveationProfileCreateInfoFB profile_create_info; + profile_create_info.type = XR_TYPE_FOVEATION_PROFILE_CREATE_INFO_FB; + profile_create_info.next = &level_profile_create_info; + + XrFoveationProfileFB foveation_profile; + XrResult result = xrCreateFoveationProfileFB(OpenXRAPI::get_singleton()->get_session(), &profile_create_info, &foveation_profile); + if (XR_FAILED(result)) { + print_line("OpenXR: Unable to create the foveation profile [", OpenXRAPI::get_singleton()->get_error_string(result), "]"); + return; + } + + XrSwapchainStateFoveationFB foveation_update_state; + foveation_update_state.type = XR_TYPE_SWAPCHAIN_STATE_FOVEATION_FB; + foveation_update_state.profile = foveation_profile; + + result = swapchain_update_state_ext->xrUpdateSwapchainFB(OpenXRAPI::get_singleton()->get_color_swapchain(), (XrSwapchainStateBaseHeaderFB *)&foveation_update_state); + if (XR_FAILED(result)) { + print_line("OpenXR: Unable to update the swapchain [", OpenXRAPI::get_singleton()->get_error_string(result), "]"); + + // We still want to destroy our profile so keep going... + } + + result = xrDestroyFoveationProfileFB(foveation_profile); + if (XR_FAILED(result)) { + print_line("OpenXR: Unable to destroy the foveation profile [", OpenXRAPI::get_singleton()->get_error_string(result), "]"); + } +} diff --git a/modules/openxr/extensions/openxr_fb_foveation_extension.h b/modules/openxr/extensions/openxr_fb_foveation_extension.h new file mode 100644 index 00000000000..1c5e7227316 --- /dev/null +++ b/modules/openxr/extensions/openxr_fb_foveation_extension.h @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* openxr_fb_foveation_extension.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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_FOVEATION_EXTENSION_H +#define OPENXR_FB_FOVEATION_EXTENSION_H + +// This extension implements the FB Foveation extension. +// This is an extension Meta added due to VRS being unavailable on Android. +// Other Android based devices are implementing this as well, see: +// https://github.khronos.org/OpenXR-Inventory/extension_support.html#XR_FB_foveation + +// Note: Currently we only support this for OpenGL. +// This extension works on enabling foveated rendering on the swapchain. +// Vulkan does not render 3D content directly to the swapchain image +// hence this extension can't be used. + +#include "../openxr_api.h" +#include "../util.h" +#include "openxr_extension_wrapper.h" +#include "openxr_fb_update_swapchain_extension.h" + +class OpenXRFBFoveationExtension : public OpenXRExtensionWrapper { +public: + static OpenXRFBFoveationExtension *get_singleton(); + + OpenXRFBFoveationExtension(const String &p_rendering_driver); + virtual ~OpenXRFBFoveationExtension() override; + + virtual HashMap get_requested_extensions() override; + + virtual void on_instance_created(const XrInstance p_instance) override; + virtual void on_instance_destroyed() override; + + virtual void *set_swapchain_create_info_and_get_next_pointer(void *p_next_pointer) override; + + virtual void on_state_ready() override; + + bool is_enabled() const; + + XrFoveationLevelFB get_foveation_level() const; + void set_foveation_level(XrFoveationLevelFB p_foveation_level); + + XrFoveationDynamicFB get_foveation_dynamic() const; + void set_foveation_dynamic(XrFoveationDynamicFB p_foveation_dynamic); + +private: + static OpenXRFBFoveationExtension *singleton; + + // Setup + String rendering_driver; + bool fb_foveation_ext = false; + bool fb_foveation_configuration_ext = false; + + // Configuration + XrFoveationLevelFB foveation_level = XR_FOVEATION_LEVEL_NONE_FB; + XrFoveationDynamicFB foveation_dynamic = XR_FOVEATION_DYNAMIC_DISABLED_FB; + + void update_profile(); + + // Enable foveation on this swapchain + XrSwapchainCreateInfoFoveationFB swapchain_create_info_foveation_fb; + OpenXRFBUpdateSwapchainExtension *swapchain_update_state_ext = nullptr; + + // OpenXR API call wrappers + EXT_PROTO_XRRESULT_FUNC3(xrCreateFoveationProfileFB, (XrSession), session, (const XrFoveationProfileCreateInfoFB *), create_info, (XrFoveationProfileFB *), profile); + EXT_PROTO_XRRESULT_FUNC1(xrDestroyFoveationProfileFB, (XrFoveationProfileFB), profile); +}; + +#endif // OPENXR_FB_FOVEATION_EXTENSION_H diff --git a/modules/openxr/extensions/openxr_fb_update_swapchain_extension.cpp b/modules/openxr/extensions/openxr_fb_update_swapchain_extension.cpp new file mode 100644 index 00000000000..1289183ea48 --- /dev/null +++ b/modules/openxr/extensions/openxr_fb_update_swapchain_extension.cpp @@ -0,0 +1,102 @@ +/**************************************************************************/ +/* openxr_fb_update_swapchain_extension.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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_update_swapchain_extension.h" + +// always include this as late as possible +#include "../openxr_platform_inc.h" + +OpenXRFBUpdateSwapchainExtension *OpenXRFBUpdateSwapchainExtension::singleton = nullptr; + +OpenXRFBUpdateSwapchainExtension *OpenXRFBUpdateSwapchainExtension::get_singleton() { + return singleton; +} + +OpenXRFBUpdateSwapchainExtension::OpenXRFBUpdateSwapchainExtension(const String &p_rendering_driver) { + singleton = this; + rendering_driver = p_rendering_driver; +} + +OpenXRFBUpdateSwapchainExtension::~OpenXRFBUpdateSwapchainExtension() { + singleton = nullptr; +} + +HashMap OpenXRFBUpdateSwapchainExtension::get_requested_extensions() { + HashMap request_extensions; + + request_extensions[XR_FB_SWAPCHAIN_UPDATE_STATE_EXTENSION_NAME] = &fb_swapchain_update_state_ext; + + if (rendering_driver == "vulkan") { +#ifdef XR_USE_GRAPHICS_API_VULKAN + request_extensions[XR_FB_SWAPCHAIN_UPDATE_STATE_VULKAN_EXTENSION_NAME] = &fb_swapchain_update_state_vulkan_ext; +#endif + } else if (rendering_driver == "opengl3") { +#ifdef XR_USE_GRAPHICS_API_OPENGL_ES + request_extensions[XR_FB_SWAPCHAIN_UPDATE_STATE_OPENGL_ES_EXTENSION_NAME] = &fb_swapchain_update_state_opengles_ext; +#endif + } + + return request_extensions; +} + +void OpenXRFBUpdateSwapchainExtension::on_instance_created(const XrInstance p_instance) { + if (fb_swapchain_update_state_ext) { + EXT_INIT_XR_FUNC(xrUpdateSwapchainFB); + EXT_INIT_XR_FUNC(xrGetSwapchainStateFB); + } + + if (fb_swapchain_update_state_vulkan_ext) { + // nothing to register here... + } + + if (fb_swapchain_update_state_opengles_ext) { + // nothing to register here... + } +} + +void OpenXRFBUpdateSwapchainExtension::on_instance_destroyed() { + fb_swapchain_update_state_ext = false; + fb_swapchain_update_state_vulkan_ext = false; + fb_swapchain_update_state_opengles_ext = false; +} + +bool OpenXRFBUpdateSwapchainExtension::is_enabled() const { + if (rendering_driver == "vulkan") { + return fb_swapchain_update_state_ext && fb_swapchain_update_state_vulkan_ext; + } else if (rendering_driver == "opengl3") { +#ifdef XR_USE_GRAPHICS_API_OPENGL_ES + return fb_swapchain_update_state_ext && fb_swapchain_update_state_opengles_ext; +#else + return fb_swapchain_update_state_ext; +#endif + } + + return false; +} diff --git a/modules/openxr/extensions/openxr_fb_update_swapchain_extension.h b/modules/openxr/extensions/openxr_fb_update_swapchain_extension.h new file mode 100644 index 00000000000..a02b550e587 --- /dev/null +++ b/modules/openxr/extensions/openxr_fb_update_swapchain_extension.h @@ -0,0 +1,73 @@ +/**************************************************************************/ +/* openxr_fb_update_swapchain_extension.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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_UPDATE_SWAPCHAIN_EXTENSION_H +#define OPENXR_FB_UPDATE_SWAPCHAIN_EXTENSION_H + +// This extension implements the FB update swapchain extension. +// This is an extension Meta added to further configure the swapchain. +// Other Android based devices are implementing this as well, see: +// https://github.khronos.org/OpenXR-Inventory/extension_support.html#XR_FB_swapchain_update_state + +#include "../openxr_api.h" +#include "../util.h" +#include "openxr_extension_wrapper.h" + +class OpenXRFBUpdateSwapchainExtension : public OpenXRExtensionWrapper { + friend class OpenXRFBFoveationExtension; + +public: + static OpenXRFBUpdateSwapchainExtension *get_singleton(); + + OpenXRFBUpdateSwapchainExtension(const String &p_rendering_driver); + virtual ~OpenXRFBUpdateSwapchainExtension() override; + + virtual HashMap get_requested_extensions() override; + + virtual void on_instance_created(const XrInstance p_instance) override; + virtual void on_instance_destroyed() override; + + bool is_enabled() const; + +private: + static OpenXRFBUpdateSwapchainExtension *singleton; + + // Setup + String rendering_driver; + bool fb_swapchain_update_state_ext = false; + bool fb_swapchain_update_state_vulkan_ext = false; + bool fb_swapchain_update_state_opengles_ext = false; + + // OpenXR API call wrappers + EXT_PROTO_XRRESULT_FUNC2(xrUpdateSwapchainFB, (XrSwapchain), swapchain, (const XrSwapchainStateBaseHeaderFB *), state); + EXT_PROTO_XRRESULT_FUNC2(xrGetSwapchainStateFB, (XrSwapchain), swapchain, (XrSwapchainStateBaseHeaderFB *), state); +}; + +#endif // OPENXR_FB_UPDATE_SWAPCHAIN_EXTENSION_H diff --git a/modules/openxr/extensions/openxr_opengl_extension.h b/modules/openxr/extensions/openxr_opengl_extension.h index 1537f13067a..3b0aa0bce92 100644 --- a/modules/openxr/extensions/openxr_opengl_extension.h +++ b/modules/openxr/extensions/openxr_opengl_extension.h @@ -39,38 +39,8 @@ #include "core/templates/vector.h" -#ifdef ANDROID_ENABLED -#define XR_USE_GRAPHICS_API_OPENGL_ES -#include -#include -#include -#include -#else -#define XR_USE_GRAPHICS_API_OPENGL -#endif - -#ifdef WINDOWS_ENABLED -// Including windows.h here is absolutely evil, we shouldn't be doing this outside of platform -// however due to the way the openxr headers are put together, we have no choice. -#include -#endif - -#ifdef X11_ENABLED -#define GL_GLEXT_PROTOTYPES 1 -#define GL3_PROTOTYPES 1 -#include "thirdparty/glad/glad/gl.h" -#include "thirdparty/glad/glad/glx.h" - -#include -#endif - -#ifdef ANDROID_ENABLED -// The jobject type from jni.h is used by openxr_platform.h on Android. -#include -#endif - -// Include platform dependent structs. -#include +// always include this as late as possible +#include "../openxr_platform_inc.h" class OpenXROpenGLExtension : public OpenXRGraphicsExtensionWrapper { public: diff --git a/modules/openxr/extensions/openxr_vulkan_extension.h b/modules/openxr/extensions/openxr_vulkan_extension.h index 4add6f6fa24..f31621fda0f 100644 --- a/modules/openxr/extensions/openxr_vulkan_extension.h +++ b/modules/openxr/extensions/openxr_vulkan_extension.h @@ -36,24 +36,9 @@ #include "openxr_extension_wrapper.h" #include "core/templates/vector.h" -#include "drivers/vulkan/vulkan_context.h" -// Need to include Vulkan so we know of type definitions. -#define XR_USE_GRAPHICS_API_VULKAN - -#ifdef WINDOWS_ENABLED -// Including windows.h here is absolutely evil, we shouldn't be doing this outside of platform -// however due to the way the openxr headers are put together, we have no choice. -#include -#endif - -#ifdef ANDROID_ENABLED -// The jobject type from jni.h is used by openxr_platform.h on Android. -#include -#endif - -// Include platform dependent structs. -#include +// always include this as late as possible +#include "../openxr_platform_inc.h" class OpenXRVulkanExtension : public OpenXRGraphicsExtensionWrapper, VulkanHooks { public: diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp index 972b6539e3a..b1c7ab16153 100644 --- a/modules/openxr/openxr_api.cpp +++ b/modules/openxr/openxr_api.cpp @@ -43,30 +43,7 @@ #include "editor/editor_settings.h" #endif -// We need to have all the graphics API defines before the Vulkan or OpenGL -// extensions are included, otherwise we'll only get one graphics API. -#ifdef VULKAN_ENABLED -#define XR_USE_GRAPHICS_API_VULKAN -#endif -#if defined(GLES3_ENABLED) && !defined(MACOS_ENABLED) -#ifdef ANDROID_ENABLED -#define XR_USE_GRAPHICS_API_OPENGL_ES -#include -#include -#include -#include -#else -#define XR_USE_GRAPHICS_API_OPENGL -#endif // ANDROID_ENABLED -#ifdef X11_ENABLED -#define GL_GLEXT_PROTOTYPES 1 -#define GL3_PROTOTYPES 1 -#include "thirdparty/glad/glad/gl.h" -#include "thirdparty/glad/glad/glx.h" - -#include -#endif // X11_ENABLED -#endif // GLES_ENABLED +#include "openxr_platform_inc.h" #ifdef VULKAN_ENABLED #include "extensions/openxr_vulkan_extension.h" @@ -78,7 +55,9 @@ #include "extensions/openxr_composition_layer_depth_extension.h" #include "extensions/openxr_fb_display_refresh_rate_extension.h" +#include "extensions/openxr_fb_foveation_extension.h" #include "extensions/openxr_fb_passthrough_extension_wrapper.h" +#include "extensions/openxr_fb_update_swapchain_extension.h" #ifdef ANDROID_ENABLED #define OPENXR_LOADER_NAME "libopenxr_loader.so" @@ -1340,6 +1319,10 @@ bool OpenXRAPI::initialize(const String &p_rendering_driver) { ERR_FAIL_V_MSG(false, "OpenXR: Unsupported rendering device."); } + // Also register our rendering extensions + register_extension_wrapper(memnew(OpenXRFBUpdateSwapchainExtension(p_rendering_driver))); + register_extension_wrapper(memnew(OpenXRFBFoveationExtension(p_rendering_driver))); + // initialize for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) { wrapper->on_before_instance_created(); @@ -1859,6 +1842,10 @@ bool OpenXRAPI::pre_draw_viewport(RID p_render_target) { return true; } +XrSwapchain OpenXRAPI::get_color_swapchain() { + return swapchains[OPENXR_SWAPCHAIN_COLOR].swapchain; +} + 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); @@ -2010,6 +1997,55 @@ void OpenXRAPI::set_render_target_size_multiplier(double multiplier) { render_target_size_multiplier = multiplier; } +bool OpenXRAPI::is_foveation_supported() const { + OpenXRFBFoveationExtension *fov_ext = OpenXRFBFoveationExtension::get_singleton(); + return fov_ext != nullptr && fov_ext->is_enabled(); +} + +int OpenXRAPI::get_foveation_level() const { + OpenXRFBFoveationExtension *fov_ext = OpenXRFBFoveationExtension::get_singleton(); + if (fov_ext != nullptr && fov_ext->is_enabled()) { + switch (fov_ext->get_foveation_level()) { + case XR_FOVEATION_LEVEL_NONE_FB: + return 0; + case XR_FOVEATION_LEVEL_LOW_FB: + return 1; + case XR_FOVEATION_LEVEL_MEDIUM_FB: + return 2; + case XR_FOVEATION_LEVEL_HIGH_FB: + return 3; + default: + return 0; + } + } + + return 0; +} + +void OpenXRAPI::set_foveation_level(int p_foveation_level) { + ERR_FAIL_UNSIGNED_INDEX(p_foveation_level, 4); + OpenXRFBFoveationExtension *fov_ext = OpenXRFBFoveationExtension::get_singleton(); + if (fov_ext != nullptr && fov_ext->is_enabled()) { + XrFoveationLevelFB levels[] = { XR_FOVEATION_LEVEL_NONE_FB, XR_FOVEATION_LEVEL_LOW_FB, XR_FOVEATION_LEVEL_MEDIUM_FB, XR_FOVEATION_LEVEL_HIGH_FB }; + fov_ext->set_foveation_level(levels[p_foveation_level]); + } +} + +bool OpenXRAPI::get_foveation_dynamic() const { + OpenXRFBFoveationExtension *fov_ext = OpenXRFBFoveationExtension::get_singleton(); + if (fov_ext != nullptr && fov_ext->is_enabled()) { + return fov_ext->get_foveation_dynamic() == XR_FOVEATION_DYNAMIC_LEVEL_ENABLED_FB; + } + return false; +} + +void OpenXRAPI::set_foveation_dynamic(bool p_foveation_dynamic) { + OpenXRFBFoveationExtension *fov_ext = OpenXRFBFoveationExtension::get_singleton(); + if (fov_ext != nullptr && fov_ext->is_enabled()) { + fov_ext->set_foveation_dynamic(p_foveation_dynamic ? XR_FOVEATION_DYNAMIC_LEVEL_ENABLED_FB : XR_FOVEATION_DYNAMIC_DISABLED_FB); + } +} + OpenXRAPI::OpenXRAPI() { // OpenXRAPI is only constructed if OpenXR is enabled. singleton = this; diff --git a/modules/openxr/openxr_api.h b/modules/openxr/openxr_api.h index 26de5351531..89f8f3cbec9 100644 --- a/modules/openxr/openxr_api.h +++ b/modules/openxr/openxr_api.h @@ -54,7 +54,6 @@ // Godot is currently restricted to C++17 which doesn't allow this notation. Make sure critical fields are set. // forward declarations, we don't want to include these fully -class OpenXRVulkanExtension; class OpenXRInterface; class OpenXRAPI { @@ -356,6 +355,7 @@ public: void pre_render(); bool pre_draw_viewport(RID p_render_target); + XrSwapchain get_color_swapchain(); RID get_color_texture(); RID get_depth_texture(); void post_draw_viewport(RID p_render_target); @@ -370,6 +370,15 @@ public: double get_render_target_size_multiplier() const; void set_render_target_size_multiplier(double multiplier); + // Foveation settings + bool is_foveation_supported() const; + + int get_foveation_level() const; + void set_foveation_level(int p_foveation_level); + + bool get_foveation_dynamic() const; + void set_foveation_dynamic(bool p_foveation_dynamic); + // action map String get_default_action_map_resource_name(); diff --git a/modules/openxr/openxr_interface.cpp b/modules/openxr/openxr_interface.cpp index cf8d1654b1e..7b1530677f7 100644 --- a/modules/openxr/openxr_interface.cpp +++ b/modules/openxr/openxr_interface.cpp @@ -54,10 +54,23 @@ void OpenXRInterface::_bind_methods() { ClassDB::bind_method(D_METHOD("set_render_target_size_multiplier", "multiplier"), &OpenXRInterface::set_render_target_size_multiplier); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "render_target_size_multiplier"), "set_render_target_size_multiplier", "get_render_target_size_multiplier"); + // Foveation level + ClassDB::bind_method(D_METHOD("is_foveation_supported"), &OpenXRInterface::is_foveation_supported); + + ClassDB::bind_method(D_METHOD("get_foveation_level"), &OpenXRInterface::get_foveation_level); + ClassDB::bind_method(D_METHOD("set_foveation_level", "foveation_level"), &OpenXRInterface::set_foveation_level); + ADD_PROPERTY(PropertyInfo(Variant::INT, "foveation_level"), "set_foveation_level", "get_foveation_level"); + + ClassDB::bind_method(D_METHOD("get_foveation_dynamic"), &OpenXRInterface::get_foveation_dynamic); + ClassDB::bind_method(D_METHOD("set_foveation_dynamic", "foveation_dynamic"), &OpenXRInterface::set_foveation_dynamic); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "foveation_dynamic"), "set_foveation_dynamic", "get_foveation_dynamic"); + + // Action sets ClassDB::bind_method(D_METHOD("is_action_set_active", "name"), &OpenXRInterface::is_action_set_active); ClassDB::bind_method(D_METHOD("set_action_set_active", "name", "active"), &OpenXRInterface::set_action_set_active); ClassDB::bind_method(D_METHOD("get_action_sets"), &OpenXRInterface::get_action_sets); + // Refresh rates ClassDB::bind_method(D_METHOD("get_available_display_refresh_rates"), &OpenXRInterface::get_available_display_refresh_rates); // Hand tracking. @@ -740,6 +753,46 @@ void OpenXRInterface::set_render_target_size_multiplier(double multiplier) { } } +bool OpenXRInterface::is_foveation_supported() const { + if (openxr_api == nullptr) { + return false; + } else { + return openxr_api->is_foveation_supported(); + } +} + +int OpenXRInterface::get_foveation_level() const { + if (openxr_api == nullptr) { + return 0; + } else { + return openxr_api->get_foveation_level(); + } +} + +void OpenXRInterface::set_foveation_level(int p_foveation_level) { + if (openxr_api == nullptr) { + return; + } else { + openxr_api->set_foveation_level(p_foveation_level); + } +} + +bool OpenXRInterface::get_foveation_dynamic() const { + if (openxr_api == nullptr) { + return false; + } else { + return openxr_api->get_foveation_dynamic(); + } +} + +void OpenXRInterface::set_foveation_dynamic(bool p_foveation_dynamic) { + if (openxr_api == nullptr) { + return; + } else { + openxr_api->set_foveation_dynamic(p_foveation_dynamic); + } +} + 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 81efbd67770..38cf4bdbe8a 100644 --- a/modules/openxr/openxr_interface.h +++ b/modules/openxr/openxr_interface.h @@ -130,6 +130,14 @@ public: double get_render_target_size_multiplier() const; void set_render_target_size_multiplier(double multiplier); + bool is_foveation_supported() const; + + int get_foveation_level() const; + void set_foveation_level(int p_foveation_level); + + bool get_foveation_dynamic() const; + void set_foveation_dynamic(bool p_foveation_dynamic); + virtual Size2 get_render_target_size() override; virtual uint32_t get_view_count() override; virtual Transform3D get_camera_transform() override; diff --git a/modules/openxr/openxr_platform_inc.h b/modules/openxr/openxr_platform_inc.h new file mode 100644 index 00000000000..6288d1e380d --- /dev/null +++ b/modules/openxr/openxr_platform_inc.h @@ -0,0 +1,78 @@ +/**************************************************************************/ +/* openxr_platform_inc.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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_PLATFORM_INC_H +#define OPENXR_PLATFORM_INC_H + +// In various places we need to include platform definitions but we can't +// include these in our normal header files as we'll end up with issues. + +#ifdef VULKAN_ENABLED +#define XR_USE_GRAPHICS_API_VULKAN +#include "drivers/vulkan/vulkan_context.h" +#endif // VULKAN_ENABLED + +#if defined(GLES3_ENABLED) && !defined(MACOS_ENABLED) +#ifdef ANDROID_ENABLED +#define XR_USE_GRAPHICS_API_OPENGL_ES +#include +#include +#include +#include +#else +#define XR_USE_GRAPHICS_API_OPENGL +#endif // ANDROID_ENABLED +#ifdef X11_ENABLED +#define GL_GLEXT_PROTOTYPES 1 +#define GL3_PROTOTYPES 1 +#include "thirdparty/glad/glad/gl.h" +#include "thirdparty/glad/glad/glx.h" +#endif // X11_ENABLED +#endif // defined(GLES3_ENABLED) && !defined(MACOS_ENABLED) + +#ifdef X11_ENABLED +#include +#endif // X11_ENABLED + +#ifdef WINDOWS_ENABLED +// Including windows.h here is absolutely evil, we shouldn't be doing this outside of platform +// however due to the way the openxr headers are put together, we have no choice. +#include +#endif // WINDOWS_ENABLED + +#ifdef ANDROID_ENABLED +// The jobject type from jni.h is used by openxr_platform.h on Android. +#include +#endif // ANDROID_ENABLED + +// Include platform dependent structs. +#include + +#endif // OPENXR_PLATFORM_INC_H