Skip to content

Refactor shadow caching system #554

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 46 commits into from
May 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
0059c70
Very debuggy code just start nothing functional or usable yet.
FrancescoC-unity Mar 31, 2020
8154bb4
remove old cached stuff (not finished, need switch branch)
FrancescoC-unity Apr 1, 2020
80a940d
revert wrongful commit
FrancescoC-unity Apr 1, 2020
487b3f3
A bit of restructuring
FrancescoC-unity Apr 2, 2020
1703f6b
A good point as any to sync up
FrancescoC-unity Apr 2, 2020
ff40c80
sync
FrancescoC-unity Apr 3, 2020
6769905
Move from old repo...
FrancescoC-unity Apr 6, 2020
d8695e5
fix regression
FrancescoC-unity Apr 6, 2020
f48b46a
Handle case in which shadow doesn't fit
FrancescoC-unity Apr 6, 2020
4aba20b
leftover not committed
FrancescoC-unity Apr 6, 2020
78cbfd8
Fix some issues with culling of point lights from the
FrancescoC-unity Apr 7, 2020
73bc818
preliminary support for cascaded shadows
FrancescoC-unity Apr 7, 2020
85d6d75
Fix some issue with the directional
FrancescoC-unity Apr 7, 2020
f1566c9
Subshadow update
FrancescoC-unity Apr 7, 2020
d0d7e7d
Add some guards, some debug and the new C++ api for infinite bounds
FrancescoC-unity Apr 8, 2020
a357b98
Some UI for the resolution
FrancescoC-unity Apr 9, 2020
fe0589d
Merge branch 'HDRP/staging' into HDRP/refactor-shadow-caching-system …
FrancescoC-unity Apr 9, 2020
68194d0
Fix merge issues
FrancescoC-unity Apr 9, 2020
b46c323
Update debug cs.hlsl
FrancescoC-unity Apr 9, 2020
1e48577
Render graph stuff and some leftover removal
FrancescoC-unity Apr 9, 2020
90b3c11
micro cleanup
FrancescoC-unity Apr 9, 2020
cf5346b
Fix behaviour when defragging and excess lights are there
FrancescoC-unity Apr 9, 2020
d2a8a7d
Public API
FrancescoC-unity Apr 10, 2020
64b7151
Remove dbg names
FrancescoC-unity Apr 10, 2020
ab8428e
Bug on debug view of area atlas
FrancescoC-unity Apr 10, 2020
d320421
Missing field on new PCSS signature for directional
FrancescoC-unity Apr 10, 2020
2e3d1f2
More public API
FrancescoC-unity Apr 10, 2020
0b2c79b
Refresh cached shadows on resolution changes.
FrancescoC-unity Apr 16, 2020
89acc00
Merge branch 'HDRP/staging' into HDRP/refactor-shadow-caching-system
FrancescoC-unity Apr 30, 2020
3cea555
merge conflicts patchup
FrancescoC-unity Apr 30, 2020
f28f392
Fix missing shadows on normal shadows
FrancescoC-unity Apr 30, 2020
751c11a
Small cleanup and turning positive the doesn'tHavePlacement
FrancescoC-unity Apr 30, 2020
9419c02
Fix case on switching light type and issue with non-rectangle area li…
FrancescoC-unity May 5, 2020
f765fbf
tentative fix for error on build
FrancescoC-unity May 5, 2020
f05e39d
Debug logging
FrancescoC-unity May 6, 2020
a7cac1c
update naming of debug
FrancescoC-unity May 6, 2020
45c706d
Fix issue with double inclusion
FrancescoC-unity May 11, 2020
0d177c8
DOCS
FrancescoC-unity May 19, 2020
709a723
upgrade guide
FrancescoC-unity May 19, 2020
97ecd8f
Merge branch 'HDRP/staging' into HDRP/refactor-shadow-caching-system
FrancescoC-unity May 19, 2020
4c19ac4
post merge fixups
FrancescoC-unity May 19, 2020
ba72963
leftover comment
FrancescoC-unity May 19, 2020
be80680
changelog
FrancescoC-unity May 19, 2020
ce9d867
Doc changes in upgrade guide
FrancescoC-unity May 19, 2020
0aff2be
have print debug info only in editor
FrancescoC-unity May 22, 2020
92d58f5
Merge branch 'HDRP/staging' into HDRP/refactor-shadow-caching-system
sebastienlagarde May 22, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions com.unity.render-pipelines.high-definition/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Fixed the sub surface mask not being taken into account when computing ray traced sub surface scattering.
- Slightly changed the TAA anti-flicker mechanism so that it is more aggressive on almost static images (only on High preset for now).
- Changed default exposure compensation to 0.
- Refactored shadow caching system.

