diff --git a/modules/mobile_vr/mobile_vr_interface.cpp b/modules/mobile_vr/mobile_vr_interface.cpp index e5f1a940ece2..b4e639078caa 100644 --- a/modules/mobile_vr/mobile_vr_interface.cpp +++ b/modules/mobile_vr/mobile_vr_interface.cpp @@ -574,18 +574,11 @@ 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)); - } + PackedVector2Array eye_foci = get_vrs_eye_foci(aspect_ratio); return xr_vrs.make_vrs_texture(target_size, eye_foci); } diff --git a/modules/openxr/openxr_interface.cpp b/modules/openxr/openxr_interface.cpp index 68e04694e3d0..5077628a5dcd 100644 --- a/modules/openxr/openxr_interface.cpp +++ b/modules/openxr/openxr_interface.cpp @@ -1471,6 +1471,22 @@ Vector3 OpenXRInterface::get_hand_joint_angular_velocity(Hand p_hand, HandJoints return Vector3(); } +PackedVector2Array OpenXRInterface::get_vrs_eye_foci(float p_aspect) { + PackedVector2Array ret; + + if (!openxr_api) { + return ret; + } + + uint32_t view_count = get_view_count(); + + for (uint32_t v = 0; v < view_count; v++) { + ret.push_back(openxr_api->get_eye_focus(v, p_aspect)); + } + + return ret; +} + RID OpenXRInterface::get_vrs_texture() { if (!openxr_api) { return RID(); diff --git a/modules/openxr/openxr_interface.h b/modules/openxr/openxr_interface.h index d1bf2aaf7878..3f87a26204de 100644 --- a/modules/openxr/openxr_interface.h +++ b/modules/openxr/openxr_interface.h @@ -285,6 +285,7 @@ class OpenXRInterface : public XRInterface { 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 PackedVector2Array get_vrs_eye_foci(float p_aspect) override; virtual RID get_vrs_texture() override; OpenXRInterface(); diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index dd3ae1ad3cbe..d22cc54a653f 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -3672,6 +3672,9 @@ void Viewport::set_vrs_mode(Viewport::VRSMode p_vrs_mode) { case VRS_XR: { RS::get_singleton()->viewport_set_vrs_mode(viewport, RS::VIEWPORT_VRS_XR); } break; + case VRS_XR_DYNAMIC: { + RS::get_singleton()->viewport_set_vrs_mode(viewport, RS::VIEWPORT_VRS_XR_DYNAMIC); + } break; default: { RS::get_singleton()->viewport_set_vrs_mode(viewport, RS::VIEWPORT_VRS_DISABLED); } break; @@ -4831,7 +4834,7 @@ void Viewport::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "texture_mipmap_bias", PROPERTY_HINT_RANGE, "-2,2,0.001"), "set_texture_mipmap_bias", "get_texture_mipmap_bias"); 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_mode", PROPERTY_HINT_ENUM, "Disabled,Texture,Depth buffer,XR,XR Dynamic"), "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 @@ -4955,6 +4958,7 @@ void Viewport::_bind_methods() { BIND_ENUM_CONSTANT(VRS_DISABLED); BIND_ENUM_CONSTANT(VRS_TEXTURE); BIND_ENUM_CONSTANT(VRS_XR); + BIND_ENUM_CONSTANT(VRS_XR_DYNAMIC); BIND_ENUM_CONSTANT(VRS_MAX); BIND_ENUM_CONSTANT(VRS_UPDATE_DISABLED); @@ -4968,7 +4972,7 @@ void Viewport::_validate_property(PropertyInfo &p_property) const { p_property.usage = PROPERTY_USAGE_NO_EDITOR; } - if (vrs_mode == VRS_DISABLED && (p_property.name == "vrs_update_mode")) { + if ((vrs_mode == VRS_DISABLED || vrs_mode == VRS_XR_DYNAMIC) && (p_property.name == "vrs_update_mode")) { p_property.usage = PROPERTY_USAGE_NO_EDITOR; } } diff --git a/scene/main/viewport.h b/scene/main/viewport.h index 3a5ad2d83cb4..a6fcf2aca1dc 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -211,6 +211,7 @@ class Viewport : public Node { VRS_DISABLED, VRS_TEXTURE, VRS_XR, + VRS_XR_DYNAMIC, VRS_MAX }; diff --git a/servers/rendering/renderer_rd/effects/vrs.cpp b/servers/rendering/renderer_rd/effects/vrs.cpp index cbeefdd4d189..6f3884781d10 100644 --- a/servers/rendering/renderer_rd/effects/vrs.cpp +++ b/servers/rendering/renderer_rd/effects/vrs.cpp @@ -42,10 +42,14 @@ using namespace RendererRD; VRS::VRS() { { Vector vrs_modes; - vrs_modes.push_back("\n"); // VRS_DEFAULT - vrs_modes.push_back("\n#define USE_MULTIVIEW\n"); // VRS_MULTIVIEW - vrs_modes.push_back("\n#define SPLIT_RG\n"); // VRS_RG - vrs_modes.push_back("\n#define SPLIT_RG\n#define USE_MULTIVIEW\n"); // VRS_RG_MULTIVIEW + vrs_modes.push_back("\n#define SOURCE_TEXTURE\n"); // VRS_DEFAULT + vrs_modes.push_back("\n#define SOURCE_TEXTURE\n#define USE_MULTIVIEW\n"); // VRS_MULTIVIEW + vrs_modes.push_back("\n#define SOURCE_TEXTURE\n#define SPLIT_RG\n"); // VRS_RG + vrs_modes.push_back("\n#define SOURCE_TEXTURE\n#define SPLIT_RG\n#define USE_MULTIVIEW\n"); // VRS_RG_MULTIVIEW + vrs_modes.push_back("\n"); // VRS_DYNAMIC + vrs_modes.push_back("\n#define USE_MULTIVIEW\n"); // VRS_DYNAMIC_MULTIVIEW + vrs_modes.push_back("\n#define SPLIT_RG\n"); // VRS_DYNAMIC_RG + vrs_modes.push_back("\n#define SPLIT_RG\n#define USE_MULTIVIEW\n"); // VRS_DYNAMIC_RG_MULTIVIEW vrs_shader.shader.initialize(vrs_modes); @@ -106,6 +110,46 @@ void VRS::copy_vrs(RID p_source_rd_texture, RID p_dest_framebuffer, bool p_multi RD::get_singleton()->draw_list_end(); } +void VRS::draw_vrs(RID p_dest_framebuffer, const Vector &p_eye_centers, const float p_min_radius, const float p_max_radius, const float p_aspect_ratio) { + MaterialStorage *material_storage = MaterialStorage::get_singleton(); + ERR_FAIL_NULL(material_storage); + + bool multiview = p_eye_centers.size() > 1; + + int mode = 0; + VRSPushConstant push_constant = {}; + bool uses_rg_format = RD::get_singleton()->vrs_get_format() == RD::DATA_FORMAT_R8G8_UNORM; + if (uses_rg_format) { + mode = multiview ? VRS_DYNAMIC_RG_MULTIVIEW : VRS_DYNAMIC_RG; + } else { + mode = multiview ? VRS_DYNAMIC_MULTIVIEW : VRS_DYNAMIC; + + // Default to 4x4 as it's not possible to query the max fragment size from RenderingDevice. This can be improved to use the largest size + // available if this code is moved over to RenderingDevice at some point. + push_constant.max_texel_factor = 2.0; + } + + push_constant.min_radius = p_min_radius; + push_constant.max_radius = p_max_radius; + push_constant.aspect_ratio = p_aspect_ratio; + + int i = 0; + for (const Vector2 &eye_center : p_eye_centers) { + push_constant.eye_center[i][0] = eye_center.x * 0.5 + 0.5; + push_constant.eye_center[i][1] = eye_center.y * 0.5 + 0.5; + i++; + } + + RID shader = vrs_shader.shader.version_get_shader(vrs_shader.shader_version, mode); + ERR_FAIL_COND(shader.is_null()); + + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer); + 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_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(); +} + Size2i VRS::get_vrs_texture_size(const Size2i p_base_size) const { Size2i vrs_texel_size = RD::get_singleton()->vrs_get_texel_size(); return Size2i((p_base_size.x + vrs_texel_size.x - 1) / vrs_texel_size.x, (p_base_size.y + vrs_texel_size.y - 1) / vrs_texel_size.y); @@ -116,6 +160,13 @@ void VRS::update_vrs_texture(RID p_vrs_fb, RID p_render_target) { 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); +#ifndef _3D_DISABLED + if (vrs_mode == RS::VIEWPORT_VRS_XR_DYNAMIC) { + // We're not copying but generating, so always do this! + vrs_update_mode = RS::VIEWPORT_VRS_UPDATE_ALWAYS; + } +#endif // _3D_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"); @@ -144,6 +195,24 @@ void VRS::update_vrs_texture(RID p_vrs_fb, RID p_render_target) { } } } + } else if (vrs_mode == RS::VIEWPORT_VRS_XR_DYNAMIC) { + Ref interface = XRServer::get_singleton()->get_primary_interface(); + if (interface.is_valid()) { + Size2 size = texture_storage->render_target_get_size(p_render_target); + float aspect_ratio = size.x / size.y; + + // Need to get these from our interface: + PackedVector2Array eye_centers = interface->get_vrs_eye_foci(aspect_ratio); + + // Need to vary these based on performance: + float min_radius = 0.1; + float max_radius = 0.3; + + // TODO: should check if any of the above has changed since last frame, + // or VRS texture was marked dirty. Skip draw if neither is the case. + + draw_vrs(p_vrs_fb, eye_centers, min_radius, max_radius, aspect_ratio); + } #endif // _3D_DISABLED } diff --git a/servers/rendering/renderer_rd/effects/vrs.h b/servers/rendering/renderer_rd/effects/vrs.h index be28ef59de5e..f03f1afd90cb 100644 --- a/servers/rendering/renderer_rd/effects/vrs.h +++ b/servers/rendering/renderer_rd/effects/vrs.h @@ -46,14 +46,19 @@ class VRS { VRS_MULTIVIEW, VRS_RG, VRS_RG_MULTIVIEW, + VRS_DYNAMIC, + VRS_DYNAMIC_MULTIVIEW, + VRS_DYNAMIC_RG, + VRS_DYNAMIC_RG_MULTIVIEW, VRS_MAX, }; struct VRSPushConstant { + float eye_center[2][2]; float max_texel_factor; // 4x8, 8x4 and 8x8 are only available on some GPUs. - float res1; - float res2; - float res3; + float min_radius; + float max_radius; + float aspect_ratio; }; struct VRSShader { @@ -68,6 +73,7 @@ class VRS { ~VRS(); void copy_vrs(RID p_source_rd_texture, RID p_dest_framebuffer, bool p_multiview = false); + void draw_vrs(RID p_dest_framebuffer, const Vector &p_eye_centers, const float p_min_radius, const float p_max_radius, const float p_aspect_ratio); Size2i get_vrs_texture_size(const Size2i p_base_size) const; void update_vrs_texture(RID p_vrs_fb, RID p_render_target); diff --git a/servers/rendering/renderer_rd/shaders/effects/vrs.glsl b/servers/rendering/renderer_rd/shaders/effects/vrs.glsl index 1d3463dd2bf5..542eb0fc0e43 100644 --- a/servers/rendering/renderer_rd/shaders/effects/vrs.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/vrs.glsl @@ -20,10 +20,11 @@ layout(location = 0) out vec2 uv_interp; #endif layout(push_constant, std430) uniform Params { + vec2 eye_center[2]; float max_texel_factor; - float res1; - float res2; - float res3; + float min_radius; + float max_radius; + float aspect_ratio; } params; @@ -49,14 +50,22 @@ void main() { #else // has_VK_KHR_multiview #define ViewIndex 0 #endif // has_VK_KHR_multiview +#else // USE_MULTIVIEW +#define ViewIndex 0 #endif //USE_MULTIVIEW #ifdef USE_MULTIVIEW layout(location = 0) in vec3 uv_interp; + +#ifdef SOURCE_TEXTURE layout(set = 0, binding = 0) uniform sampler2DArray source_color; +#endif /* SOURCE_TEXTURE */ #else /* USE_MULTIVIEW */ layout(location = 0) in vec2 uv_interp; + +#ifdef SOURCE_TEXTURE layout(set = 0, binding = 0) uniform sampler2D source_color; +#endif /* SOURCE_TEXTURE */ #endif /* USE_MULTIVIEW */ #ifdef SPLIT_RG @@ -66,10 +75,11 @@ layout(location = 0) out uint frag_color; #endif layout(push_constant, std430) uniform Params { + vec2 eye_center[2]; float max_texel_factor; - float res1; - float res2; - float res3; + float min_radius; + float max_radius; + float aspect_ratio; } params; @@ -80,8 +90,18 @@ void main() { vec2 uv = uv_interp; #endif + vec2 color; +#ifdef SOURCE_TEXTURE // Input is standardized. 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); + color = textureLod(source_color, uv, 0.0).rg; +#else + vec2 offset = uv.xy - params.eye_center[ViewIndex]; // Q: Might need to invert y? + offset.y /= params.aspect_ratio; // Q: or *= ?? + float dist = length(offset); + float density = clamp((dist - params.min_radius) / (params.max_radius - params.min_radius), 0.0, 1.0); + + color = vec2(density); +#endif // SOURCE_TEXTURE #ifdef SPLIT_RG // Density map for VRS according to VK_EXT_fragment_density_map, we can use as is. diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp index e7f513e630f9..8f8b1ca28b28 100644 --- a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp +++ b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp @@ -169,10 +169,10 @@ void RenderSceneBuffersRD::configure(const RenderSceneBuffersConfiguration *p_co // Create our color buffer. const bool resolve_target = msaa_3d != RS::VIEWPORT_MSAA_DISABLED; - create_texture(RB_SCOPE_BUFFERS, RB_TEX_COLOR, base_data_format, get_color_usage_bits(resolve_target, false, can_be_storage)); + create_texture(RB_SCOPE_BUFFERS, RB_TEX_COLOR, base_data_format, get_color_usage_bits(resolve_target, false, can_be_storage), RD::TEXTURE_SAMPLES_1, Size2i(), 0, 1, true, true); // Create our depth buffer. - create_texture(RB_SCOPE_BUFFERS, RB_TEX_DEPTH, get_depth_format(resolve_target, false, can_be_storage), get_depth_usage_bits(resolve_target, false, can_be_storage)); + create_texture(RB_SCOPE_BUFFERS, RB_TEX_DEPTH, get_depth_format(resolve_target, false, can_be_storage), get_depth_usage_bits(resolve_target, false, can_be_storage), RD::TEXTURE_SAMPLES_1, Size2i(), 0, 1, true, true); // Create our MSAA buffers. if (msaa_3d == RS::VIEWPORT_MSAA_DISABLED) { diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 2051d0caac0e..4c8d205497d1 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -2941,6 +2941,7 @@ void RenderingServer::_bind_methods() { BIND_ENUM_CONSTANT(VIEWPORT_VRS_DISABLED); BIND_ENUM_CONSTANT(VIEWPORT_VRS_TEXTURE); BIND_ENUM_CONSTANT(VIEWPORT_VRS_XR); + BIND_ENUM_CONSTANT(VIEWPORT_VRS_XR_DYNAMIC); BIND_ENUM_CONSTANT(VIEWPORT_VRS_MAX); BIND_ENUM_CONSTANT(VIEWPORT_VRS_UPDATE_DISABLED); diff --git a/servers/rendering_server.h b/servers/rendering_server.h index 0917af73c6a6..fc08c5688e81 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -1086,6 +1086,7 @@ class RenderingServer : public Object { VIEWPORT_VRS_DISABLED, VIEWPORT_VRS_TEXTURE, VIEWPORT_VRS_XR, + VIEWPORT_VRS_XR_DYNAMIC, VIEWPORT_VRS_MAX, }; diff --git a/servers/xr/xr_interface.cpp b/servers/xr/xr_interface.cpp index e9d2fc85203b..9859454d9339 100644 --- a/servers/xr/xr_interface.cpp +++ b/servers/xr/xr_interface.cpp @@ -169,6 +169,19 @@ int XRInterface::get_camera_feed_id() { return 0; } +PackedVector2Array XRInterface::get_vrs_eye_foci(float p_aspect) { + PackedVector2Array eye_foci; + + for (uint32_t v = 0; v < get_view_count(); v++) { + Projection cm = get_projection_for_view(v, p_aspect, 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 eye_foci; +} + RID XRInterface::get_vrs_texture() { return RID(); } diff --git a/servers/xr/xr_interface.h b/servers/xr/xr_interface.h index abd5476fc93b..3350a7af37bc 100644 --- a/servers/xr/xr_interface.h +++ b/servers/xr/xr_interface.h @@ -155,6 +155,7 @@ class XRInterface : public RefCounted { virtual bool set_environment_blend_mode(EnvironmentBlendMode mode) { return false; } /** VRS **/ + virtual PackedVector2Array get_vrs_eye_foci(float p_aspect); /* obtain eye foci, used with VRS_XR_DYNAMIC */ virtual RID get_vrs_texture(); /* obtain VRS texture */ XRInterface(); diff --git a/servers/xr/xr_interface_extension.cpp b/servers/xr/xr_interface_extension.cpp index 93042baec54e..c566a6fd7eba 100644 --- a/servers/xr/xr_interface_extension.cpp +++ b/servers/xr/xr_interface_extension.cpp @@ -234,6 +234,15 @@ Projection XRInterfaceExtension::get_projection_for_view(uint32_t p_view, double return Projection(); } +PackedVector2Array XRInterfaceExtension::get_vrs_eye_foci(float p_aspect) { + PackedVector2Array vrs_eye_foci; + if (GDVIRTUAL_CALL(_get_vrs_eye_foci, p_aspect, vrs_eye_foci)) { + return vrs_eye_foci; + } else { + return XRInterface::get_vrs_eye_foci(p_aspect); + } +} + RID XRInterfaceExtension::get_vrs_texture() { RID vrs_texture; if (GDVIRTUAL_CALL(_get_vrs_texture, vrs_texture)) { diff --git a/servers/xr/xr_interface_extension.h b/servers/xr/xr_interface_extension.h index 5ec19a975d2f..f8afd5962eb4 100644 --- a/servers/xr/xr_interface_extension.h +++ b/servers/xr/xr_interface_extension.h @@ -103,6 +103,7 @@ class XRInterfaceExtension : public XRInterface { virtual Transform3D get_camera_transform() override; virtual Transform3D get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) override; virtual Projection get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) override; + virtual PackedVector2Array get_vrs_eye_foci(float p_aspect) override; virtual RID get_vrs_texture() override; virtual RID get_color_texture() override; virtual RID get_depth_texture() override; @@ -113,6 +114,7 @@ class XRInterfaceExtension : public XRInterface { GDVIRTUAL0R(Transform3D, _get_camera_transform); GDVIRTUAL2R(Transform3D, _get_transform_for_view, uint32_t, const Transform3D &); GDVIRTUAL4R(PackedFloat64Array, _get_projection_for_view, uint32_t, double, double, double); + GDVIRTUAL1R(PackedVector2Array, _get_vrs_eye_foci, float); GDVIRTUAL0R(RID, _get_vrs_texture); GDVIRTUAL0R(RID, _get_color_texture); GDVIRTUAL0R(RID, _get_depth_texture);