From 7d60d68c7ec83746a29a59da06a14b6bd56bc17c Mon Sep 17 00:00:00 2001 From: Hugo Locurcio Date: Sat, 23 Oct 2021 13:29:33 +0200 Subject: [PATCH] Add an option to update shadow maps less often This can be used to improve performance on low-end setups, at the cost of shadows visibly lagging behind for dynamic lights/objects when up close. This option works best with lights that use Reverse Cull Face and a low negative bias value. Co-authored-by: Manuele Finocchiaro --- doc/classes/ProjectSettings.xml | 3 +++ servers/visual/visual_server_scene.cpp | 29 +++++++++++++++++++------- servers/visual/visual_server_scene.h | 7 +++++++ servers/visual_server.cpp | 1 + 4 files changed, 32 insertions(+), 8 deletions(-) diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 42531083a0..2828215f57 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -1451,6 +1451,9 @@ Lower-end override for [member rendering/quality/shadows/filter_mode] on mobile devices, due to performance concerns or driver support. + + If [code]true[/code], directional and point light shadows are only updated every 2 frames instead of being updated every frame. Updates are staggered across frames to avoid stuttering. This improves performance at the cost of shadows updating in a more "choppy" manner, especially at lower framerates. The difference is mainly noticeable with fast-moving lights, especially when close to the camera. + Forces [MeshInstance] to always perform skinning on the CPU (applies to both GLES2 and GLES3). See also [member rendering/quality/skinning/software_skinning_fallback]. diff --git a/servers/visual/visual_server_scene.cpp b/servers/visual/visual_server_scene.cpp index f80814b89c..401590bd18 100644 --- a/servers/visual/visual_server_scene.cpp +++ b/servers/visual/visual_server_scene.cpp @@ -2641,8 +2641,19 @@ void VisualServerScene::_prepare_scene(const Transform p_cam_transform, const Ca RID *directional_light_ptr = &light_instance_cull_result[light_cull_count]; directional_light_count = 0; - // directional lights - { + if (bool(GLOBAL_GET("rendering/quality/shadows/update_every_2_frames"))) { + // Toggle between directional and point light shadow updating every frame. + // This update staggering avoids stuttering by splitting CPU/GPU load across frames. + if (shadow_map_update == ShadowMapUpdate::SHADOW_MAP_UPDATE_DIRECTIONAL) { + shadow_map_update = ShadowMapUpdate::SHADOW_MAP_UPDATE_POINT; + } else { + shadow_map_update = ShadowMapUpdate::SHADOW_MAP_UPDATE_DIRECTIONAL; + } + } else { + shadow_map_update = ShadowMapUpdate::SHADOW_MAP_UPDATE_POINT_AND_DIRECTIONAL; + } + + { // directional lights Instance **lights_with_shadow = (Instance **)alloca(sizeof(Instance *) * scenario->directional_lights.size()); int directional_shadow_count = 0; @@ -2668,17 +2679,19 @@ void VisualServerScene::_prepare_scene(const Transform p_cam_transform, const Ca } } - VSG::scene_render->set_directional_shadow_count(directional_shadow_count); + if (shadow_map_update != ShadowMapUpdate::SHADOW_MAP_UPDATE_POINT) { + VSG::scene_render->set_directional_shadow_count(directional_shadow_count); - for (int i = 0; i < directional_shadow_count; i++) { - _light_instance_update_shadow(lights_with_shadow[i], p_cam_transform, p_cam_projection, p_cam_orthogonal, p_shadow_atlas, scenario); + for (int i = 0; i < directional_shadow_count; i++) { + _light_instance_update_shadow(lights_with_shadow[i], p_cam_transform, p_cam_projection, p_cam_orthogonal, p_shadow_atlas, scenario); + } } } - { //setup shadow maps - //SortArray sorter; - //sorter.sort(light_cull_result,light_cull_count); + if (shadow_map_update != ShadowMapUpdate::SHADOW_MAP_UPDATE_DIRECTIONAL) { + // Set up point light shadow maps. + for (int i = 0; i < light_cull_count; i++) { Instance *ins = light_cull_result[i]; diff --git a/servers/visual/visual_server_scene.h b/servers/visual/visual_server_scene.h index ca5b2285dc..85717edcef 100644 --- a/servers/visual/visual_server_scene.h +++ b/servers/visual/visual_server_scene.h @@ -54,6 +54,12 @@ public: MAX_EXTERIOR_PORTALS = 128, }; + enum ShadowMapUpdate { + SHADOW_MAP_UPDATE_POINT_AND_DIRECTIONAL, // Update both point and directional light shadows for this frame (default). + SHADOW_MAP_UPDATE_POINT, // Update point light shadows only for this frame. + SHADOW_MAP_UPDATE_DIRECTIONAL, // Update directional light shadows only for this frame. + }; + uint64_t render_pass; static VisualServerScene *singleton; @@ -508,6 +514,7 @@ public: int directional_light_count; RID reflection_probe_instance_cull_result[MAX_REFLECTION_PROBES_CULLED]; int reflection_probe_cull_count; + ShadowMapUpdate shadow_map_update = ShadowMapUpdate::SHADOW_MAP_UPDATE_POINT_AND_DIRECTIONAL; RID_Owner instance_owner; diff --git a/servers/visual_server.cpp b/servers/visual_server.cpp index 45e9bd55c6..e0e3a0a7dd 100644 --- a/servers/visual_server.cpp +++ b/servers/visual_server.cpp @@ -2635,6 +2635,7 @@ VisualServer::VisualServer() { GLOBAL_DEF("rendering/quality/shadows/filter_mode", 1); GLOBAL_DEF("rendering/quality/shadows/filter_mode.mobile", 0); ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/shadows/filter_mode", PropertyInfo(Variant::INT, "rendering/quality/shadows/filter_mode", PROPERTY_HINT_ENUM, "Disabled,PCF5,PCF13")); + GLOBAL_DEF("rendering/quality/shadows/update_every_2_frames", false); GLOBAL_DEF("rendering/quality/reflections/texture_array_reflections", true); GLOBAL_DEF("rendering/quality/reflections/texture_array_reflections.mobile", false);