## [7.1.1] - 2019-09-05

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,50 @@ You can use **Update Mode** to specify the calculation method HDRP uses to updat
| **On Enable** | HDRP updates the shadow maps for the light whenever you enable the GameObject. |
| **On Demand** | HDRP updates the shadow maps for the light every time you request them. To do this, call the RequestShadowMapRendering() method in the Light's HDAdditionalLightData component. |

**Note:** no matter what Update Mode a Light uses, if Unity resizes the content of the shadow atlas (due to shadow maps not fitting on the atlas at their original resolution), Unity also updates the shadow map to perform the required rescaling.
The High Definition Render Pipeline (HDRP) uses shadow caching to increase performance by not unnecessarily updating the shadow maps for [Lights](Light-Component.md). HDRP has shadow atlases for punctual, area, and directional Lights, as well as separate shadow atlases specifically for cached punctual and cached area Lights. For cached directional Lights, they use the same atlas as normal directional Lights.
A Light's **Update Mode** determines whether or not HDRP caches its shadow map:If you set a Light's **Update Mode** to **OnEnable** or **OnDemand**, HDRP caches the Light's shadow map.If you set a Light's **Update Mode** to **Every Frame**, HDRP does not cache the Light's shadow map.
When a Light that caches its shadows renders its shadow map for the first time, HDRP registers it with the cached shadow manager which assigns the shadow map to a cached shadow atlas. In the case of directional Lights, HDRP uses the same shadow atlas for cached and non-cached directional Lights.

If the Light's **Update Mode** is set to **OnDemand**, you can manually request HDRP to update the Light's shadow map. To do this, access the Light's **HDAdditionalLightData** component and call the `RequestShadowMapRendering` function. Also, if the Light has multiple shadows (e.g. multiple cascades of a directional light), you can request the update of a specific sub-shadow. To do this, use the `RequestSubShadowMapRendering(shadowIndex)` function.
For a Light that does cache its shadows, if you disable it or set its **Update Mode** to **Every Frame**, you can tell HDRP to preserve the Light's shadow map's place in the cached shadow atlas. This means that, if you enable the Light again, HDRP does not need to re-render the shadow map or place it into a shadow atlas. For information on how to make a Light preserve its shadow map's place in the cached shadow atlas, see [Preserving shadow atlas placement](#preserving-shadow-atlas-placement).

### Customising shadow caching
HDRP caches shadow maps for punctual Lights into one atlas, area Lights into another, and directional Lights into the same atlas as non-cached Directional Lights. You can change the resolution of the first two cached shadow atlases independently of one another. To do this:

1. Select an HDRP Asset to view it in the Inspector.
2. For punctual lights, go to **Lighting > Shadows > Punctual Light Shadows**. For area lights, go to **Lighting > Shadows > Area Light Shadows**.
3. Set the value for **Cached Shadow Atlas Resolution** to the value you want. To help with shadow atlas organisation, try to keep the resolution of individual shadow maps as a multiple of 64. For the most optimal organisation, set the same resolution to as many shadow maps as possible.

If the shadow atlas is full when a Light requests a spot, the cached shadow manager does not add the Light's shadow map and thus the Light does not cast shadows. This means that it is important to manage the space you have available. To check if a Light can fit in the shadow atlas, you can use the `HDCachedShadowManager.instance.WouldFitInAtlas` helper function. To see if a Light already has a place in the atlas or if it is waiting for one, the [Render Pipeline Debug window](Render-Pipeline-Debug-Window.md) includes an option which logs the status of the cached shadow atlas. To use this:

1. Click menu: **Window > Render Pipeline > Render Pipeline Debug**.
2. Go to **Lighting > Shadows**.
3. Click the **Log Cached Shadow Atlas Status** button. This prints a message to the Console window which describes whether a Light has a place in the atlas or is waiting for one.



