From 9042ddf19f33445abbb69a8330fff7e98fcda2dc Mon Sep 17 00:00:00 2001 From: Bastiaan Olij Date: Tue, 26 Mar 2024 12:57:26 +1100 Subject: [PATCH] Improvements to VRS/Foveated rendering --- doc/classes/RenderingServer.xml | 23 ++- doc/classes/Viewport.xml | 15 ++ doc/classes/XRVRS.xml | 30 ++++ .../d3d12/rendering_device_driver_d3d12.cpp | 4 + drivers/d3d12/rendering_device_driver_d3d12.h | 1 + drivers/gles3/storage/texture_storage.h | 2 + .../vulkan/rendering_device_driver_vulkan.cpp | 8 +- .../vulkan/rendering_device_driver_vulkan.h | 1 + .../doc_classes/MobileVRInterface.xml | 8 + modules/mobile_vr/mobile_vr_interface.cpp | 54 ++++++- modules/mobile_vr/mobile_vr_interface.h | 11 ++ .../openxr/doc_classes/OpenXRInterface.xml | 8 + .../openxr_eye_gaze_interaction.cpp | 36 +++++ .../extensions/openxr_eye_gaze_interaction.h | 6 + modules/openxr/openxr_api.cpp | 85 ++++++++-- modules/openxr/openxr_api.h | 4 + modules/openxr/openxr_interface.cpp | 44 +++++ modules/openxr/openxr_interface.h | 10 ++ scene/main/viewport.cpp | 35 ++++ scene/main/viewport.h | 12 ++ servers/register_server_types.cpp | 1 + .../rendering/dummy/storage/texture_storage.h | 2 + servers/rendering/renderer_rd/effects/vrs.cpp | 22 ++- servers/rendering/renderer_rd/effects/vrs.h | 7 +- .../renderer_rd/shaders/effects/vrs.glsl | 42 +++-- .../storage_rd/texture_storage.cpp | 14 ++ .../renderer_rd/storage_rd/texture_storage.h | 3 + servers/rendering/renderer_viewport.cpp | 7 + servers/rendering/renderer_viewport.h | 1 + servers/rendering/rendering_device_commons.h | 2 + servers/rendering/rendering_server_default.h | 1 + servers/rendering/storage/texture_storage.h | 2 + servers/rendering_server.cpp | 8 +- servers/rendering_server.h | 9 ++ servers/xr/xr_interface.cpp | 83 +--------- servers/xr/xr_interface.h | 13 +- servers/xr/xr_vrs.cpp | 151 ++++++++++++++++++ servers/xr/xr_vrs.h | 67 ++++++++ 38 files changed, 708 insertions(+), 124 deletions(-) create mode 100644 doc/classes/XRVRS.xml create mode 100644 servers/xr/xr_vrs.cpp create mode 100644 servers/xr/xr_vrs.h diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index 519bba4e7c4d..27bf2587d47a 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -4036,6 +4036,15 @@ The texture to use when the VRS mode is set to [constant RenderingServer.VIEWPORT_VRS_TEXTURE]. Equivalent to [member ProjectSettings.rendering/vrs/texture]. + + + + + + Sets the update mode for Variable Rate Shading (VRS) for the viewport. VRS requires the input texture to be converted to the format usable by the VRS method supported by the hardware. The update mode defines how often this happens. If the GPU does not support VRS, or VRS is not enabled, this property is ignored. + If set to [constant RenderingServer.VIEWPORT_VRS_UPDATE_ONCE], the input texture is copied once and the mode is changed to [constant RenderingServer.VIEWPORT_VRS_UPDATE_DISABLED]. + + @@ -4976,11 +4985,23 @@ Variable rate shading uses a texture. Note, for stereoscopic use a texture atlas with a texture for each view. - Variable rate shading texture is supplied by the primary [XRInterface]. + Variable rate shading texture is supplied by the primary [XRInterface]. Note that this may override the update mode. Represents the size of the [enum ViewportVRSMode] enum. + + The input texture for variable rate shading will not be processed. + + + The input texture for variable rate shading will be processed once. + + + The input texture for variable rate shading will be processed each frame. + + + Represents the size of the [enum ViewportVRSUpdateMode] enum. + Automatically selects the appropriate process mode based on your sky shader. If your shader uses [code]TIME[/code] or [code]POSITION[/code], this will use [constant SKY_MODE_REALTIME]. If your shader uses any of the [code]LIGHT_*[/code] variables or any custom uniforms, this uses [constant SKY_MODE_INCREMENTAL]. Otherwise, this defaults to [constant SKY_MODE_QUALITY]. diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml index cf33eac9c7f3..b288ee7ff63a 100644 --- a/doc/classes/Viewport.xml +++ b/doc/classes/Viewport.xml @@ -407,6 +407,9 @@ - 8×8 = rgb(255, 255, 0) - #ffff00 - Not supported on most hardware [/codeblock] + + Sets the update mode for Variable Rate Shading (VRS) for the viewport. VRS requires the input texture to be converted to the format usable by the VRS method supported by the hardware. The update mode defines how often this happens. If the GPU does not support VRS, or VRS is not enabled, this property is ignored. + The custom [World2D] which can be used as 2D environment source. @@ -664,5 +667,17 @@ Represents the size of the [enum VRSMode] enum. + + The input texture for variable rate shading will not be processed. + + + The input texture for variable rate shading will be processed once. + + + The input texture for variable rate shading will be processed each frame. + + + Represents the size of the [enum VRSUpdateMode] enum. + diff --git a/doc/classes/XRVRS.xml b/doc/classes/XRVRS.xml new file mode 100644 index 000000000000..b19b461fab6b --- /dev/null +++ b/doc/classes/XRVRS.xml @@ -0,0 +1,30 @@ + + + + Helper class for XR interfaces that generates VRS images. + + + This class is used by various XR interfaces to generate VRS textures that can be used to speed up rendering. + + + + + + + + + + Generates the VRS texture based on a render [param target_size] adjusted by our VRS tile size. For each eyes focal point passed in [param eye_foci] a layer is created. Focal point should be in NDC. + The result will be cached, requesting a VRS texture with unchanged parameters and settings will return the cached RID. + + + + + + The minimum radius around the focal point where full quality is guaranteed if VRS is used as a percentage of screen size. + + + The strength used to calculate the VRS density map. The greater this value, the more noticeable VRS is. + + + diff --git a/drivers/d3d12/rendering_device_driver_d3d12.cpp b/drivers/d3d12/rendering_device_driver_d3d12.cpp index fc7aa62aae2d..9407826ebfa6 100644 --- a/drivers/d3d12/rendering_device_driver_d3d12.cpp +++ b/drivers/d3d12/rendering_device_driver_d3d12.cpp @@ -5912,6 +5912,9 @@ uint64_t RenderingDeviceDriverD3D12::limit_get(Limit p_limit) { case LIMIT_VRS_TEXEL_WIDTH: case LIMIT_VRS_TEXEL_HEIGHT: return vrs_capabilities.ss_image_tile_size; + case LIMIT_VRS_MAX_FRAGMENT_WIDTH: + case LIMIT_VRS_MAX_FRAGMENT_HEIGHT: + return vrs_capabilities.ss_max_fragment_size; default: { #ifdef DEV_ENABLED WARN_PRINT("Returning maximum value for unknown limit " + itos(p_limit) + "."); @@ -6218,6 +6221,7 @@ Error RenderingDeviceDriverD3D12::_check_capabilities() { vrs_capabilities.primitive_in_multiviewport = options6.PerPrimitiveShadingRateSupportedWithViewportIndexing; vrs_capabilities.ss_image_supported = true; vrs_capabilities.ss_image_tile_size = options6.ShadingRateImageTileSize; + vrs_capabilities.ss_max_fragment_size = 8; // TODO figure out if this is supplied and/or needed vrs_capabilities.additional_rates_supported = options6.AdditionalShadingRatesSupported; } } diff --git a/drivers/d3d12/rendering_device_driver_d3d12.h b/drivers/d3d12/rendering_device_driver_d3d12.h index 61da9a51695d..8e1223bdaad7 100644 --- a/drivers/d3d12/rendering_device_driver_d3d12.h +++ b/drivers/d3d12/rendering_device_driver_d3d12.h @@ -105,6 +105,7 @@ class RenderingDeviceDriverD3D12 : public RenderingDeviceDriver { bool primitive_in_multiviewport = false; bool ss_image_supported = false; // We can provide a density map attachment on our framebuffer. uint32_t ss_image_tile_size = 0; + uint32_t ss_max_fragment_size = 0; bool additional_rates_supported = false; }; diff --git a/drivers/gles3/storage/texture_storage.h b/drivers/gles3/storage/texture_storage.h index ef310262c7a0..8a03d72b9b93 100644 --- a/drivers/gles3/storage/texture_storage.h +++ b/drivers/gles3/storage/texture_storage.h @@ -672,6 +672,8 @@ public: virtual void render_target_set_vrs_mode(RID p_render_target, RS::ViewportVRSMode p_mode) override {} virtual RS::ViewportVRSMode render_target_get_vrs_mode(RID p_render_target) const override { return RS::VIEWPORT_VRS_DISABLED; } + virtual void render_target_set_vrs_update_mode(RID p_render_target, RS::ViewportVRSUpdateMode p_mode) override {} + virtual RS::ViewportVRSUpdateMode render_target_get_vrs_update_mode(RID p_render_target) const override { return RS::VIEWPORT_VRS_UPDATE_DISABLED; } virtual void render_target_set_vrs_texture(RID p_render_target, RID p_texture) override {} virtual RID render_target_get_vrs_texture(RID p_render_target) const override { return RID(); } diff --git a/drivers/vulkan/rendering_device_driver_vulkan.cpp b/drivers/vulkan/rendering_device_driver_vulkan.cpp index 803555cb0752..339d0782bfa9 100644 --- a/drivers/vulkan/rendering_device_driver_vulkan.cpp +++ b/drivers/vulkan/rendering_device_driver_vulkan.cpp @@ -758,11 +758,13 @@ Error RenderingDeviceDriverVulkan::_check_device_capabilities() { vrs_capabilities.min_texel_size.y = vrs_properties.minFragmentShadingRateAttachmentTexelSize.height; vrs_capabilities.max_texel_size.x = vrs_properties.maxFragmentShadingRateAttachmentTexelSize.width; vrs_capabilities.max_texel_size.y = vrs_properties.maxFragmentShadingRateAttachmentTexelSize.height; + vrs_capabilities.max_fragment_size.x = vrs_properties.maxFragmentSize.width; // either 4 or 8 + vrs_capabilities.max_fragment_size.y = vrs_properties.maxFragmentSize.height; // generally the same as width // We'll attempt to default to a texel size of 16x16. vrs_capabilities.texel_size = Vector2i(16, 16).clamp(vrs_capabilities.min_texel_size, vrs_capabilities.max_texel_size); - print_verbose(String(" Attachment fragment shading rate") + String(", min texel size: (") + itos(vrs_capabilities.min_texel_size.x) + String(", ") + itos(vrs_capabilities.min_texel_size.y) + String(")") + String(", max texel size: (") + itos(vrs_capabilities.max_texel_size.x) + String(", ") + itos(vrs_capabilities.max_texel_size.y) + String(")")); + print_verbose(String(" Attachment fragment shading rate") + String(", min texel size: (") + itos(vrs_capabilities.min_texel_size.x) + String(", ") + itos(vrs_capabilities.min_texel_size.y) + String(")") + String(", max texel size: (") + itos(vrs_capabilities.max_texel_size.x) + String(", ") + itos(vrs_capabilities.max_texel_size.y) + String(")") + String(", max fragment size: (") + itos(vrs_capabilities.max_fragment_size.x) + String(", ") + itos(vrs_capabilities.max_fragment_size.y) + String(")")); } } else { @@ -4887,6 +4889,10 @@ uint64_t RenderingDeviceDriverVulkan::limit_get(Limit p_limit) { return vrs_capabilities.texel_size.x; case LIMIT_VRS_TEXEL_HEIGHT: return vrs_capabilities.texel_size.y; + case LIMIT_VRS_MAX_FRAGMENT_WIDTH: + return vrs_capabilities.max_fragment_size.x; + case LIMIT_VRS_MAX_FRAGMENT_HEIGHT: + return vrs_capabilities.max_fragment_size.y; default: ERR_FAIL_V(0); } diff --git a/drivers/vulkan/rendering_device_driver_vulkan.h b/drivers/vulkan/rendering_device_driver_vulkan.h index 70c4cebba5ed..e70019962a8c 100644 --- a/drivers/vulkan/rendering_device_driver_vulkan.h +++ b/drivers/vulkan/rendering_device_driver_vulkan.h @@ -87,6 +87,7 @@ class RenderingDeviceDriverVulkan : public RenderingDeviceDriver { Size2i min_texel_size; Size2i max_texel_size; + Size2i max_fragment_size; Size2i texel_size; // The texel size we'll use }; diff --git a/modules/mobile_vr/doc_classes/MobileVRInterface.xml b/modules/mobile_vr/doc_classes/MobileVRInterface.xml index 83380541426d..0dbe06d22077 100644 --- a/modules/mobile_vr/doc_classes/MobileVRInterface.xml +++ b/modules/mobile_vr/doc_classes/MobileVRInterface.xml @@ -40,6 +40,14 @@ The oversample setting. Because of the lens distortion we have to render our buffers at a higher resolution then the screen can natively handle. A value between 1.5 and 2.0 often provides good results but at the cost of performance. + + The minimum radius around the focal point where full quality is guaranteed if VRS is used as a percentage of screen size. + [b]Note:[/b] Mobile and Forward+ renderers only. Requires [member Viewport.vrs_mode] to be set to [constant Viewport.VRS_XR]. + + + The strength used to calculate the VRS density map. The greater this value, the more noticeable VRS is. This improves performance at the cost of quality. + [b]Note:[/b] Mobile and Forward+ renderers only. Requires [member Viewport.vrs_mode] to be set to [constant Viewport.VRS_XR]. + diff --git a/modules/mobile_vr/mobile_vr_interface.cpp b/modules/mobile_vr/mobile_vr_interface.cpp index d23edcd1d18c..f27281866a58 100644 --- a/modules/mobile_vr/mobile_vr_interface.cpp +++ b/modules/mobile_vr/mobile_vr_interface.cpp @@ -250,6 +250,16 @@ void MobileVRInterface::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "oversample", PROPERTY_HINT_RANGE, "1.0,2.0,0.1"), "set_oversample", "get_oversample"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "k1", PROPERTY_HINT_RANGE, "0.1,10.0,0.0001"), "set_k1", "get_k1"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "k2", PROPERTY_HINT_RANGE, "0.1,10.0,0.0001"), "set_k2", "get_k2"); + + ClassDB::bind_method(D_METHOD("get_vrs_min_radius"), &MobileVRInterface::get_vrs_min_radius); + ClassDB::bind_method(D_METHOD("set_vrs_min_radius", "radius"), &MobileVRInterface::set_vrs_min_radius); + + ClassDB::bind_method(D_METHOD("get_vrs_strength"), &MobileVRInterface::get_vrs_strength); + ClassDB::bind_method(D_METHOD("set_vrs_strength", "strength"), &MobileVRInterface::set_vrs_strength); + + ADD_GROUP("Vulkan VRS", "vrs_"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "vrs_min_radius", PROPERTY_HINT_RANGE, "1.0,100.0,1.0"), "set_vrs_min_radius", "get_vrs_min_radius"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "vrs_strength", PROPERTY_HINT_RANGE, "0.1,10.0,0.1"), "set_vrs_strength", "get_vrs_strength"); } void MobileVRInterface::set_eye_height(const double p_eye_height) { @@ -316,6 +326,22 @@ double MobileVRInterface::get_k2() const { return k2; }; +float MobileVRInterface::get_vrs_min_radius() const { + return xr_vrs.get_vrs_min_radius(); +} + +void MobileVRInterface::set_vrs_min_radius(float p_vrs_min_radius) { + xr_vrs.set_vrs_min_radius(p_vrs_min_radius); +} + +float MobileVRInterface::get_vrs_strength() const { + return xr_vrs.get_vrs_strength(); +} + +void MobileVRInterface::set_vrs_strength(float p_vrs_strength) { + xr_vrs.set_vrs_strength(p_vrs_strength); +} + uint32_t MobileVRInterface::get_view_count() { // needs stereo... return 2; @@ -489,11 +515,16 @@ Vector MobileVRInterface::post_draw_viewport(RID p_render_target, Vector blit_to_screen; - // We must have a valid render target + // We must have a valid render target. ERR_FAIL_COND_V(!p_render_target.is_valid(), blit_to_screen); - // Because we are rendering to our device we must use our main viewport! - ERR_FAIL_COND_V(p_screen_rect == Rect2(), blit_to_screen); + // We will only output to screen if this is our main viewport. + if (p_screen_rect == Rect2()) { + // Warn the developer once, it's up to the developer to output to screen. + WARN_PRINT_ONCE("SubViewport used with MobileVRInterface, no output to screen"); + + return blit_to_screen; + } Rect2 modified_screen_rect = Rect2(p_screen_rect.position + offset_rect.position * p_screen_rect.size, p_screen_rect.size * offset_rect.size); @@ -542,6 +573,23 @@ void MobileVRInterface::process() { }; }; +RID MobileVRInterface::get_vrs_texture() { + PackedVector2Array eye_foci; + + Size2 target_size = get_render_target_size(); + real_t aspect_ratio = target_size.x / target_size.y; + uint32_t view_count = get_view_count(); + + for (uint32_t v = 0; v < view_count; v++) { + Projection cm = get_projection_for_view(v, aspect_ratio, 0.1, 1000.0); + Vector3 center = cm.xform(Vector3(0.0, 0.0, 999.0)); + + eye_foci.push_back(Vector2(center.x, center.y)); + } + + return xr_vrs.make_vrs_texture(target_size, eye_foci); +} + MobileVRInterface::MobileVRInterface() {} MobileVRInterface::~MobileVRInterface() { diff --git a/modules/mobile_vr/mobile_vr_interface.h b/modules/mobile_vr/mobile_vr_interface.h index e1d43fff7415..490b1c393ce2 100644 --- a/modules/mobile_vr/mobile_vr_interface.h +++ b/modules/mobile_vr/mobile_vr_interface.h @@ -33,6 +33,7 @@ #include "servers/xr/xr_interface.h" #include "servers/xr/xr_positional_tracker.h" +#include "servers/xr/xr_vrs.h" /** The mobile interface is a native VR interface that can be used on Android and iOS phones. @@ -72,6 +73,8 @@ private: Ref head; Transform3D head_transform; + XRVRS xr_vrs; + /* logic for processing our sensor data, this was originally in our positional tracker logic but I think that doesn't make sense in hindsight. It only makes marginally more sense to park it here for now, @@ -138,6 +141,12 @@ public: void set_k2(const double p_k2); double get_k2() const; + float get_vrs_min_radius() const; + void set_vrs_min_radius(float p_vrs_min_radius); + + float get_vrs_strength() const; + void set_vrs_strength(float p_vrs_strength); + virtual StringName get_name() const override; virtual uint32_t get_capabilities() const override; @@ -161,6 +170,8 @@ public: virtual void process() override; + virtual RID get_vrs_texture() override; + MobileVRInterface(); ~MobileVRInterface(); }; diff --git a/modules/openxr/doc_classes/OpenXRInterface.xml b/modules/openxr/doc_classes/OpenXRInterface.xml index 86ba1416c824..309cbe0d722c 100644 --- a/modules/openxr/doc_classes/OpenXRInterface.xml +++ b/modules/openxr/doc_classes/OpenXRInterface.xml @@ -152,6 +152,14 @@ The render size multiplier for the current HMD. Must be set before the interface has been initialized. + + The minimum radius around the focal point where full quality is guaranteed if VRS is used as a percentage of screen size. + [b]Note:[/b] Mobile and Forward+ renderers only. Requires [member Viewport.vrs_mode] to be set to [constant Viewport.VRS_XR]. + + + The strength used to calculate the VRS density map. The greater this value, the more noticeable VRS is. This improves performance at the cost of quality. + [b]Note:[/b] Mobile and Forward+ renderers only. Requires [member Viewport.vrs_mode] to be set to [constant Viewport.VRS_XR]. + diff --git a/modules/openxr/extensions/openxr_eye_gaze_interaction.cpp b/modules/openxr/extensions/openxr_eye_gaze_interaction.cpp index 477a1c260957..eea996edd939 100644 --- a/modules/openxr/extensions/openxr_eye_gaze_interaction.cpp +++ b/modules/openxr/extensions/openxr_eye_gaze_interaction.cpp @@ -34,6 +34,7 @@ #include "core/os/os.h" #include "../action_map/openxr_interaction_profile_metadata.h" +#include "../openxr_api.h" OpenXREyeGazeInteractionExtension *OpenXREyeGazeInteractionExtension::singleton = nullptr; @@ -106,3 +107,38 @@ void OpenXREyeGazeInteractionExtension::on_register_metadata() { metadata->register_interaction_profile("Eye gaze", "/interaction_profiles/ext/eye_gaze_interaction", XR_EXT_EYE_GAZE_INTERACTION_EXTENSION_NAME); metadata->register_io_path("/interaction_profiles/ext/eye_gaze_interaction", "Gaze pose", "/user/eyes_ext", "/user/eyes_ext/input/gaze_ext/pose", "", OpenXRAction::OPENXR_ACTION_POSE); } + +bool OpenXREyeGazeInteractionExtension::get_eye_gaze_pose(double p_dist, Vector3 &r_eye_pose) { + OpenXRAPI *openxr_api = OpenXRAPI::get_singleton(); + ERR_FAIL_NULL_V(openxr_api, false); + + if (!init_eye_gaze_pose) { + init_eye_gaze_pose = true; + + eye_tracker = openxr_api->find_tracker("/user/eyes_ext"); + if (eye_tracker.is_null()) { + WARN_PRINT("Couldn't obtain eye tracker"); + } + + eye_action = openxr_api->find_action("eye_gaze_pose"); + if (eye_action.is_null()) { + WARN_PRINT("Couldn't obtain pose action for `eye_gaze_pose`, make sure to add this to your action map."); + } + } + + if (eye_tracker.is_null() || eye_action.is_null()) { + return false; + } + + Transform3D eye_transform; + Vector3 linear_velocity; + Vector3 angular_velocity; + XRPose::TrackingConfidence confidence = openxr_api->get_action_pose(eye_action, eye_tracker, eye_transform, linear_velocity, angular_velocity); + if (confidence == XRPose::XR_TRACKING_CONFIDENCE_NONE) { + return false; + } + + r_eye_pose = eye_transform.origin + eye_transform.basis[2] * p_dist; + + return true; +} diff --git a/modules/openxr/extensions/openxr_eye_gaze_interaction.h b/modules/openxr/extensions/openxr_eye_gaze_interaction.h index 2b99f8edff0b..114c1aacc7ec 100644 --- a/modules/openxr/extensions/openxr_eye_gaze_interaction.h +++ b/modules/openxr/extensions/openxr_eye_gaze_interaction.h @@ -50,11 +50,17 @@ public: virtual void on_register_metadata() override; + bool get_eye_gaze_pose(double p_dist, Vector3 &r_eye_pose); + private: static OpenXREyeGazeInteractionExtension *singleton; bool available = false; XrSystemEyeGazeInteractionPropertiesEXT properties; + + bool init_eye_gaze_pose = false; + RID eye_tracker; + RID eye_action; }; #endif // OPENXR_EYE_GAZE_INTERACTION_H diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp index 40e3ecfefc92..8731a36865cc 100644 --- a/modules/openxr/openxr_api.cpp +++ b/modules/openxr/openxr_api.cpp @@ -54,6 +54,7 @@ #endif #include "extensions/openxr_composition_layer_depth_extension.h" +#include "extensions/openxr_eye_gaze_interaction.h" #include "extensions/openxr_fb_display_refresh_rate_extension.h" #include "extensions/openxr_fb_foveation_extension.h" #include "extensions/openxr_fb_update_swapchain_extension.h" @@ -1826,6 +1827,46 @@ bool OpenXRAPI::get_view_projection(uint32_t p_view, double p_z_near, double p_z return graphics_extension->create_projection_fov(render_state.views[p_view].fov, p_z_near, p_z_far, p_camera_matrix); } +Vector2 OpenXRAPI::get_eye_focus(uint32_t p_view, float p_aspect) { + ERR_FAIL_NULL_V(graphics_extension, Vector2()); + + if (!render_state.running) { + return Vector2(); + } + + // xrWaitFrame not run yet + if (render_state.predicted_display_time == 0) { + return Vector2(); + } + + // we don't have valid view info + if (render_state.views == nullptr || !render_state.view_pose_valid) { + return Vector2(); + } + + Projection cm; + if (!graphics_extension->create_projection_fov(render_state.views[p_view].fov, 0.1, 1000.0, cm)) { + return Vector2(); + } + + // Default focus to center... + Vector3 focus = cm.xform(Vector3(0.0, 0.0, 999.9)); + + // Lets check for eye tracking... + OpenXREyeGazeInteractionExtension *eye_gaze_interaction = OpenXREyeGazeInteractionExtension::get_singleton(); + if (eye_gaze_interaction && eye_gaze_interaction->supports_eye_gaze_interaction()) { + Vector3 eye_gaze_pose; + if (eye_gaze_interaction->get_eye_gaze_pose(1.0, eye_gaze_pose)) { + Transform3D view_transform = transform_from_pose(render_state.views[p_view].pose); + + eye_gaze_pose = view_transform.xform_inv(eye_gaze_pose); + focus = cm.xform(eye_gaze_pose); + } + } + + return Vector2(focus.x, focus.y); +} + bool OpenXRAPI::poll_events() { ERR_FAIL_COND_V(instance == XR_NULL_HANDLE, false); @@ -2633,10 +2674,23 @@ bool OpenXRAPI::xr_result(XrResult result, const char *format, Array args) const RID OpenXRAPI::get_tracker_rid(XrPath p_path) { List current; tracker_owner.get_owned_list(¤t); - for (int i = 0; i < current.size(); i++) { - Tracker *tracker = tracker_owner.get_or_null(current[i]); + for (const RID &E : current) { + Tracker *tracker = tracker_owner.get_or_null(E); if (tracker && tracker->toplevel_path == p_path) { - return current[i]; + return E; + } + } + + return RID(); +} + +RID OpenXRAPI::find_tracker(const String &p_name) { + List current; + tracker_owner.get_owned_list(¤t); + for (const RID &E : current) { + Tracker *tracker = tracker_owner.get_or_null(E); + if (tracker && tracker->name == p_name) { + return E; } } @@ -2827,10 +2881,23 @@ void OpenXRAPI::action_set_free(RID p_action_set) { RID OpenXRAPI::get_action_rid(XrAction p_action) { List current; action_owner.get_owned_list(¤t); - for (int i = 0; i < current.size(); i++) { - Action *action = action_owner.get_or_null(current[i]); + for (const RID &E : current) { + Action *action = action_owner.get_or_null(E); if (action && action->handle == p_action) { - return current[i]; + return E; + } + } + + return RID(); +} + +RID OpenXRAPI::find_action(const String &p_name) { + List current; + action_owner.get_owned_list(¤t); + for (const RID &E : current) { + Action *action = action_owner.get_or_null(E); + if (action && action->name == p_name) { + return E; } } @@ -2931,10 +2998,10 @@ void OpenXRAPI::action_free(RID p_action) { RID OpenXRAPI::get_interaction_profile_rid(XrPath p_path) { List current; interaction_profile_owner.get_owned_list(¤t); - for (int i = 0; i < current.size(); i++) { - InteractionProfile *ip = interaction_profile_owner.get_or_null(current[i]); + for (const RID &E : current) { + InteractionProfile *ip = interaction_profile_owner.get_or_null(E); if (ip && ip->path == p_path) { - return current[i]; + return E; } } diff --git a/modules/openxr/openxr_api.h b/modules/openxr/openxr_api.h index c95867810c6b..f9d2e6014836 100644 --- a/modules/openxr/openxr_api.h +++ b/modules/openxr/openxr_api.h @@ -456,6 +456,7 @@ public: XRPose::TrackingConfidence get_head_center(Transform3D &r_transform, Vector3 &r_linear_velocity, Vector3 &r_angular_velocity); bool get_view_transform(uint32_t p_view, Transform3D &r_transform); bool get_view_projection(uint32_t p_view, double p_z_near, double p_z_far, Projection &p_camera_matrix); + Vector2 get_eye_focus(uint32_t p_view, float p_aspect); bool process(); void pre_render(); @@ -515,6 +516,9 @@ public: bool interaction_profile_suggest_bindings(RID p_interaction_profile); void interaction_profile_free(RID p_interaction_profile); + RID find_tracker(const String &p_name); + RID find_action(const String &p_name); + bool sync_action_sets(const Vector p_active_sets); bool get_action_bool(RID p_action, RID p_tracker); float get_action_float(RID p_action, RID p_tracker); diff --git a/modules/openxr/openxr_interface.cpp b/modules/openxr/openxr_interface.cpp index 39a61d1b4d26..b92d1edb90f8 100644 --- a/modules/openxr/openxr_interface.cpp +++ b/modules/openxr/openxr_interface.cpp @@ -97,6 +97,16 @@ void OpenXRInterface::_bind_methods() { ClassDB::bind_method(D_METHOD("is_hand_interaction_supported"), &OpenXRInterface::is_hand_interaction_supported); ClassDB::bind_method(D_METHOD("is_eye_gaze_interaction_supported"), &OpenXRInterface::is_eye_gaze_interaction_supported); + // VRS + ClassDB::bind_method(D_METHOD("get_vrs_min_radius"), &OpenXRInterface::get_vrs_min_radius); + ClassDB::bind_method(D_METHOD("set_vrs_min_radius", "radius"), &OpenXRInterface::set_vrs_min_radius); + ClassDB::bind_method(D_METHOD("get_vrs_strength"), &OpenXRInterface::get_vrs_strength); + ClassDB::bind_method(D_METHOD("set_vrs_strength", "strength"), &OpenXRInterface::set_vrs_strength); + + ADD_GROUP("Vulkan VRS", "vrs_"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "vrs_min_radius", PROPERTY_HINT_RANGE, "1.0,100.0,1.0"), "set_vrs_min_radius", "get_vrs_min_radius"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "vrs_strength", PROPERTY_HINT_RANGE, "0.1,10.0,0.1"), "set_vrs_strength", "get_vrs_strength"); + BIND_ENUM_CONSTANT(HAND_LEFT); BIND_ENUM_CONSTANT(HAND_RIGHT); BIND_ENUM_CONSTANT(HAND_MAX); @@ -872,6 +882,22 @@ Array OpenXRInterface::get_action_sets() const { return arr; } +float OpenXRInterface::get_vrs_min_radius() const { + return xr_vrs.get_vrs_min_radius(); +} + +void OpenXRInterface::set_vrs_min_radius(float p_vrs_min_radius) { + xr_vrs.set_vrs_min_radius(p_vrs_min_radius); +} + +float OpenXRInterface::get_vrs_strength() const { + return xr_vrs.get_vrs_strength(); +} + +void OpenXRInterface::set_vrs_strength(float p_vrs_strength) { + xr_vrs.set_vrs_strength(p_vrs_strength); +} + double OpenXRInterface::get_render_target_size_multiplier() const { if (openxr_api == nullptr) { return 1.0; @@ -1435,6 +1461,24 @@ Vector3 OpenXRInterface::get_hand_joint_angular_velocity(Hand p_hand, HandJoints return Vector3(); } +RID OpenXRInterface::get_vrs_texture() { + if (!openxr_api) { + return RID(); + } + + PackedVector2Array eye_foci; + + Size2 target_size = get_render_target_size(); + real_t aspect_ratio = target_size.x / target_size.y; + uint32_t view_count = get_view_count(); + + for (uint32_t v = 0; v < view_count; v++) { + eye_foci.push_back(openxr_api->get_eye_focus(v, aspect_ratio)); + } + + return xr_vrs.make_vrs_texture(target_size, eye_foci); +} + OpenXRInterface::OpenXRInterface() { openxr_api = OpenXRAPI::get_singleton(); if (openxr_api) { diff --git a/modules/openxr/openxr_interface.h b/modules/openxr/openxr_interface.h index ac333047576d..f0ee0dc3c4ed 100644 --- a/modules/openxr/openxr_interface.h +++ b/modules/openxr/openxr_interface.h @@ -80,6 +80,8 @@ private: XRPose::TrackingConfidence head_confidence; Transform3D transform_for_view[2]; // We currently assume 2, but could be 4 for VARJO which we do not support yet + XRVRS xr_vrs; + void _load_action_map(); struct Action { // An action we've registered with OpenXR @@ -168,6 +170,12 @@ public: bool get_foveation_dynamic() const; void set_foveation_dynamic(bool p_foveation_dynamic); + float get_vrs_min_radius() const; + void set_vrs_min_radius(float p_vrs_min_radius); + + float get_vrs_strength() const; + void set_vrs_strength(float p_vrs_strength); + virtual Size2 get_render_target_size() override; virtual uint32_t get_view_count() override; virtual Transform3D get_camera_transform() override; @@ -276,6 +284,8 @@ public: Vector3 get_hand_joint_linear_velocity(Hand p_hand, HandJoints p_joint) const; Vector3 get_hand_joint_angular_velocity(Hand p_hand, HandJoints p_joint) const; + virtual RID get_vrs_texture() override; + OpenXRInterface(); ~OpenXRInterface(); }; diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 9dbe10f30bcf..06cdc022133d 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -3735,6 +3735,28 @@ Viewport::VRSMode Viewport::get_vrs_mode() const { return vrs_mode; } +void Viewport::set_vrs_update_mode(VRSUpdateMode p_vrs_update_mode) { + ERR_MAIN_THREAD_GUARD; + + vrs_update_mode = p_vrs_update_mode; + switch (p_vrs_update_mode) { + case VRS_UPDATE_ONCE: { + RS::get_singleton()->viewport_set_vrs_update_mode(viewport, RS::VIEWPORT_VRS_UPDATE_ONCE); + } break; + case VRS_UPDATE_ALWAYS: { + RS::get_singleton()->viewport_set_vrs_update_mode(viewport, RS::VIEWPORT_VRS_UPDATE_ALWAYS); + } break; + default: { + RS::get_singleton()->viewport_set_vrs_update_mode(viewport, RS::VIEWPORT_VRS_UPDATE_DISABLED); + } break; + } +} + +Viewport::VRSUpdateMode Viewport::get_vrs_update_mode() const { + ERR_READ_THREAD_GUARD_V(VRS_UPDATE_DISABLED); + return vrs_update_mode; +} + void Viewport::set_vrs_texture(Ref p_texture) { ERR_MAIN_THREAD_GUARD; vrs_texture = p_texture; @@ -4775,6 +4797,9 @@ void Viewport::_bind_methods() { ClassDB::bind_method(D_METHOD("set_vrs_mode", "mode"), &Viewport::set_vrs_mode); ClassDB::bind_method(D_METHOD("get_vrs_mode"), &Viewport::get_vrs_mode); + ClassDB::bind_method(D_METHOD("set_vrs_update_mode", "mode"), &Viewport::set_vrs_update_mode); + ClassDB::bind_method(D_METHOD("get_vrs_update_mode"), &Viewport::get_vrs_update_mode); + ClassDB::bind_method(D_METHOD("set_vrs_texture", "texture"), &Viewport::set_vrs_texture); ClassDB::bind_method(D_METHOD("get_vrs_texture"), &Viewport::get_vrs_texture); @@ -4807,6 +4832,7 @@ void Viewport::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fsr_sharpness", PROPERTY_HINT_RANGE, "0,2,0.1"), "set_fsr_sharpness", "get_fsr_sharpness"); ADD_GROUP("Variable Rate Shading", "vrs_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "vrs_mode", PROPERTY_HINT_ENUM, "Disabled,Texture,Depth buffer,XR"), "set_vrs_mode", "get_vrs_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "vrs_update_mode", PROPERTY_HINT_ENUM, "Disabled,Once,Always"), "set_vrs_update_mode", "get_vrs_update_mode"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "vrs_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_vrs_texture", "get_vrs_texture"); #endif ADD_GROUP("Canvas Items", "canvas_item_"); @@ -4930,12 +4956,21 @@ void Viewport::_bind_methods() { BIND_ENUM_CONSTANT(VRS_TEXTURE); BIND_ENUM_CONSTANT(VRS_XR); BIND_ENUM_CONSTANT(VRS_MAX); + + BIND_ENUM_CONSTANT(VRS_UPDATE_DISABLED); + BIND_ENUM_CONSTANT(VRS_UPDATE_ONCE); + BIND_ENUM_CONSTANT(VRS_UPDATE_ALWAYS); + BIND_ENUM_CONSTANT(VRS_UPDATE_MAX); } void Viewport::_validate_property(PropertyInfo &p_property) const { if (vrs_mode != VRS_TEXTURE && (p_property.name == "vrs_texture")) { p_property.usage = PROPERTY_USAGE_NO_EDITOR; } + + if (vrs_mode == VRS_DISABLED && (p_property.name == "vrs_update_mode")) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; + } } Viewport::Viewport() { diff --git a/scene/main/viewport.h b/scene/main/viewport.h index 394d48143c90..2474b890b068 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -213,6 +213,13 @@ public: VRS_MAX }; + enum VRSUpdateMode { + VRS_UPDATE_DISABLED, + VRS_UPDATE_ONCE, + VRS_UPDATE_ALWAYS, + VRS_UPDATE_MAX + }; + private: friend class ViewportTexture; @@ -337,6 +344,7 @@ private: // VRS VRSMode vrs_mode = VRS_DISABLED; + VRSUpdateMode vrs_update_mode = VRS_UPDATE_ONCE; Ref vrs_texture; struct GUI { @@ -636,6 +644,9 @@ public: void set_vrs_mode(VRSMode p_vrs_mode); VRSMode get_vrs_mode() const; + void set_vrs_update_mode(VRSUpdateMode p_vrs_update_mode); + VRSUpdateMode get_vrs_update_mode() const; + void set_vrs_texture(Ref p_texture); Ref get_vrs_texture() const; @@ -844,6 +855,7 @@ VARIANT_ENUM_CAST(Viewport::DebugDraw); VARIANT_ENUM_CAST(Viewport::SDFScale); VARIANT_ENUM_CAST(Viewport::SDFOversize); VARIANT_ENUM_CAST(Viewport::VRSMode); +VARIANT_ENUM_CAST(Viewport::VRSUpdateMode); VARIANT_ENUM_CAST(SubViewport::ClearMode); VARIANT_ENUM_CAST(Viewport::RenderInfo); VARIANT_ENUM_CAST(Viewport::RenderInfoType); diff --git a/servers/register_server_types.cpp b/servers/register_server_types.cpp index 1a75614a4c0a..7a15d202a330 100644 --- a/servers/register_server_types.cpp +++ b/servers/register_server_types.cpp @@ -325,6 +325,7 @@ void register_server_types() { PhysicsServer3DManager::get_singleton()->set_default_server("GodotPhysics3D"); GDREGISTER_ABSTRACT_CLASS(XRInterface); + GDREGISTER_CLASS(XRVRS); GDREGISTER_CLASS(XRBodyTracker); GDREGISTER_CLASS(XRControllerTracker); GDREGISTER_CLASS(XRFaceTracker); diff --git a/servers/rendering/dummy/storage/texture_storage.h b/servers/rendering/dummy/storage/texture_storage.h index 1331ca72c251..43572b65e097 100644 --- a/servers/rendering/dummy/storage/texture_storage.h +++ b/servers/rendering/dummy/storage/texture_storage.h @@ -193,6 +193,8 @@ public: virtual void render_target_set_vrs_mode(RID p_render_target, RS::ViewportVRSMode p_mode) override {} virtual RS::ViewportVRSMode render_target_get_vrs_mode(RID p_render_target) const override { return RS::VIEWPORT_VRS_DISABLED; } + virtual void render_target_set_vrs_update_mode(RID p_render_target, RS::ViewportVRSUpdateMode p_mode) override {} + virtual RS::ViewportVRSUpdateMode render_target_get_vrs_update_mode(RID p_render_target) const override { return RS::VIEWPORT_VRS_UPDATE_DISABLED; } virtual void render_target_set_vrs_texture(RID p_render_target, RID p_texture) override {} virtual RID render_target_get_vrs_texture(RID p_render_target) const override { return RID(); } diff --git a/servers/rendering/renderer_rd/effects/vrs.cpp b/servers/rendering/renderer_rd/effects/vrs.cpp index 30c318fb9ab9..94453bf95fb5 100644 --- a/servers/rendering/renderer_rd/effects/vrs.cpp +++ b/servers/rendering/renderer_rd/effects/vrs.cpp @@ -80,7 +80,16 @@ void VRS::copy_vrs(RID p_source_rd_texture, RID p_dest_framebuffer, bool p_multi RD::Uniform u_source_rd_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector({ default_sampler, p_source_rd_texture })); - VRSMode mode = p_multiview ? VRS_MULTIVIEW : VRS_DEFAULT; + VRSPushConstant push_constant = {}; + + int mode = p_multiview ? VRS_MULTIVIEW : VRS_DEFAULT; + + // Set maximum texel factor based on maximum fragment size, some GPUs do not support 8x8 (fragment shading rate approach). + if (MIN(RD::get_singleton()->limit_get(RD::LIMIT_VRS_MAX_FRAGMENT_WIDTH), RD::get_singleton()->limit_get(RD::LIMIT_VRS_MAX_FRAGMENT_HEIGHT)) > 4) { + push_constant.max_texel_factor = 3.0; + } else { + push_constant.max_texel_factor = 2.0; + } RID shader = vrs_shader.shader.version_get_shader(vrs_shader.shader_version, mode); ERR_FAIL_COND(shader.is_null()); @@ -88,7 +97,7 @@ void VRS::copy_vrs(RID p_source_rd_texture, RID p_dest_framebuffer, bool p_multi RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, Vector()); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, vrs_shader.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0); - // RD::get_singleton()->draw_list_set_push_constant(draw_list, &vrs_shader.push_constant, sizeof(VRSPushConstant)); + RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(VRSPushConstant)); RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u); RD::get_singleton()->draw_list_end(); } @@ -111,12 +120,11 @@ Size2i VRS::get_vrs_texture_size(const Size2i p_base_size) const { void VRS::update_vrs_texture(RID p_vrs_fb, RID p_render_target) { TextureStorage *texture_storage = TextureStorage::get_singleton(); RS::ViewportVRSMode vrs_mode = texture_storage->render_target_get_vrs_mode(p_render_target); + RS::ViewportVRSUpdateMode vrs_update_mode = texture_storage->render_target_get_vrs_update_mode(p_render_target); - if (vrs_mode != RS::VIEWPORT_VRS_DISABLED) { + if (vrs_mode != RS::VIEWPORT_VRS_DISABLED && vrs_update_mode != RS::VIEWPORT_VRS_UPDATE_DISABLED) { RD::get_singleton()->draw_command_begin_label("VRS Setup"); - // TODO figure out if image has changed since it was last copied so we can save some resources.. - if (vrs_mode == RS::VIEWPORT_VRS_TEXTURE) { RID vrs_texture = texture_storage->render_target_get_vrs_texture(p_render_target); if (vrs_texture.is_valid()) { @@ -145,6 +153,10 @@ void VRS::update_vrs_texture(RID p_vrs_fb, RID p_render_target) { #endif // _3D_DISABLED } + if (vrs_update_mode == RS::VIEWPORT_VRS_UPDATE_ONCE) { + texture_storage->render_target_set_vrs_update_mode(p_render_target, RS::VIEWPORT_VRS_UPDATE_DISABLED); + } + RD::get_singleton()->draw_command_end_label(); } } diff --git a/servers/rendering/renderer_rd/effects/vrs.h b/servers/rendering/renderer_rd/effects/vrs.h index b18cf5579143..94878e46615f 100644 --- a/servers/rendering/renderer_rd/effects/vrs.h +++ b/servers/rendering/renderer_rd/effects/vrs.h @@ -47,11 +47,12 @@ private: VRS_MAX, }; - /* we have no push constant here (yet) struct VRSPushConstant { - + float max_texel_factor; // 4x8, 8x4 and 8x8 are only available on some GPUs. + float res1; + float res2; + float res3; }; - */ struct VRSShader { // VRSPushConstant push_constant; diff --git a/servers/rendering/renderer_rd/shaders/effects/vrs.glsl b/servers/rendering/renderer_rd/shaders/effects/vrs.glsl index 23b0373eefe8..7ed3fa3348d7 100644 --- a/servers/rendering/renderer_rd/shaders/effects/vrs.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/vrs.glsl @@ -19,6 +19,14 @@ layout(location = 0) out vec3 uv_interp; layout(location = 0) out vec2 uv_interp; #endif +layout(push_constant, std430) uniform Params { + float max_texel_factor; + float res1; + float res2; + float res3; +} +params; + void main() { vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0)); gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0); @@ -53,6 +61,14 @@ layout(set = 0, binding = 0) uniform sampler2D source_color; layout(location = 0) out uint frag_color; +layout(push_constant, std430) uniform Params { + float max_texel_factor; + float res1; + float res2; + float res3; +} +params; + void main() { #ifdef USE_MULTIVIEW vec3 uv = uv_interp; @@ -60,20 +76,22 @@ void main() { vec2 uv = uv_interp; #endif -#ifdef USE_MULTIVIEW - vec4 color = textureLod(source_color, uv, 0.0); - frag_color = uint(color.r * 255.0); -#else /* USE_MULTIVIEW */ + // Input is standardised. R for X, G for Y, 0.0 (0) = 1, 0.33 (85) = 2, 0.66 (170) = 3, 1.0 (255) = 8 vec4 color = textureLod(source_color, uv, 0.0); - // for user supplied VRS map we do a color mapping - color.r *= 3.0; - frag_color = int(color.r) << 2; + // Output image shading rate image for VRS according to VK_KHR_fragment_shading_rate. + color.r = clamp(floor(color.r * params.max_texel_factor + 0.1), 0.0, params.max_texel_factor); + color.g = clamp(floor(color.g * params.max_texel_factor + 0.1), 0.0, params.max_texel_factor); - color.g *= 3.0; - frag_color += int(color.g); + // Note 1x4, 4x1, 1x8, 8x1, 2x8 and 8x2 are not supported: + if (color.r < (color.g - 1.0)) { + color.r = color.g - 1.0; + } + if (color.g < (color.r - 1.0)) { + color.g = color.r - 1.0; + } - // note 1x4, 4x1, 1x8, 8x1, 2x8 and 8x2 are not supported - // 4x8, 8x4 and 8x8 are only available on some GPUs -#endif /* USE_MULTIVIEW */ + // Encode to frag_color; + frag_color = int(color.r + 0.1) << 2; + frag_color += int(color.g + 0.1); } diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp index 76ff566b18bd..f844919df1c4 100644 --- a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp @@ -3960,6 +3960,20 @@ RS::ViewportVRSMode TextureStorage::render_target_get_vrs_mode(RID p_render_targ return rt->vrs_mode; } +void TextureStorage::render_target_set_vrs_update_mode(RID p_render_target, RS::ViewportVRSUpdateMode p_mode) { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_NULL(rt); + + rt->vrs_update_mode = p_mode; +} + +RS::ViewportVRSUpdateMode TextureStorage::render_target_get_vrs_update_mode(RID p_render_target) const { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_NULL_V(rt, RS::VIEWPORT_VRS_UPDATE_DISABLED); + + return rt->vrs_update_mode; +} + void TextureStorage::render_target_set_vrs_texture(RID p_render_target, RID p_texture) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_NULL(rt); diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.h b/servers/rendering/renderer_rd/storage_rd/texture_storage.h index 1304b284d507..704f5fb1bdad 100644 --- a/servers/rendering/renderer_rd/storage_rd/texture_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.h @@ -366,6 +366,7 @@ private: // VRS RS::ViewportVRSMode vrs_mode = RS::VIEWPORT_VRS_DISABLED; + RS::ViewportVRSUpdateMode vrs_update_mode = RS::VIEWPORT_VRS_UPDATE_ONCE; RID vrs_texture; // overridden textures @@ -746,6 +747,8 @@ public: virtual void render_target_set_vrs_mode(RID p_render_target, RS::ViewportVRSMode p_mode) override; virtual RS::ViewportVRSMode render_target_get_vrs_mode(RID p_render_target) const override; + virtual void render_target_set_vrs_update_mode(RID p_render_target, RS::ViewportVRSUpdateMode p_mode) override; + virtual RS::ViewportVRSUpdateMode render_target_get_vrs_update_mode(RID p_render_target) const override; virtual void render_target_set_vrs_texture(RID p_render_target, RID p_texture) override; virtual RID render_target_get_vrs_texture(RID p_render_target) const override; diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp index 31d5a9074cfa..075df10e0562 100644 --- a/servers/rendering/renderer_viewport.cpp +++ b/servers/rendering/renderer_viewport.cpp @@ -1450,6 +1450,13 @@ void RendererViewport::viewport_set_vrs_mode(RID p_viewport, RS::ViewportVRSMode _configure_3d_render_buffers(viewport); } +void RendererViewport::viewport_set_vrs_update_mode(RID p_viewport, RS::ViewportVRSUpdateMode p_mode) { + Viewport *viewport = viewport_owner.get_or_null(p_viewport); + ERR_FAIL_NULL(viewport); + + RSG::texture_storage->render_target_set_vrs_update_mode(viewport->render_target, p_mode); +} + void RendererViewport::viewport_set_vrs_texture(RID p_viewport, RID p_texture) { Viewport *viewport = viewport_owner.get_or_null(p_viewport); ERR_FAIL_NULL(viewport); diff --git a/servers/rendering/renderer_viewport.h b/servers/rendering/renderer_viewport.h index 5107398c54b3..8bdce04c506a 100644 --- a/servers/rendering/renderer_viewport.h +++ b/servers/rendering/renderer_viewport.h @@ -298,6 +298,7 @@ public: virtual RID viewport_find_from_screen_attachment(DisplayServer::WindowID p_id = DisplayServer::MAIN_WINDOW_ID) const; void viewport_set_vrs_mode(RID p_viewport, RS::ViewportVRSMode p_mode); + void viewport_set_vrs_update_mode(RID p_viewport, RS::ViewportVRSUpdateMode p_mode); void viewport_set_vrs_texture(RID p_viewport, RID p_texture); void handle_timestamp(String p_timestamp, uint64_t p_cpu_time, uint64_t p_gpu_time); diff --git a/servers/rendering/rendering_device_commons.h b/servers/rendering/rendering_device_commons.h index 28d641c879a6..918bf9b834ad 100644 --- a/servers/rendering/rendering_device_commons.h +++ b/servers/rendering/rendering_device_commons.h @@ -810,6 +810,8 @@ public: LIMIT_SUBGROUP_OPERATIONS, LIMIT_VRS_TEXEL_WIDTH, LIMIT_VRS_TEXEL_HEIGHT, + LIMIT_VRS_MAX_FRAGMENT_WIDTH, + LIMIT_VRS_MAX_FRAGMENT_HEIGHT, }; enum Features { diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h index e0049e3fa4f3..bb2f3d94ce9c 100644 --- a/servers/rendering/rendering_server_default.h +++ b/servers/rendering/rendering_server_default.h @@ -672,6 +672,7 @@ public: FUNC2(call_set_vsync_mode, DisplayServer::VSyncMode, DisplayServer::WindowID) FUNC2(viewport_set_vrs_mode, RID, ViewportVRSMode) + FUNC2(viewport_set_vrs_update_mode, RID, ViewportVRSUpdateMode) FUNC2(viewport_set_vrs_texture, RID, RID) /* COMPOSITOR EFFECT */ diff --git a/servers/rendering/storage/texture_storage.h b/servers/rendering/storage/texture_storage.h index cf37fbfb4a05..72c4dd305bde 100644 --- a/servers/rendering/storage/texture_storage.h +++ b/servers/rendering/storage/texture_storage.h @@ -169,6 +169,8 @@ public: virtual void render_target_set_vrs_mode(RID p_render_target, RS::ViewportVRSMode p_mode) = 0; virtual RS::ViewportVRSMode render_target_get_vrs_mode(RID p_render_target) const = 0; + virtual void render_target_set_vrs_update_mode(RID p_render_target, RS::ViewportVRSUpdateMode p_mode) = 0; + virtual RS::ViewportVRSUpdateMode render_target_get_vrs_update_mode(RID p_render_target) const = 0; virtual void render_target_set_vrs_texture(RID p_render_target, RID p_texture) = 0; virtual RID render_target_get_vrs_texture(RID p_render_target) const = 0; diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 60e8f18c199b..ab82e42f433c 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -2824,6 +2824,7 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("viewport_get_measured_render_time_gpu", "viewport"), &RenderingServer::viewport_get_measured_render_time_gpu); ClassDB::bind_method(D_METHOD("viewport_set_vrs_mode", "viewport", "mode"), &RenderingServer::viewport_set_vrs_mode); + ClassDB::bind_method(D_METHOD("viewport_set_vrs_update_mode", "viewport", "mode"), &RenderingServer::viewport_set_vrs_update_mode); ClassDB::bind_method(D_METHOD("viewport_set_vrs_texture", "viewport", "texture"), &RenderingServer::viewport_set_vrs_texture); BIND_ENUM_CONSTANT(VIEWPORT_SCALING_3D_MODE_BILINEAR); @@ -2832,7 +2833,7 @@ void RenderingServer::_bind_methods() { BIND_ENUM_CONSTANT(VIEWPORT_SCALING_3D_MODE_MAX); BIND_ENUM_CONSTANT(VIEWPORT_UPDATE_DISABLED); - BIND_ENUM_CONSTANT(VIEWPORT_UPDATE_ONCE); // Then goes to disabled); must be manually updated. + BIND_ENUM_CONSTANT(VIEWPORT_UPDATE_ONCE); // Then goes to disabled, must be manually updated. BIND_ENUM_CONSTANT(VIEWPORT_UPDATE_WHEN_VISIBLE); // Default BIND_ENUM_CONSTANT(VIEWPORT_UPDATE_WHEN_PARENT_VISIBLE); BIND_ENUM_CONSTANT(VIEWPORT_UPDATE_ALWAYS); @@ -2914,6 +2915,11 @@ void RenderingServer::_bind_methods() { BIND_ENUM_CONSTANT(VIEWPORT_VRS_XR); BIND_ENUM_CONSTANT(VIEWPORT_VRS_MAX); + BIND_ENUM_CONSTANT(VIEWPORT_VRS_UPDATE_DISABLED); + BIND_ENUM_CONSTANT(VIEWPORT_VRS_UPDATE_ONCE); // Then goes to disabled, must be manually updated. + BIND_ENUM_CONSTANT(VIEWPORT_VRS_UPDATE_ALWAYS); + BIND_ENUM_CONSTANT(VIEWPORT_VRS_UPDATE_MAX); + /* SKY API */ ClassDB::bind_method(D_METHOD("sky_create"), &RenderingServer::sky_create); diff --git a/servers/rendering_server.h b/servers/rendering_server.h index 240d82c90b8d..5bc028dfdd80 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -1056,7 +1056,15 @@ public: VIEWPORT_VRS_MAX, }; + enum ViewportVRSUpdateMode { + VIEWPORT_VRS_UPDATE_DISABLED, + VIEWPORT_VRS_UPDATE_ONCE, + VIEWPORT_VRS_UPDATE_ALWAYS, + VIEWPORT_VRS_UPDATE_MAX, + }; + virtual void viewport_set_vrs_mode(RID p_viewport, ViewportVRSMode p_mode) = 0; + virtual void viewport_set_vrs_update_mode(RID p_viewport, ViewportVRSUpdateMode p_mode) = 0; virtual void viewport_set_vrs_texture(RID p_viewport, RID p_texture) = 0; /* SKY API */ @@ -1815,6 +1823,7 @@ VARIANT_ENUM_CAST(RenderingServer::ViewportOcclusionCullingBuildQuality); VARIANT_ENUM_CAST(RenderingServer::ViewportSDFOversize); VARIANT_ENUM_CAST(RenderingServer::ViewportSDFScale); VARIANT_ENUM_CAST(RenderingServer::ViewportVRSMode); +VARIANT_ENUM_CAST(RenderingServer::ViewportVRSUpdateMode); VARIANT_ENUM_CAST(RenderingServer::SkyMode); VARIANT_ENUM_CAST(RenderingServer::CompositorEffectCallbackType); VARIANT_ENUM_CAST(RenderingServer::CompositorEffectFlags); diff --git a/servers/xr/xr_interface.cpp b/servers/xr/xr_interface.cpp index 9ced28fd52da..26f315a45426 100644 --- a/servers/xr/xr_interface.cpp +++ b/servers/xr/xr_interface.cpp @@ -132,13 +132,7 @@ void XRInterface::set_primary(bool p_primary) { XRInterface::XRInterface() {} -XRInterface::~XRInterface() { - if (vrs.vrs_texture.is_valid()) { - ERR_FAIL_NULL(RenderingServer::get_singleton()); - RS::get_singleton()->free(vrs.vrs_texture); - vrs.vrs_texture = RID(); - } -} +XRInterface::~XRInterface() {} // query if this interface supports this play area mode bool XRInterface::supports_play_area_mode(XRInterface::PlayAreaMode p_mode) { @@ -176,80 +170,7 @@ int XRInterface::get_camera_feed_id() { } RID XRInterface::get_vrs_texture() { - // Default logic will return a standard VRS image based on our target size and default projections. - // Note that this only gets called if VRS is supported on the hardware. - - int32_t texel_width = RD::get_singleton()->limit_get(RD::LIMIT_VRS_TEXEL_WIDTH); - int32_t texel_height = RD::get_singleton()->limit_get(RD::LIMIT_VRS_TEXEL_HEIGHT); - int view_count = get_view_count(); - Size2 target_size = get_render_target_size(); - real_t aspect = target_size.x / target_size.y; // is this y/x ? - Size2 vrs_size = Size2(round(0.5 + target_size.x / texel_width), round(0.5 + target_size.y / texel_height)); - real_t radius = vrs_size.length() * 0.5; - Size2 vrs_sizei = vrs_size; - - if (vrs.size != vrs_sizei) { - const uint8_t densities[] = { - 0, // 1x1 - 1, // 1x2 - // 2, // 1x4 - not supported - // 3, // 1x8 - not supported - // 4, // 2x1 - 5, // 2x2 - 6, // 2x4 - // 9, // 4x2 - 10, // 4x4 - }; - - // out with the old - if (vrs.vrs_texture.is_valid()) { - RS::get_singleton()->free(vrs.vrs_texture); - vrs.vrs_texture = RID(); - } - - // in with the new - Vector> images; - vrs.size = vrs_sizei; - - for (int i = 0; i < view_count && i < 2; i++) { - PackedByteArray data; - data.resize(vrs_sizei.x * vrs_sizei.y); - uint8_t *data_ptr = data.ptrw(); - - // Our near and far don't matter much for what we're doing here, but there are some interfaces that will remember this as the near and far and may fail as a result... - Projection cm = get_projection_for_view(i, aspect, 0.1, 1000.0); - Vector3 center = cm.xform(Vector3(0.0, 0.0, 999.0)); - - Vector2i view_center; - view_center.x = int(vrs_size.x * (center.x + 1.0) * 0.5); - view_center.y = int(vrs_size.y * (center.y + 1.0) * 0.5); - - int d = 0; - for (int y = 0; y < vrs_sizei.y; y++) { - for (int x = 0; x < vrs_sizei.x; x++) { - Vector2 offset = Vector2(x - view_center.x, y - view_center.y); - offset.y *= aspect; - real_t distance = offset.length(); - int idx = round(5.0 * distance / radius); - if (idx > 4) { - idx = 4; - } - uint8_t density = densities[idx]; - - data_ptr[d++] = density; - } - } - images.push_back(Image::create_from_data(vrs_sizei.x, vrs_sizei.y, false, Image::FORMAT_R8, data)); - } - - if (images.size() == 1) { - vrs.vrs_texture = RS::get_singleton()->texture_2d_create(images[0]); - } else { - vrs.vrs_texture = RS::get_singleton()->texture_2d_layered_create(images, RS::TEXTURE_LAYERED_2D_ARRAY); - } - } - - return vrs.vrs_texture; + return RID(); } /** these are optional, so we want dummies **/ diff --git a/servers/xr/xr_interface.h b/servers/xr/xr_interface.h index 809800d8b9e1..7beec219bb55 100644 --- a/servers/xr/xr_interface.h +++ b/servers/xr/xr_interface.h @@ -34,6 +34,7 @@ #include "core/math/projection.h" #include "core/os/thread_safe.h" #include "servers/xr_server.h" +#include "xr_vrs.h" // forward declaration struct BlitToScreen; @@ -133,7 +134,6 @@ public: // These methods are called from the rendering thread. virtual Transform3D get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) = 0; /* get each views transform */ virtual Projection get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) = 0; /* get each view projection matrix */ - virtual RID get_vrs_texture(); /* obtain VRS texture */ virtual RID get_color_texture(); /* obtain color output texture (if applicable) */ virtual RID get_depth_texture(); /* obtain depth output texture (if applicable, used for reprojection) */ virtual RID get_velocity_texture(); /* obtain velocity output texture (if applicable, used for spacewarp) */ @@ -149,19 +149,16 @@ public: virtual bool start_passthrough() { return false; } virtual void stop_passthrough() {} - /** environment blend mode. */ + /** environment blend mode **/ virtual Array get_supported_environment_blend_modes(); virtual XRInterface::EnvironmentBlendMode get_environment_blend_mode() const { return XR_ENV_BLEND_MODE_OPAQUE; } virtual bool set_environment_blend_mode(EnvironmentBlendMode mode) { return false; } + /** VRS **/ + virtual RID get_vrs_texture(); /* obtain VRS texture */ + XRInterface(); ~XRInterface(); - -private: - struct VRSData { - RID vrs_texture; - Size2i size; - } vrs; }; VARIANT_ENUM_CAST(XRInterface::Capabilities); diff --git a/servers/xr/xr_vrs.cpp b/servers/xr/xr_vrs.cpp new file mode 100644 index 000000000000..9d1e2f2068dc --- /dev/null +++ b/servers/xr/xr_vrs.cpp @@ -0,0 +1,151 @@ +/**************************************************************************/ +/* xr_vrs.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 "xr_vrs.h" + +#include "servers/rendering/renderer_scene_render.h" +#include "servers/rendering_server.h" + +void XRVRS::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_vrs_min_radius"), &XRVRS::get_vrs_min_radius); + ClassDB::bind_method(D_METHOD("set_vrs_min_radius", "radius"), &XRVRS::set_vrs_min_radius); + + ClassDB::bind_method(D_METHOD("get_vrs_strength"), &XRVRS::get_vrs_strength); + ClassDB::bind_method(D_METHOD("set_vrs_strength", "strength"), &XRVRS::set_vrs_strength); + + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "vrs_min_radius", PROPERTY_HINT_RANGE, "1.0,100.0,1.0"), "set_vrs_min_radius", "get_vrs_min_radius"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "vrs_strength", PROPERTY_HINT_RANGE, "0.1,10.0,0.1"), "set_vrs_strength", "get_vrs_strength"); + + ClassDB::bind_method(D_METHOD("make_vrs_texture", "target_size", "eye_foci"), &XRVRS::make_vrs_texture); +} + +XRVRS::~XRVRS() { + if (vrs_texture.is_valid()) { + ERR_FAIL_NULL(RS::get_singleton()); + RS::get_singleton()->free(vrs_texture); + vrs_texture = RID(); + } +} + +float XRVRS::get_vrs_min_radius() const { + return vrs_min_radius; +} + +void XRVRS::set_vrs_min_radius(float p_vrs_min_radius) { + if (p_vrs_min_radius < 1.0) { + WARN_PRINT_ONCE("VRS minimum radius can not be set below 1.0"); + vrs_min_radius = 1.0; + } else if (p_vrs_min_radius > 100.0) { + WARN_PRINT_ONCE("VRS minimum radius can not be set above 100.0"); + vrs_min_radius = 100.0; + } else { + vrs_min_radius = p_vrs_min_radius; + vrs_dirty = true; + } +} + +float XRVRS::get_vrs_strength() const { + return vrs_strength; +} + +void XRVRS::set_vrs_strength(float p_vrs_strength) { + if (p_vrs_strength < 0.1) { + WARN_PRINT_ONCE("VRS strength can not be set below 0.1"); + vrs_strength = 0.1; + } else if (p_vrs_strength > 10.0) { + WARN_PRINT_ONCE("VRS strength can not be set above 10.0"); + vrs_strength = 10.0; + } else { + vrs_strength = p_vrs_strength; + vrs_dirty = true; + } +} + +RID XRVRS::make_vrs_texture(const Size2 &p_target_size, const PackedVector2Array &p_eye_foci) { + ERR_FAIL_COND_V(p_eye_foci.is_empty(), RID()); + + int32_t texel_width = RD::get_singleton()->limit_get(RD::LIMIT_VRS_TEXEL_WIDTH); + int32_t texel_height = RD::get_singleton()->limit_get(RD::LIMIT_VRS_TEXEL_HEIGHT); + + Size2 vrs_size = Size2(0.5 + p_target_size.x / texel_width, 0.5 + p_target_size.y / texel_height).round(); + float max_radius = 0.5 * MIN(vrs_size.x, vrs_size.y); // Maximum radius that fits inside of our image + float min_radius = vrs_min_radius * max_radius / 100.0; // Minimum radius as a percentage of our size + real_t outer_radius = MAX(1.0, (max_radius - min_radius) / vrs_strength); + Size2 vrs_sizei = vrs_size; + + // Our density map is now unified, with a value of (0.0, 0.0) meaning a 1x1 texel size and (1.0, 1.0) an max texel size. + // For our standard VRS extension on Vulkan this means a maximum of 8x8. + // For the density map extension this scales depending on the max texel size. + + if (target_size != vrs_sizei || eye_foci != p_eye_foci || vrs_dirty) { + // Out with the old. + if (vrs_texture.is_valid()) { + RS::get_singleton()->free(vrs_texture); + vrs_texture = RID(); + } + + // In with the new. + Vector> images; + target_size = vrs_sizei; + eye_foci = p_eye_foci; + + for (int i = 0; i < eye_foci.size() && i < RendererSceneRender::MAX_RENDER_VIEWS; i++) { + PackedByteArray data; + data.resize(vrs_sizei.x * vrs_sizei.y * 2); + uint8_t *data_ptr = data.ptrw(); + + Vector2i view_center; + view_center.x = int(vrs_size.x * (eye_foci[i].x + 1.0) * 0.5); + view_center.y = int(vrs_size.y * (eye_foci[i].y + 1.0) * 0.5); + + int d = 0; + for (int y = 0; y < vrs_sizei.y; y++) { + for (int x = 0; x < vrs_sizei.x; x++) { + Vector2 offset = Vector2(x - view_center.x, y - view_center.y); + real_t density = 255.0 * MAX(0.0, (Math::abs(offset.x) - min_radius) / outer_radius); + data_ptr[d++] = MIN(255, density); + density = 255.0 * MAX(0.0, (Math::abs(offset.y) - min_radius) / outer_radius); + data_ptr[d++] = MIN(255, density); + } + } + images.push_back(Image::create_from_data(vrs_sizei.x, vrs_sizei.y, false, Image::FORMAT_RG8, data)); + } + + if (images.size() == 1) { + vrs_texture = RS::get_singleton()->texture_2d_create(images[0]); + } else { + vrs_texture = RS::get_singleton()->texture_2d_layered_create(images, RS::TEXTURE_LAYERED_2D_ARRAY); + } + + vrs_dirty = false; + } + + return vrs_texture; +} diff --git a/servers/xr/xr_vrs.h b/servers/xr/xr_vrs.h new file mode 100644 index 000000000000..35dfe55620d0 --- /dev/null +++ b/servers/xr/xr_vrs.h @@ -0,0 +1,67 @@ +/**************************************************************************/ +/* xr_vrs.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 XR_VRS_H +#define XR_VRS_H + +#include "core/object/class_db.h" +#include "core/object/object.h" +#include "core/templates/rid.h" +#include "core/variant/variant.h" + +/* This is a helper class for generating stereoscopic VRS images */ + +class XRVRS : public Object { + GDCLASS(XRVRS, Object); + +private: + float vrs_min_radius = 20.0; + float vrs_strength = 1.0; + bool vrs_dirty = true; + + RID vrs_texture; + Size2i target_size; + PackedVector2Array eye_foci; + +protected: + static void _bind_methods(); + +public: + ~XRVRS(); + + float get_vrs_min_radius() const; + void set_vrs_min_radius(float p_vrs_min_radius); + float get_vrs_strength() const; + void set_vrs_strength(float p_vrs_strength); + + RID make_vrs_texture(const Size2 &p_target_size, const PackedVector2Array &p_eye_foci); +}; + +#endif // XR_VRS_H