From 251ae9c1383c84813576e3e515f0f1ac1e03b993 Mon Sep 17 00:00:00 2001 From: devloglogan Date: Mon, 20 Jan 2025 13:09:20 -0600 Subject: [PATCH] Implement OpenXR FB swapchain update extensions --- .../doc_classes/OpenXRCompositionLayer.xml | 99 +++++++ .../openxr_composition_layer_extension.cpp | 45 ++++ .../openxr_composition_layer_extension.h | 55 ++++ .../openxr_fb_update_swapchain_extension.cpp | 245 ++++++++++++++++++ .../openxr_fb_update_swapchain_extension.h | 16 ++ .../openxr/scene/openxr_composition_layer.cpp | 213 ++++++++++++++- .../openxr/scene/openxr_composition_layer.h | 76 ++++++ 7 files changed, 748 insertions(+), 1 deletion(-) diff --git a/modules/openxr/doc_classes/OpenXRCompositionLayer.xml b/modules/openxr/doc_classes/OpenXRCompositionLayer.xml index cfc7cd4d970..6d605f50393 100644 --- a/modules/openxr/doc_classes/OpenXRCompositionLayer.xml +++ b/modules/openxr/doc_classes/OpenXRCompositionLayer.xml @@ -53,10 +53,109 @@ The sort order for this composition layer. Higher numbers will be shown in front of lower numbers. [b]Note:[/b] This will have no effect if a fallback mesh is being used. + + The swizzle value for the alpha channel of the swapchain state. + [b]Note:[/b] This property only has an effect on devices that support the OpenXR XR_FB_swapchain_update_state OpenGLES/Vulkan extensions. + + + The swizzle value for the blue channel of the swapchain state. + [b]Note:[/b] This property only has an effect on devices that support the OpenXR XR_FB_swapchain_update_state OpenGLES/Vulkan extensions. + + + The border color of the swapchain state that is used when the wrap mode clamps to the border. + [b]Note:[/b] This property only has an effect on devices that support the OpenXR XR_FB_swapchain_update_state OpenGLES/Vulkan extensions. + + + The swizzle value for the green channel of the swapchain state. + [b]Note:[/b] This property only has an effect on devices that support the OpenXR XR_FB_swapchain_update_state OpenGLES/Vulkan extensions. + + + The horizontal wrap mode of the swapchain state. + [b]Note:[/b] This property only has an effect on devices that support the OpenXR XR_FB_swapchain_update_state OpenGLES/Vulkan extensions. + + + The magnification filter of the swapchain state. + [b]Note:[/b] This property only has an effect on devices that support the OpenXR XR_FB_swapchain_update_state OpenGLES/Vulkan extensions. + + + The max anisotropy of the swapchain state. + [b]Note:[/b] This property only has an effect on devices that support the OpenXR XR_FB_swapchain_update_state OpenGLES/Vulkan extensions. + + + The minification filter of the swapchain state. + [b]Note:[/b] This property only has an effect on devices that support the OpenXR XR_FB_swapchain_update_state OpenGLES/Vulkan extensions. + + + The mipmap mode of the swapchain state. + [b]Note:[/b] This property only has an effect on devices that support the OpenXR XR_FB_swapchain_update_state OpenGLES/Vulkan extensions. + + + The swizzle value for the red channel of the swapchain state. + [b]Note:[/b] This property only has an effect on devices that support the OpenXR XR_FB_swapchain_update_state OpenGLES/Vulkan extensions. + + + The vertical wrap mode of the swapchain state. + [b]Note:[/b] This property only has an effect on devices that support the OpenXR XR_FB_swapchain_update_state OpenGLES/Vulkan extensions. + If enabled, an Android surface will be created (with the dimensions from [member android_surface_size]) which will provide the 2D content for the composition layer, rather than using [member layer_viewport]. See [method get_android_surface] for information about how to get the surface so that your application can draw to it. [b]Note:[/b] This will only work in Android builds. + + + Perform nearest-neighbor filtering when sampling the texture. + + + Perform linear filtering when sampling the texture. + + + Perform cubic filtering when sampling the texture. + + + Disable mipmapping. + [b]Note:[/b] Mipmapping can only be disabled in the compatibility renderer. + + + Use the mipmap of the nearest resolution. + + + Use linear interpolation of the two mipmaps of the nearest resolution. + + + Clamp the texture to its specified border color. + + + Clamp the texture to its edge color. + + + Repeat the texture infinitely. + + + Repeat the texture infinitely, mirroring it on each repeat. + + + Mirror the texture once and then clamp the texture to its edge color. + [b]Note:[/b] This wrap mode is not available in the compatibility renderer. + + + Maps a color channel to the value of the red channel. + + + Maps a color channel to the value of the green channel. + + + Maps a color channel to the value of the blue channel. + + + Maps a color channel to the value of the alpha channel. + + + Maps a color channel to the value of zero. + + + Maps a color channel to the value of one. + + diff --git a/modules/openxr/extensions/openxr_composition_layer_extension.cpp b/modules/openxr/extensions/openxr_composition_layer_extension.cpp index f6270288913..56badf59189 100644 --- a/modules/openxr/extensions/openxr_composition_layer_extension.cpp +++ b/modules/openxr/extensions/openxr_composition_layer_extension.cpp @@ -35,6 +35,7 @@ #include #endif +#include "openxr_fb_update_swapchain_extension.h" #include "platform/android/api/java_class_wrapper.h" #include "servers/rendering/rendering_server_globals.h" @@ -227,6 +228,13 @@ void OpenXRViewportCompositionLayerProvider::set_viewport(RID p_viewport, Size2i void OpenXRViewportCompositionLayerProvider::set_use_android_surface(bool p_use_android_surface, Size2i p_size) { #ifdef ANDROID_ENABLED if (p_use_android_surface == use_android_surface) { + if (use_android_surface && swapchain_size != p_size) { + OpenXRFBUpdateSwapchainExtension *fb_update_swapchain_ext = OpenXRFBUpdateSwapchainExtension::get_singleton(); + if (fb_update_swapchain_ext && fb_update_swapchain_ext->is_android_ext_enabled()) { + swapchain_size = p_size; + fb_update_swapchain_ext->update_swapchain_surface_size(android_surface.swapchain, swapchain_size); + } + } return; } @@ -280,6 +288,8 @@ void OpenXRViewportCompositionLayerProvider::create_android_surface() { jobject surface; composition_layer_extension->create_android_surface_swapchain(&info, &android_surface.swapchain, &surface); + swapchain_state.dirty = true; + if (surface) { android_surface.surface.instantiate(JavaClassWrapper::get_singleton()->wrap("android.view.Surface"), surface); } @@ -327,6 +337,11 @@ void OpenXRViewportCompositionLayerProvider::on_pre_render() { } } } + + if (swapchain_state.dirty) { + update_swapchain_state(); + swapchain_state.dirty = false; + } } XrCompositionLayerBaseHeader *OpenXRViewportCompositionLayerProvider::get_composition_layer() { @@ -393,6 +408,34 @@ XrCompositionLayerBaseHeader *OpenXRViewportCompositionLayerProvider::get_compos return composition_layer; } +void OpenXRViewportCompositionLayerProvider::update_swapchain_state() { + OpenXRFBUpdateSwapchainExtension *fb_update_swapchain_ext = OpenXRFBUpdateSwapchainExtension::get_singleton(); + if (!fb_update_swapchain_ext) { + return; + } + +#ifdef ANDROID_ENABLED + if (use_android_surface) { + if (android_surface.swapchain == XR_NULL_HANDLE) { + return; + } + + fb_update_swapchain_ext->update_swapchain_state(android_surface.swapchain, &swapchain_state); + } else +#endif + { + if (subviewport.swapchain_info.get_swapchain() == XR_NULL_HANDLE) { + return; + } + + fb_update_swapchain_ext->update_swapchain_state(subviewport.swapchain_info.get_swapchain(), &swapchain_state); + } +} + +OpenXRViewportCompositionLayerProvider::SwapchainState *OpenXRViewportCompositionLayerProvider::get_swapchain_state() { + return &swapchain_state; +} + void OpenXRViewportCompositionLayerProvider::update_swapchain_sub_image(XrSwapchainSubImage &r_subimage) { #ifdef ANDROID_ENABLED if (use_android_surface) { @@ -451,6 +494,8 @@ bool OpenXRViewportCompositionLayerProvider::update_and_acquire_swapchain(bool p return false; } + swapchain_state.dirty = true; + // Acquire our image so we can start rendering into it, // we can ignore should_render here, ret will be false. bool should_render = true; diff --git a/modules/openxr/extensions/openxr_composition_layer_extension.h b/modules/openxr/extensions/openxr_composition_layer_extension.h index 7a4b08015f0..c4c16ee65de 100644 --- a/modules/openxr/extensions/openxr_composition_layer_extension.h +++ b/modules/openxr/extensions/openxr_composition_layer_extension.h @@ -96,6 +96,56 @@ private: }; class OpenXRViewportCompositionLayerProvider { +public: + // Must be identical to Filter enum definition in OpenXRCompositionLayer. + enum Filter { + FILTER_NEAREST, + FILTER_LINEAR, + FILTER_CUBIC, + }; + + // Must be identical to MipmapMode enum definition in OpenXRCompositionLayer. + enum MipmapMode { + MIPMAP_MODE_DISABLED, + MIPMAP_MODE_NEAREST, + MIPMAP_MODE_LINEAR, + }; + + // Must be identical to Wrap enum definition in OpenXRCompositionLayer. + enum Wrap { + WRAP_CLAMP_TO_BORDER, + WRAP_CLAMP_TO_EDGE, + WRAP_REPEAT, + WRAP_MIRRORED_REPEAT, + WRAP_MIRROR_CLAMP_TO_EDGE, + }; + + // Must be identical to Swizzle enum definition in OpenXRCompositionLayer. + enum Swizzle { + SWIZZLE_RED, + SWIZZLE_GREEN, + SWIZZLE_BLUE, + SWIZZLE_ALPHA, + SWIZZLE_ZERO, + SWIZZLE_ONE, + }; + + struct SwapchainState { + Filter min_filter = Filter::FILTER_LINEAR; + Filter mag_filter = Filter::FILTER_LINEAR; + MipmapMode mipmap_mode = MipmapMode::MIPMAP_MODE_LINEAR; + Wrap horizontal_wrap = Wrap::WRAP_CLAMP_TO_BORDER; + Wrap vertical_wrap = Wrap::WRAP_CLAMP_TO_BORDER; + Swizzle red_swizzle = Swizzle::SWIZZLE_RED; + Swizzle green_swizzle = Swizzle::SWIZZLE_GREEN; + Swizzle blue_swizzle = Swizzle::SWIZZLE_BLUE; + Swizzle alpha_swizzle = Swizzle::SWIZZLE_ALPHA; + float max_anisotropy = 1.0; + Color border_color = { 0.0, 0.0, 0.0, 0.0 }; + bool dirty = false; + }; + +private: XrCompositionLayerBaseHeader *composition_layer = nullptr; int sort_order = 1; bool alpha_blend = false; @@ -129,6 +179,8 @@ class OpenXRViewportCompositionLayerProvider { void update_swapchain_sub_image(XrSwapchainSubImage &r_swapchain_sub_image); void free_swapchain(); + SwapchainState swapchain_state; + #ifdef ANDROID_ENABLED void create_android_surface(); #endif @@ -155,6 +207,9 @@ public: void on_pre_render(); XrCompositionLayerBaseHeader *get_composition_layer(); + void update_swapchain_state(); + SwapchainState *get_swapchain_state(); + OpenXRViewportCompositionLayerProvider(XrCompositionLayerBaseHeader *p_composition_layer); ~OpenXRViewportCompositionLayerProvider(); }; diff --git a/modules/openxr/extensions/openxr_fb_update_swapchain_extension.cpp b/modules/openxr/extensions/openxr_fb_update_swapchain_extension.cpp index c3f692185b5..a3b31003fbd 100644 --- a/modules/openxr/extensions/openxr_fb_update_swapchain_extension.cpp +++ b/modules/openxr/extensions/openxr_fb_update_swapchain_extension.cpp @@ -33,6 +33,19 @@ // Always include this as late as possible. #include "../openxr_platform_inc.h" +#ifndef GL_CUBIC_IMG +#define GL_CUBIC_IMG 0x9139 +#endif +#ifndef GL_CUBIC_MIPMAP_LINEAR_IMG +#define GL_CUBIC_MIPMAP_LINEAR_IMG 0x913B +#endif +#ifndef GL_CUBIC_MIPMAP_NEAREST_IMG +#define GL_CUBIC_MIPMAP_NEAREST_IMG 0x913A +#endif +#ifndef GL_CLAMP_TO_BORDER +#define GL_CLAMP_TO_BORDER 0x812D +#endif + OpenXRFBUpdateSwapchainExtension *OpenXRFBUpdateSwapchainExtension::singleton = nullptr; OpenXRFBUpdateSwapchainExtension *OpenXRFBUpdateSwapchainExtension::get_singleton() { @@ -63,6 +76,10 @@ HashMap OpenXRFBUpdateSwapchainExtension::get_requested_extensio #endif } +#ifdef ANDROID_ENABLED + request_extensions[XR_FB_SWAPCHAIN_UPDATE_STATE_ANDROID_SURFACE_EXTENSION_NAME] = &fb_swapchain_update_state_android_ext; +#endif + return request_extensions; } @@ -100,3 +117,231 @@ bool OpenXRFBUpdateSwapchainExtension::is_enabled() const { return false; } + +bool OpenXRFBUpdateSwapchainExtension::is_android_ext_enabled() const { + return fb_swapchain_update_state_android_ext; +} + +void OpenXRFBUpdateSwapchainExtension::update_swapchain_state(XrSwapchain p_swapchain, const OpenXRViewportCompositionLayerProvider::SwapchainState *p_swapchain_state) { + if (!p_swapchain_state) { + return; + } + + if (rendering_driver == "vulkan") { +#ifdef XR_USE_GRAPHICS_API_VULKAN + if (!fb_swapchain_update_state_ext || !fb_swapchain_update_state_vulkan_ext) { + return; + } + + Color border_color = p_swapchain_state->border_color; + XrSwapchainStateSamplerVulkanFB swapchain_state = { + XR_TYPE_SWAPCHAIN_STATE_SAMPLER_VULKAN_FB, // type + nullptr, // next + (VkFilter)filter_to_vk(p_swapchain_state->min_filter), // minFilter + (VkFilter)filter_to_vk(p_swapchain_state->mag_filter), // magFilter + (VkSamplerMipmapMode)mipmap_mode_to_vk(p_swapchain_state->mipmap_mode), // mipmapMode + (VkSamplerAddressMode)wrap_to_vk(p_swapchain_state->horizontal_wrap), // wrapModeS; + (VkSamplerAddressMode)wrap_to_vk(p_swapchain_state->vertical_wrap), // wrapModeT + (VkComponentSwizzle)swizzle_to_vk(p_swapchain_state->red_swizzle), // swizzleRed + (VkComponentSwizzle)swizzle_to_vk(p_swapchain_state->green_swizzle), // swizzleGreen + (VkComponentSwizzle)swizzle_to_vk(p_swapchain_state->blue_swizzle), // swizzleBlue + (VkComponentSwizzle)swizzle_to_vk(p_swapchain_state->alpha_swizzle), // swizzleAlpha + p_swapchain_state->max_anisotropy, // maxAnisotropy + { border_color.r, border_color.g, border_color.b, border_color.a } // borderColor + }; + + XrResult result = xrUpdateSwapchainFB(p_swapchain, (XrSwapchainStateBaseHeaderFB *)&swapchain_state); + if (XR_FAILED(result)) { + print_error(vformat("OpenXR: Failed to update swapchain [%s]", OpenXRAPI::get_singleton()->get_error_string(result))); + return; + } +#endif + } else if (rendering_driver == "opengl3") { +#ifdef XR_USE_GRAPHICS_API_OPENGL_ES + if (!fb_swapchain_update_state_ext || !fb_swapchain_update_state_opengles_ext) { + return; + } + + Color border_color = p_swapchain_state->border_color; + XrSwapchainStateSamplerOpenGLESFB swapchain_state = { + XR_TYPE_SWAPCHAIN_STATE_SAMPLER_OPENGL_ES_FB, // type + nullptr, // next + filter_to_gl(p_swapchain_state->min_filter, p_swapchain_state->mipmap_mode), // minFilter + filter_to_gl(p_swapchain_state->mag_filter), // magFilter + wrap_to_gl(p_swapchain_state->horizontal_wrap), // wrapModeS; + wrap_to_gl(p_swapchain_state->vertical_wrap), // wrapModeT + swizzle_to_gl(p_swapchain_state->red_swizzle), // swizzleRed + swizzle_to_gl(p_swapchain_state->green_swizzle), // swizzleGreen + swizzle_to_gl(p_swapchain_state->blue_swizzle), // swizzleBlue + swizzle_to_gl(p_swapchain_state->alpha_swizzle), // swizzleAlpha + p_swapchain_state->max_anisotropy, // maxAnisotropy + { border_color.r, border_color.g, border_color.b, border_color.a } // borderColor + }; + + XrResult result = xrUpdateSwapchainFB(p_swapchain, (XrSwapchainStateBaseHeaderFB *)&swapchain_state); + if (XR_FAILED(result)) { + print_error(vformat("OpenXR: Failed to update swapchain [%s]", OpenXRAPI::get_singleton()->get_error_string(result))); + return; + } +#endif + } +} + +void OpenXRFBUpdateSwapchainExtension::update_swapchain_surface_size(XrSwapchain p_swapchain, const Size2i &p_size) { +#ifdef ANDROID_ENABLED + if (!fb_swapchain_update_state_ext || !fb_swapchain_update_state_android_ext) { + return; + } + + XrSwapchainStateAndroidSurfaceDimensionsFB swapchain_state = { + XR_TYPE_SWAPCHAIN_STATE_ANDROID_SURFACE_DIMENSIONS_FB, // type + nullptr, // next + (uint32_t)p_size.width, // width + (uint32_t)p_size.height // height + }; + + XrResult result = xrUpdateSwapchainFB(p_swapchain, (XrSwapchainStateBaseHeaderFB *)&swapchain_state); + if (XR_FAILED(result)) { + print_error(vformat("OpenXR: Failed to update swapchain surface size [%s]", OpenXRAPI::get_singleton()->get_error_string(result))); + } +#endif +} + +uint32_t OpenXRFBUpdateSwapchainExtension::filter_to_gl(OpenXRViewportCompositionLayerProvider::Filter p_filter, OpenXRViewportCompositionLayerProvider::MipmapMode p_mipmap_mode) { +#ifdef XR_USE_GRAPHICS_API_OPENGL_ES + switch (p_mipmap_mode) { + case OpenXRViewportCompositionLayerProvider::MipmapMode::MIPMAP_MODE_DISABLED: + switch (p_filter) { + case OpenXRViewportCompositionLayerProvider::Filter::FILTER_NEAREST: + return GL_NEAREST; + case OpenXRViewportCompositionLayerProvider::Filter::FILTER_LINEAR: + return GL_LINEAR; + case OpenXRViewportCompositionLayerProvider::Filter::FILTER_CUBIC: + return GL_CUBIC_IMG; + } + case OpenXRViewportCompositionLayerProvider::MipmapMode::MIPMAP_MODE_NEAREST: + switch (p_filter) { + case OpenXRViewportCompositionLayerProvider::Filter::FILTER_NEAREST: + return GL_NEAREST_MIPMAP_NEAREST; + case OpenXRViewportCompositionLayerProvider::Filter::FILTER_LINEAR: + return GL_LINEAR_MIPMAP_NEAREST; + case OpenXRViewportCompositionLayerProvider::Filter::FILTER_CUBIC: + return GL_CUBIC_MIPMAP_NEAREST_IMG; + } + case OpenXRViewportCompositionLayerProvider::MipmapMode::MIPMAP_MODE_LINEAR: + switch (p_filter) { + case OpenXRViewportCompositionLayerProvider::Filter::FILTER_NEAREST: + return GL_NEAREST_MIPMAP_LINEAR; + case OpenXRViewportCompositionLayerProvider::Filter::FILTER_LINEAR: + return GL_LINEAR_MIPMAP_LINEAR; + case OpenXRViewportCompositionLayerProvider::Filter::FILTER_CUBIC: + return GL_CUBIC_MIPMAP_LINEAR_IMG; + } + } +#endif + return 0; +} + +uint32_t OpenXRFBUpdateSwapchainExtension::wrap_to_gl(OpenXRViewportCompositionLayerProvider::Wrap p_wrap) { +#ifdef XR_USE_GRAPHICS_API_OPENGL_ES + switch (p_wrap) { + case OpenXRViewportCompositionLayerProvider::Wrap::WRAP_CLAMP_TO_BORDER: + return GL_CLAMP_TO_BORDER; + case OpenXRViewportCompositionLayerProvider::Wrap::WRAP_CLAMP_TO_EDGE: + return GL_CLAMP_TO_EDGE; + case OpenXRViewportCompositionLayerProvider::Wrap::WRAP_REPEAT: + return GL_REPEAT; + case OpenXRViewportCompositionLayerProvider::Wrap::WRAP_MIRRORED_REPEAT: + return GL_MIRRORED_REPEAT; + case OpenXRViewportCompositionLayerProvider::Wrap::WRAP_MIRROR_CLAMP_TO_EDGE: + return GL_CLAMP_TO_EDGE; + } +#endif + return 0; +} + +uint32_t OpenXRFBUpdateSwapchainExtension::swizzle_to_gl(OpenXRViewportCompositionLayerProvider::Swizzle p_swizzle) { +#ifdef XR_USE_GRAPHICS_API_OPENGL_ES + switch (p_swizzle) { + case OpenXRViewportCompositionLayerProvider::Swizzle::SWIZZLE_RED: + return GL_RED; + case OpenXRViewportCompositionLayerProvider::Swizzle::SWIZZLE_GREEN: + return GL_GREEN; + case OpenXRViewportCompositionLayerProvider::Swizzle::SWIZZLE_BLUE: + return GL_BLUE; + case OpenXRViewportCompositionLayerProvider::Swizzle::SWIZZLE_ALPHA: + return GL_ALPHA; + case OpenXRViewportCompositionLayerProvider::Swizzle::SWIZZLE_ZERO: + return GL_ZERO; + case OpenXRViewportCompositionLayerProvider::Swizzle::SWIZZLE_ONE: + return GL_ONE; + } +#endif + return 0; +} + +uint32_t OpenXRFBUpdateSwapchainExtension::filter_to_vk(OpenXRViewportCompositionLayerProvider::Filter p_filter) { +#ifdef XR_USE_GRAPHICS_API_VULKAN + switch (p_filter) { + case OpenXRViewportCompositionLayerProvider::Filter::FILTER_NEAREST: + return VK_FILTER_NEAREST; + case OpenXRViewportCompositionLayerProvider::Filter::FILTER_LINEAR: + return VK_FILTER_LINEAR; + case OpenXRViewportCompositionLayerProvider::Filter::FILTER_CUBIC: + return VK_FILTER_CUBIC_EXT; + } +#endif + return 0; +} + +uint32_t OpenXRFBUpdateSwapchainExtension::mipmap_mode_to_vk(OpenXRViewportCompositionLayerProvider::MipmapMode p_mipmap_mode) { +#ifdef XR_USE_GRAPHICS_API_VULKAN + switch (p_mipmap_mode) { + case OpenXRViewportCompositionLayerProvider::MipmapMode::MIPMAP_MODE_DISABLED: + return VK_SAMPLER_MIPMAP_MODE_LINEAR; + case OpenXRViewportCompositionLayerProvider::MipmapMode::MIPMAP_MODE_NEAREST: + return VK_SAMPLER_MIPMAP_MODE_NEAREST; + case OpenXRViewportCompositionLayerProvider::MipmapMode::MIPMAP_MODE_LINEAR: + return VK_SAMPLER_MIPMAP_MODE_LINEAR; + } +#endif + return 0; +} + +uint32_t OpenXRFBUpdateSwapchainExtension::wrap_to_vk(OpenXRViewportCompositionLayerProvider::Wrap p_wrap) { +#ifdef XR_USE_GRAPHICS_API_VULKAN + switch (p_wrap) { + case OpenXRViewportCompositionLayerProvider::Wrap::WRAP_CLAMP_TO_BORDER: + return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; + case OpenXRViewportCompositionLayerProvider::Wrap::WRAP_CLAMP_TO_EDGE: + return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + case OpenXRViewportCompositionLayerProvider::Wrap::WRAP_REPEAT: + return VK_SAMPLER_ADDRESS_MODE_REPEAT; + case OpenXRViewportCompositionLayerProvider::Wrap::WRAP_MIRRORED_REPEAT: + return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT; + case OpenXRViewportCompositionLayerProvider::Wrap::WRAP_MIRROR_CLAMP_TO_EDGE: + return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE; + } +#endif + return 0; +} + +uint32_t OpenXRFBUpdateSwapchainExtension::swizzle_to_vk(OpenXRViewportCompositionLayerProvider::Swizzle p_swizzle) { +#ifdef XR_USE_GRAPHICS_API_VULKAN + switch (p_swizzle) { + case OpenXRViewportCompositionLayerProvider::Swizzle::SWIZZLE_RED: + return VK_COMPONENT_SWIZZLE_R; + case OpenXRViewportCompositionLayerProvider::Swizzle::SWIZZLE_GREEN: + return VK_COMPONENT_SWIZZLE_G; + case OpenXRViewportCompositionLayerProvider::Swizzle::SWIZZLE_BLUE: + return VK_COMPONENT_SWIZZLE_B; + case OpenXRViewportCompositionLayerProvider::Swizzle::SWIZZLE_ALPHA: + return VK_COMPONENT_SWIZZLE_A; + case OpenXRViewportCompositionLayerProvider::Swizzle::SWIZZLE_ZERO: + return VK_COMPONENT_SWIZZLE_ZERO; + case OpenXRViewportCompositionLayerProvider::Swizzle::SWIZZLE_ONE: + return VK_COMPONENT_SWIZZLE_ONE; + } +#endif + return 0; +} diff --git a/modules/openxr/extensions/openxr_fb_update_swapchain_extension.h b/modules/openxr/extensions/openxr_fb_update_swapchain_extension.h index 26ee74937e5..cdaed6c1361 100644 --- a/modules/openxr/extensions/openxr_fb_update_swapchain_extension.h +++ b/modules/openxr/extensions/openxr_fb_update_swapchain_extension.h @@ -37,6 +37,7 @@ #include "../openxr_api.h" #include "../util.h" +#include "openxr_composition_layer_extension.h" #include "openxr_extension_wrapper.h" class OpenXRFBUpdateSwapchainExtension : public OpenXRExtensionWrapper { @@ -54,6 +55,11 @@ public: virtual void on_instance_destroyed() override; bool is_enabled() const; + bool is_android_ext_enabled() const; + + void update_swapchain_state(XrSwapchain p_swapchain, const OpenXRViewportCompositionLayerProvider::SwapchainState *p_swapchain_state); + + void update_swapchain_surface_size(XrSwapchain p_swapchain, const Size2i &p_size); private: static OpenXRFBUpdateSwapchainExtension *singleton; @@ -63,6 +69,16 @@ private: bool fb_swapchain_update_state_ext = false; bool fb_swapchain_update_state_vulkan_ext = false; bool fb_swapchain_update_state_opengles_ext = false; + bool fb_swapchain_update_state_android_ext = false; + + uint32_t filter_to_gl(OpenXRViewportCompositionLayerProvider::Filter p_filter, OpenXRViewportCompositionLayerProvider::MipmapMode p_mipmap_mode = OpenXRViewportCompositionLayerProvider::MipmapMode::MIPMAP_MODE_DISABLED); + uint32_t wrap_to_gl(OpenXRViewportCompositionLayerProvider::Wrap p_wrap); + uint32_t swizzle_to_gl(OpenXRViewportCompositionLayerProvider::Swizzle p_swizzle); + + uint32_t filter_to_vk(OpenXRViewportCompositionLayerProvider::Filter p_filter); + uint32_t mipmap_mode_to_vk(OpenXRViewportCompositionLayerProvider::MipmapMode p_mipmap); + uint32_t wrap_to_vk(OpenXRViewportCompositionLayerProvider::Wrap p_wrap); + uint32_t swizzle_to_vk(OpenXRViewportCompositionLayerProvider::Swizzle p_swizzle); // OpenXR API call wrappers EXT_PROTO_XRRESULT_FUNC2(xrUpdateSwapchainFB, (XrSwapchain), swapchain, (const XrSwapchainStateBaseHeaderFB *), state); diff --git a/modules/openxr/scene/openxr_composition_layer.cpp b/modules/openxr/scene/openxr_composition_layer.cpp index 6122e892fc9..acd529826d4 100644 --- a/modules/openxr/scene/openxr_composition_layer.cpp +++ b/modules/openxr/scene/openxr_composition_layer.cpp @@ -30,7 +30,6 @@ #include "openxr_composition_layer.h" -#include "../extensions/openxr_composition_layer_extension.h" #include "../openxr_api.h" #include "../openxr_interface.h" @@ -52,6 +51,7 @@ static const char *HOLE_PUNCH_SHADER_CODE = OpenXRCompositionLayer::OpenXRCompositionLayer(XrCompositionLayerBaseHeader *p_composition_layer) { composition_layer_base_header = p_composition_layer; openxr_layer_provider = memnew(OpenXRViewportCompositionLayerProvider(composition_layer_base_header)); + swapchain_state = openxr_layer_provider->get_swapchain_state(); openxr_api = OpenXRAPI::get_singleton(); composition_layer_extension = OpenXRCompositionLayerExtension::get_singleton(); @@ -113,6 +113,39 @@ void OpenXRCompositionLayer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_android_surface"), &OpenXRCompositionLayer::get_android_surface); ClassDB::bind_method(D_METHOD("is_natively_supported"), &OpenXRCompositionLayer::is_natively_supported); + ClassDB::bind_method(D_METHOD("set_min_filter", "mode"), &OpenXRCompositionLayer::set_min_filter); + ClassDB::bind_method(D_METHOD("get_min_filter"), &OpenXRCompositionLayer::get_min_filter); + + ClassDB::bind_method(D_METHOD("set_mag_filter", "mode"), &OpenXRCompositionLayer::set_mag_filter); + ClassDB::bind_method(D_METHOD("get_mag_filter"), &OpenXRCompositionLayer::get_mag_filter); + + ClassDB::bind_method(D_METHOD("set_mipmap_mode", "mode"), &OpenXRCompositionLayer::set_mipmap_mode); + ClassDB::bind_method(D_METHOD("get_mipmap_mode"), &OpenXRCompositionLayer::get_mipmap_mode); + + ClassDB::bind_method(D_METHOD("set_horizontal_wrap", "mode"), &OpenXRCompositionLayer::set_horizontal_wrap); + ClassDB::bind_method(D_METHOD("get_horizontal_wrap"), &OpenXRCompositionLayer::get_horizontal_wrap); + + ClassDB::bind_method(D_METHOD("set_vertical_wrap", "mode"), &OpenXRCompositionLayer::set_vertical_wrap); + ClassDB::bind_method(D_METHOD("get_vertical_wrap"), &OpenXRCompositionLayer::get_vertical_wrap); + + ClassDB::bind_method(D_METHOD("set_red_swizzle", "mode"), &OpenXRCompositionLayer::set_red_swizzle); + ClassDB::bind_method(D_METHOD("get_red_swizzle"), &OpenXRCompositionLayer::get_red_swizzle); + + ClassDB::bind_method(D_METHOD("set_green_swizzle", "mode"), &OpenXRCompositionLayer::set_green_swizzle); + ClassDB::bind_method(D_METHOD("get_green_swizzle"), &OpenXRCompositionLayer::get_green_swizzle); + + ClassDB::bind_method(D_METHOD("set_blue_swizzle", "mode"), &OpenXRCompositionLayer::set_blue_swizzle); + ClassDB::bind_method(D_METHOD("get_blue_swizzle"), &OpenXRCompositionLayer::get_blue_swizzle); + + ClassDB::bind_method(D_METHOD("set_alpha_swizzle", "mode"), &OpenXRCompositionLayer::set_alpha_swizzle); + ClassDB::bind_method(D_METHOD("get_alpha_swizzle"), &OpenXRCompositionLayer::get_alpha_swizzle); + + ClassDB::bind_method(D_METHOD("set_max_anisotropy", "value"), &OpenXRCompositionLayer::set_max_anisotropy); + ClassDB::bind_method(D_METHOD("get_max_anisotropy"), &OpenXRCompositionLayer::get_max_anisotropy); + + ClassDB::bind_method(D_METHOD("set_border_color", "color"), &OpenXRCompositionLayer::set_border_color); + ClassDB::bind_method(D_METHOD("get_border_color"), &OpenXRCompositionLayer::get_border_color); + ClassDB::bind_method(D_METHOD("intersects_ray", "origin", "direction"), &OpenXRCompositionLayer::intersects_ray); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "layer_viewport", PROPERTY_HINT_NODE_TYPE, "SubViewport"), "set_layer_viewport", "get_layer_viewport"); @@ -121,6 +154,41 @@ void OpenXRCompositionLayer::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "sort_order", PROPERTY_HINT_NONE, ""), "set_sort_order", "get_sort_order"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "alpha_blend", PROPERTY_HINT_NONE, ""), "set_alpha_blend", "get_alpha_blend"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enable_hole_punch", PROPERTY_HINT_NONE, ""), "set_enable_hole_punch", "get_enable_hole_punch"); + + ADD_GROUP("Swapchain State", "swapchain_state_"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "swapchain_state_min_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,Cubic"), "set_min_filter", "get_min_filter"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "swapchain_state_mag_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,Cubic"), "set_mag_filter", "get_mag_filter"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "swapchain_state_mipmap_mode", PROPERTY_HINT_ENUM, "Disabled,Nearest,Linear"), "set_mipmap_mode", "get_mipmap_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "swapchain_state_horizontal_wrap", PROPERTY_HINT_ENUM, "Clamp to Border,Clamp to Edge,Repeat,Mirrored Repeat,Mirror Clamp to Edge"), "set_horizontal_wrap", "get_horizontal_wrap"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "swapchain_state_vertical_wrap", PROPERTY_HINT_ENUM, "Clamp to Border,Clamp to Edge,Repeat,Mirrored Repeat,Mirror Clamp to Edge"), "set_vertical_wrap", "get_vertical_wrap"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "swapchain_state_red_swizzle", PROPERTY_HINT_ENUM, "Red,Green,Blue,Alpha,Zero,One"), "set_red_swizzle", "get_red_swizzle"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "swapchain_state_green_swizzle", PROPERTY_HINT_ENUM, "Red,Green,Blue,Alpha,Zero,One"), "set_green_swizzle", "get_green_swizzle"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "swapchain_state_blue_swizzle", PROPERTY_HINT_ENUM, "Red,Green,Blue,Alpha,Zero,One"), "set_blue_swizzle", "get_blue_swizzle"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "swapchain_state_alpha_swizzle", PROPERTY_HINT_ENUM, "Red,Green,Blue,Alpha,Zero,One"), "set_alpha_swizzle", "get_alpha_swizzle"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "swapchain_state_max_anisotropy", PROPERTY_HINT_RANGE, "1.0,16.0,0.001"), "set_max_anisotropy", "get_max_anisotropy"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "swapchain_state_border_color"), "set_border_color", "get_border_color"); + ADD_GROUP("", ""); + + BIND_ENUM_CONSTANT(FILTER_NEAREST); + BIND_ENUM_CONSTANT(FILTER_LINEAR); + BIND_ENUM_CONSTANT(FILTER_CUBIC); + + BIND_ENUM_CONSTANT(MIPMAP_MODE_DISABLED); + BIND_ENUM_CONSTANT(MIPMAP_MODE_NEAREST); + BIND_ENUM_CONSTANT(MIPMAP_MODE_LINEAR); + + BIND_ENUM_CONSTANT(WRAP_CLAMP_TO_BORDER); + BIND_ENUM_CONSTANT(WRAP_CLAMP_TO_EDGE); + BIND_ENUM_CONSTANT(WRAP_REPEAT); + BIND_ENUM_CONSTANT(WRAP_MIRRORED_REPEAT); + BIND_ENUM_CONSTANT(WRAP_MIRROR_CLAMP_TO_EDGE); + + BIND_ENUM_CONSTANT(SWIZZLE_RED); + BIND_ENUM_CONSTANT(SWIZZLE_GREEN); + BIND_ENUM_CONSTANT(SWIZZLE_BLUE); + BIND_ENUM_CONSTANT(SWIZZLE_ALPHA); + BIND_ENUM_CONSTANT(SWIZZLE_ZERO); + BIND_ENUM_CONSTANT(SWIZZLE_ONE); } bool OpenXRCompositionLayer::_should_use_fallback_node() { @@ -337,6 +405,149 @@ bool OpenXRCompositionLayer::is_natively_supported() const { return false; } +void OpenXRCompositionLayer::set_min_filter(Filter p_mode) { + if (swapchain_state->min_filter == (OpenXRViewportCompositionLayerProvider::Filter)p_mode) { + return; + } + + swapchain_state->min_filter = (OpenXRViewportCompositionLayerProvider::Filter)p_mode; + swapchain_state->dirty = true; +} + +OpenXRCompositionLayer::Filter OpenXRCompositionLayer::get_min_filter() const { + return (OpenXRCompositionLayer::Filter)swapchain_state->min_filter; +} + +void OpenXRCompositionLayer::set_mag_filter(Filter p_mode) { + if (swapchain_state->mag_filter == (OpenXRViewportCompositionLayerProvider::Filter)p_mode) { + return; + } + + swapchain_state->mag_filter = (OpenXRViewportCompositionLayerProvider::Filter)p_mode; + swapchain_state->dirty = true; +} + +OpenXRCompositionLayer::Filter OpenXRCompositionLayer::get_mag_filter() const { + return (OpenXRCompositionLayer::Filter)swapchain_state->mag_filter; +} + +void OpenXRCompositionLayer::set_mipmap_mode(MipmapMode p_mode) { + if (swapchain_state->mipmap_mode == (OpenXRViewportCompositionLayerProvider::MipmapMode)p_mode) { + return; + } + + swapchain_state->mipmap_mode = (OpenXRViewportCompositionLayerProvider::MipmapMode)p_mode; + swapchain_state->dirty = true; +} + +OpenXRCompositionLayer::MipmapMode OpenXRCompositionLayer::get_mipmap_mode() const { + return (OpenXRCompositionLayer::MipmapMode)swapchain_state->mipmap_mode; +} + +void OpenXRCompositionLayer::set_horizontal_wrap(Wrap p_mode) { + if (swapchain_state->horizontal_wrap == (OpenXRViewportCompositionLayerProvider::Wrap)p_mode) { + return; + } + + swapchain_state->horizontal_wrap = (OpenXRViewportCompositionLayerProvider::Wrap)p_mode; + swapchain_state->dirty = true; +} + +OpenXRCompositionLayer::Wrap OpenXRCompositionLayer::get_horizontal_wrap() const { + return (OpenXRCompositionLayer::Wrap)swapchain_state->horizontal_wrap; +} + +void OpenXRCompositionLayer::set_vertical_wrap(Wrap p_mode) { + if (swapchain_state->vertical_wrap == (OpenXRViewportCompositionLayerProvider::Wrap)p_mode) { + return; + } + + swapchain_state->vertical_wrap = (OpenXRViewportCompositionLayerProvider::Wrap)p_mode; + swapchain_state->dirty = true; +} + +OpenXRCompositionLayer::Wrap OpenXRCompositionLayer::get_vertical_wrap() const { + return (OpenXRCompositionLayer::Wrap)swapchain_state->vertical_wrap; +} + +void OpenXRCompositionLayer::set_red_swizzle(Swizzle p_mode) { + if (swapchain_state->red_swizzle == (OpenXRViewportCompositionLayerProvider::Swizzle)p_mode) { + return; + } + + swapchain_state->red_swizzle = (OpenXRViewportCompositionLayerProvider::Swizzle)p_mode; + swapchain_state->dirty = true; +} + +OpenXRCompositionLayer::Swizzle OpenXRCompositionLayer::get_red_swizzle() const { + return (OpenXRCompositionLayer::Swizzle)swapchain_state->red_swizzle; +} + +void OpenXRCompositionLayer::set_green_swizzle(Swizzle p_mode) { + if (swapchain_state->green_swizzle == (OpenXRViewportCompositionLayerProvider::Swizzle)p_mode) { + return; + } + + swapchain_state->green_swizzle = (OpenXRViewportCompositionLayerProvider::Swizzle)p_mode; + swapchain_state->dirty = true; +} + +OpenXRCompositionLayer::Swizzle OpenXRCompositionLayer::get_green_swizzle() const { + return (OpenXRCompositionLayer::Swizzle)swapchain_state->green_swizzle; +} + +void OpenXRCompositionLayer::set_blue_swizzle(Swizzle p_mode) { + if (swapchain_state->blue_swizzle == (OpenXRViewportCompositionLayerProvider::Swizzle)p_mode) { + return; + } + + swapchain_state->blue_swizzle = (OpenXRViewportCompositionLayerProvider::Swizzle)p_mode; + swapchain_state->dirty = true; +} + +OpenXRCompositionLayer::Swizzle OpenXRCompositionLayer::get_blue_swizzle() const { + return (OpenXRCompositionLayer::Swizzle)swapchain_state->blue_swizzle; +} + +void OpenXRCompositionLayer::set_alpha_swizzle(Swizzle p_mode) { + if (swapchain_state->alpha_swizzle == (OpenXRViewportCompositionLayerProvider::Swizzle)p_mode) { + return; + } + + swapchain_state->alpha_swizzle = (OpenXRViewportCompositionLayerProvider::Swizzle)p_mode; + swapchain_state->dirty = true; +} + +OpenXRCompositionLayer::Swizzle OpenXRCompositionLayer::get_alpha_swizzle() const { + return (OpenXRCompositionLayer::Swizzle)swapchain_state->alpha_swizzle; +} + +void OpenXRCompositionLayer::set_max_anisotropy(float p_value) { + if (swapchain_state->max_anisotropy == p_value) { + return; + } + + swapchain_state->max_anisotropy = p_value; + swapchain_state->dirty = true; +} + +float OpenXRCompositionLayer::get_max_anisotropy() const { + return swapchain_state->max_anisotropy; +} + +void OpenXRCompositionLayer::set_border_color(Color p_color) { + if (swapchain_state->border_color == p_color) { + return; + } + + swapchain_state->border_color = p_color; + swapchain_state->dirty = true; +} + +Color OpenXRCompositionLayer::get_border_color() const { + return swapchain_state->border_color; +} + Ref OpenXRCompositionLayer::get_android_surface() { return openxr_layer_provider->get_android_surface(); } diff --git a/modules/openxr/scene/openxr_composition_layer.h b/modules/openxr/scene/openxr_composition_layer.h index 85c7732e352..bfb6bffee25 100644 --- a/modules/openxr/scene/openxr_composition_layer.h +++ b/modules/openxr/scene/openxr_composition_layer.h @@ -32,6 +32,7 @@ #include +#include "../extensions/openxr_composition_layer_extension.h" #include "scene/3d/node_3d.h" class JavaObject; @@ -45,6 +46,41 @@ class SubViewport; class OpenXRCompositionLayer : public Node3D { GDCLASS(OpenXRCompositionLayer, Node3D); +public: + // Must be identical to Filter enum definition in OpenXRViewportCompositionLayerProvider. + enum Filter { + FILTER_NEAREST, + FILTER_LINEAR, + FILTER_CUBIC, + }; + + // Must be identical to MipmapMode enum definition in OpenXRViewportCompositionLayerProvider. + enum MipmapMode { + MIPMAP_MODE_DISABLED, + MIPMAP_MODE_NEAREST, + MIPMAP_MODE_LINEAR, + }; + + // Must be identical to Wrap enum definition in OpenXRViewportCompositionLayerProvider. + enum Wrap { + WRAP_CLAMP_TO_BORDER, + WRAP_CLAMP_TO_EDGE, + WRAP_REPEAT, + WRAP_MIRRORED_REPEAT, + WRAP_MIRROR_CLAMP_TO_EDGE, + }; + + // Must be identical to Swizzle enum definition in OpenXRViewportCompositionLayerProvider. + enum Swizzle { + SWIZZLE_RED, + SWIZZLE_GREEN, + SWIZZLE_BLUE, + SWIZZLE_ALPHA, + SWIZZLE_ZERO, + SWIZZLE_ONE, + }; + +private: XrCompositionLayerBaseHeader *composition_layer_base_header = nullptr; OpenXRViewportCompositionLayerProvider *openxr_layer_provider = nullptr; @@ -56,6 +92,8 @@ class OpenXRCompositionLayer : public Node3D { bool should_update_fallback_mesh = false; bool openxr_session_running = false; + OpenXRViewportCompositionLayerProvider::SwapchainState *swapchain_state = nullptr; + Dictionary extension_property_values; bool _should_use_fallback_node(); @@ -114,9 +152,47 @@ public: Ref get_android_surface(); bool is_natively_supported() const; + void set_min_filter(Filter p_mode); + Filter get_min_filter() const; + + void set_mag_filter(Filter p_mode); + Filter get_mag_filter() const; + + void set_mipmap_mode(MipmapMode p_mode); + MipmapMode get_mipmap_mode() const; + + void set_horizontal_wrap(Wrap p_mode); + Wrap get_horizontal_wrap() const; + + void set_vertical_wrap(Wrap p_mode); + Wrap get_vertical_wrap() const; + + void set_red_swizzle(Swizzle p_mode); + Swizzle get_red_swizzle() const; + + void set_green_swizzle(Swizzle p_mode); + Swizzle get_green_swizzle() const; + + void set_blue_swizzle(Swizzle p_mode); + Swizzle get_blue_swizzle() const; + + void set_alpha_swizzle(Swizzle p_mode); + Swizzle get_alpha_swizzle() const; + + void set_max_anisotropy(float p_value); + float get_max_anisotropy() const; + + void set_border_color(Color p_color); + Color get_border_color() const; + virtual PackedStringArray get_configuration_warnings() const override; virtual Vector2 intersects_ray(const Vector3 &p_origin, const Vector3 &p_direction) const; ~OpenXRCompositionLayer(); }; + +VARIANT_ENUM_CAST(OpenXRCompositionLayer::Filter) +VARIANT_ENUM_CAST(OpenXRCompositionLayer::MipmapMode) +VARIANT_ENUM_CAST(OpenXRCompositionLayer::Wrap) +VARIANT_ENUM_CAST(OpenXRCompositionLayer::Swizzle)