After a Scene loads with all the already placed Lights, if you add a new Light with cached shadows to the Scene, HDRP tries to place it in order to fill the holes in the atlas. However, depending on the order of insertion, the atlas may be fragmented and the holes available are not enough to place the Light's shadow map in. In this case, you can defragment the atlas to allow for additional Lights. To do this, pass the target atlas into the following function: `HDCachedShadowManager.instance.DefragAtlas`
Note that this causes HDRP to mark all the shadow maps in the atlas as dirty which means HDRP renders them the moment their parent Light becomes visible.

### Preserving shadow atlas placement

If you disable the Light or change its **Update Mode** to **Every Frame**, the cached shadow manager unreserves the Light's shadow map's space in the cached shadow atlas and HDRP begins to render the Light's shadow map to the normal shadow atlases every frame. If the cached shadow manager needs to allocate space on the atlas for another Light, it can overwrite the space currently taken up by the original Light's shadow map.
If you plan to only temporarily set a Light's **Update Mode** to **Every Frame** and want to set it back to **On Enable** or **On Demand** later, you can preserve the Light's shadow map placement in its atlas. This is useful, for example, if you want HDRP to cache a far away Light's shadow map, but update it every frame when it gets close to the [Camera](HDRP-Camera.md). To do this, access the Light's **HDAdditionalLightData** component and enable the **preserveCachedShadow** property. If this property is set to `true`, HDRP preserves the Light's shadow map's space in its shadow atlas. Note that even if this property is enabled, if you destroy the Light, it loses its placement in the shadow atlas.

### Notes

While you are in the Unity Editor, HDRP updates shadow maps whenever you modify the Light that casts them. In a built application, HDRP refreshes cached shadow maps when you change different properties on the Light or when you call one of the following functions:

- SetShadowResolution()
- SetShadowResolutionLevel()
- SetShadowResolutionOverride()
- SetShadowUpdateMode() or shadowUpdateMode. In this case, HDRP only refreshes the cached shadow maps if the mode changes between Every Frame and not Every Frame).


Be aware that anything that is view-dependent is likely to create problems with cached shadow maps because HDRP does not automatically update them as the main view moves. A non-obvious example of this is tessellation. Because tessellation factor is view-dependent, the geometry that the main camera sees might mismatch the geometry that HDRP rendered into the cached shadow map. If this visibly occurs, trigger a request for HDRP to update the Light's shadow map. To do this, make sure the Light's **Update Mode** is set to **On Demand** and call `RequestShadowMapRendering`.




## Contact Shadows

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@ In the High Definition Render Pipeline (HDRP), some features work differently be

