diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index 42531083a04d..2828215f57b6 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 f80814b89cf0..9a4f68bbff70 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,18 @@ 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
+ if (shadow_map_update != ShadowMapUpdate::SHADOW_MAP_UPDATE_DIRECTIONAL) {
+ // Set up point light shadow maps.
- //SortArray sorter;
- //sorter.sort(light_cull_result,light_cull_count);
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 ca5b2285dc19..85717edcef63 100644
--- a/servers/visual/visual_server_scene.h
+++ b/servers/visual/visual_server_scene.h
@@ -54,6 +54,12 @@ class VisualServerScene {
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 @@ class VisualServerScene {
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 45e9bd55c616..e0e3a0a7dd0e 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);