Skip to content

Commit

Permalink
Add an option to update shadow maps less often
Browse files Browse the repository at this point in the history
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 <m4nu3lf@gmail.com>
  • Loading branch information
Calinou and m4nu3lf committed Nov 23, 2021
1 parent b0cd38b commit 02a238c
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 8 deletions.
3 changes: 3 additions & 0 deletions doc/classes/ProjectSettings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1451,6 +1451,9 @@
<member name="rendering/quality/shadows/filter_mode.mobile" type="int" setter="" getter="" default="0">
Lower-end override for [member rendering/quality/shadows/filter_mode] on mobile devices, due to performance concerns or driver support.
</member>
<member name="rendering/quality/shadows/update_every_2_frames" type="bool" setter="" getter="" default="false">
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.
</member>
<member name="rendering/quality/skinning/force_software_skinning" type="bool" setter="" getter="" default="false">
Forces [MeshInstance] to always perform skinning on the CPU (applies to both GLES2 and GLES3).
See also [member rendering/quality/skinning/software_skinning_fallback].
Expand Down
28 changes: 20 additions & 8 deletions servers/visual/visual_server_scene.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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<Instance*,_InstanceLightsort> sorter;
//sorter.sort(light_cull_result,light_cull_count);
for (int i = 0; i < light_cull_count; i++) {
Instance *ins = light_cull_result[i];

Expand Down
7 changes: 7 additions & 0 deletions servers/visual/visual_server_scene.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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> instance_owner;

Expand Down
1 change: 1 addition & 0 deletions servers/visual_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit 02a238c

Please sign in to comment.