From Unity 2020.2, it is not necessary to change the [HDRP Config package](HDRP-Config-Package.html) in order to set the [Shadows filtering quality](HDRP-Asset.html#FilteringQualities) for Deferred rendering. Instead the filtering quality can be simply set on the [HDRP Asset](HDRP-Asset.html#FilteringQualities) similarly to what was previously setting only the quality for Forward. Note that if previously the Shadow filtering quality wasn't setup on medium on the HDRP Asset you will experience a change of shadow quality as now it will be taken into account.

Starting from 2020.2, HDRP now stores OnEnable and OnDemand shadows in a separate atlas and more API is available to handle them. For more information, see [Shadows in HDRP](Shadows-in-HDRP.md).

From Unity 2020.2, the shader function `SampleShadow_PCSS` now requires you to pass in an additional float2 parameter which contains the shadow atlas resolution in x and the inverse of the atlas resolution in y.
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ public class GeneralSection
public static readonly GUIContent maxDecalContent = EditorGUIUtility.TrTextContent("Maximum Clustered Decals on Screen", "Sets the maximum number of decals that can affect transparent GameObjects on screen.");

public static readonly GUIContent resolutionContent = EditorGUIUtility.TrTextContent("Resolution", "Specifies the resolution of the shadow Atlas.");
public static readonly GUIContent cachedShadowAtlasResolution = EditorGUIUtility.TrTextContent("Cached Shadow Atlas Resolution", "Specifies the resolution of the shadow Atlas that contains the cached shadow maps.");
public static readonly GUIContent directionalShadowPrecisionContent = EditorGUIUtility.TrTextContent("Directional Shadow Precision", "Select the shadow map bit depth, this forces HDRP to use selected bit depth for shadow maps.");
public static readonly GUIContent precisionContent = EditorGUIUtility.TrTextContent("Precision", "Select the shadow map bit depth, this forces HDRP to use selected bit depth for shadow maps.");
public static readonly GUIContent dynamicRescaleContent = EditorGUIUtility.TrTextContent("Dynamic Rescale", "When enabled, scales the shadow map size using the screen size of the Light to leave more space for other shadows in the atlas.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,15 @@ static void Drawer_SectionShadows(SerializedHDRenderPipelineAsset serialized, Ed
serialized.renderPipelineSettings.hdShadowInitParams.shadowResolutionPunctual.ValueGUI<int>(Styles.punctualLightsShadowTiers);
EditorGUILayout.DelayedIntField(serialized.renderPipelineSettings.hdShadowInitParams.maxPunctualShadowMapResolution, Styles.maxShadowResolution);
--EditorGUI.indentLevel;

++EditorGUI.indentLevel;
// Because we don't know if the asset is old and had the cached shadow map resolution field, if it was set as default float (0) we force a default.
if (serialized.renderPipelineSettings.hdShadowInitParams.cachedPunctualShadowAtlasResolution.intValue == 0)
{
serialized.renderPipelineSettings.hdShadowInitParams.cachedPunctualShadowAtlasResolution.intValue = 2048;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we do that we will allocate more memory for title, even if they don't use cached shadow.
I am wondering if we should have a default of 0 (so no allocation) and a message on light that say that atlas is 0 if someone use onEnable, or a warning somewhere in the editor when we try to allocate a cached shadow and it doesn't fit in the atlas?

I am a bit worryed by all the wasted memory we have with all the atlas that we have: )

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doing this would break all existing projects using the cached shadows though :(

I guess a message on the light can be done, but then people need to go on the light to see the issue.

We cannot put anything in log without it being a spam and I thought we were not allow to spam warnings every frame.

}
CoreEditorUtils.DrawEnumPopup(serialized.renderPipelineSettings.hdShadowInitParams.cachedPunctualShadowAtlasResolution, typeof(ShadowResolutionValue), Styles.cachedShadowAtlasResolution);
--EditorGUI.indentLevel;
}

m_ShowAreaLightSection = EditorGUILayout.Foldout(m_ShowAreaLightSection, Styles.areaShadowsSubTitle, true);
Expand All @@ -405,6 +414,15 @@ static void Drawer_SectionShadows(SerializedHDRenderPipelineAsset serialized, Ed
serialized.renderPipelineSettings.hdShadowInitParams.shadowResolutionArea.ValueGUI<int>(Styles.areaLightsShadowTiers);
EditorGUILayout.DelayedIntField(serialized.renderPipelineSettings.hdShadowInitParams.maxAreaShadowMapResolution, Styles.maxShadowResolution);
--EditorGUI.indentLevel;

++EditorGUI.indentLevel;
if (serialized.renderPipelineSettings.hdShadowInitParams.cachedAreaShadowAtlasResolution.intValue == 0)
{
serialized.renderPipelineSettings.hdShadowInitParams.cachedAreaShadowAtlasResolution.intValue = 1024;
}
CoreEditorUtils.DrawEnumPopup(serialized.renderPipelineSettings.hdShadowInitParams.cachedAreaShadowAtlasResolution, typeof(ShadowResolutionValue), Styles.cachedShadowAtlasResolution);
--EditorGUI.indentLevel;

}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ class SerializedHDShadowInitParameters
public SerializedProperty maxScreenSpaceShadowSlots;
public SerializedProperty screenSpaceShadowBufferFormat;

public SerializedProperty cachedPunctualShadowAtlasResolution;
public SerializedProperty cachedAreaShadowAtlasResolution;

public SerializedHDShadowInitParameters(SerializedProperty root)
{
this.root = root;
Expand All @@ -57,6 +60,9 @@ public SerializedHDShadowInitParameters(SerializedProperty root)
maxPunctualShadowMapResolution = root.Find((HDShadowInitParameters s) => s.maxPunctualShadowMapResolution);
maxAreaShadowMapResolution = root.Find((HDShadowInitParameters s) => s.maxAreaShadowMapResolution);

cachedPunctualShadowAtlasResolution = root.Find((HDShadowInitParameters s) => s.cachedPunctualLightShadowAtlas);
cachedAreaShadowAtlasResolution = root.Find((HDShadowInitParameters s) => s.cachedAreaLightShadowAtlas);

shadowFilteringQuality = root.Find((HDShadowInitParameters s) => s.shadowFilteringQuality);
supportScreenSpaceShadows = root.Find((HDShadowInitParameters s) => s.supportScreenSpaceShadows);
maxScreenSpaceShadowSlots = root.Find((HDShadowInitParameters s) => s.maxScreenSpaceShadowSlots);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -862,7 +862,9 @@ void RegisterLightingDebug()

shadows.children.Add(new DebugUI.FloatField { displayName = "Range Minimum Value", getter = () => data.lightingDebugSettings.shadowMinValue, setter = value => data.lightingDebugSettings.shadowMinValue = value });
shadows.children.Add(new DebugUI.FloatField { displayName = "Range Maximum Value", getter = () => data.lightingDebugSettings.shadowMaxValue, setter = value => data.lightingDebugSettings.shadowMaxValue = value });

#if UNITY_EDITOR
shadows.children.Add(new DebugUI.Button { displayName = "Log Cached Shadow Atlas Status", action = () => HDCachedShadowManager.instance.PrintLightStatusInCachedAtlas() });
#endif
list.Add(shadows);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,10 @@ public enum ShadowMapDebugMode
VisualizeDirectionalLightAtlas,
/// <summary>Display area lights shadow atlas as an overlay.</summary>
VisualizeAreaLightAtlas,
/// <summary>Display punctual lights cached shadow atlas as an overlay.</summary>
VisualizeCachedPunctualLightAtlas,
/// <summary>Display area lights cached shadow atlas as an overlay.</summary>
VisualizeCachedAreaLightAtlas,
/// <summary>Display a single light shadow map as an overlay.</summary>
VisualizeShadowMap,
/// <summary>Replace rendering with a black and white view of the shadow of a single light in the scene.</summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,10 @@
#define SHADOWMAPDEBUGMODE_VISUALIZE_PUNCTUAL_LIGHT_ATLAS (1)
#define SHADOWMAPDEBUGMODE_VISUALIZE_DIRECTIONAL_LIGHT_ATLAS (2)
#define SHADOWMAPDEBUGMODE_VISUALIZE_AREA_LIGHT_ATLAS (3)
#define SHADOWMAPDEBUGMODE_VISUALIZE_SHADOW_MAP (4)
#define SHADOWMAPDEBUGMODE_SINGLE_SHADOW (5)
#define SHADOWMAPDEBUGMODE_VISUALIZE_CACHED_PUNCTUAL_LIGHT_ATLAS (4)
#define SHADOWMAPDEBUGMODE_VISUALIZE_CACHED_AREA_LIGHT_ATLAS (5)
#define SHADOWMAPDEBUGMODE_VISUALIZE_SHADOW_MAP (6)
#define SHADOWMAPDEBUGMODE_SINGLE_SHADOW (7)

//
// UnityEngine.Rendering.HighDefinition.ExposureDebugMode: static fields
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,11 @@ public HDLightType type
{
if (type != value)
{
if (m_ShadowUpdateMode != ShadowUpdateMode.EveryFrame)
{
HDShadowManager.cachedShadowManager.EvictLight(this);
}

switch (value)
{
case HDLightType.Directional:
Expand All @@ -256,6 +261,11 @@ public HDLightType type
break;
}

if (legacyLight.shadows != LightShadows.None && m_ShadowUpdateMode != ShadowUpdateMode.EveryFrame)
{
HDShadowManager.cachedShadowManager.RegisterLight(this);
}

// If the current light unit is not supported by the new light type, we change it
var supportedUnits = GetSupportedLightUnits(value, m_SpotLightShape);
if (!supportedUnits.Any(u => u == lightUnit))
Expand Down
Loading