diff --git a/TestProjects/HDRP_RuntimeTests/Assets/ReferenceImages/Linear/WindowsPlayer/Direct3D11/None/001-HDTemplate.png b/TestProjects/HDRP_RuntimeTests/Assets/ReferenceImages/Linear/WindowsPlayer/Direct3D11/None/001-HDTemplate.png
index 37f27610b2d..fbba1292d2d 100644
--- a/TestProjects/HDRP_RuntimeTests/Assets/ReferenceImages/Linear/WindowsPlayer/Direct3D11/None/001-HDTemplate.png
+++ b/TestProjects/HDRP_RuntimeTests/Assets/ReferenceImages/Linear/WindowsPlayer/Direct3D11/None/001-HDTemplate.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e2383be9287d59324957fc6d49d10a15806c4e660b2eec41495634e85a6c46aa
-size 2297615
+oid sha256:f29de1a928d4458e70387f8675326de37b6110797c1f870aff560c2a68ffd797
+size 2169620
diff --git a/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/WindowsEditor/Direct3D11/None/4052_TAA.png b/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/WindowsEditor/Direct3D11/None/4052_TAA.png
index 4534ab5eb74..7059218d5cd 100644
--- a/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/WindowsEditor/Direct3D11/None/4052_TAA.png
+++ b/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/WindowsEditor/Direct3D11/None/4052_TAA.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:1d43ff69730e73cf5f93d14ce3a093740ee5839a8a152c89dee4e9c1c06fafe7
-size 194711
+oid sha256:a01c7037e175b53757f0817eab662a6e4ed7b8b8cac307ee8d3d06f7addd9949
+size 194892
diff --git a/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/WindowsEditor/Direct3D12/None/4052_TAA.png b/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/WindowsEditor/Direct3D12/None/4052_TAA.png
index 4dbe4daad35..5fa20859a0c 100644
--- a/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/WindowsEditor/Direct3D12/None/4052_TAA.png
+++ b/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/WindowsEditor/Direct3D12/None/4052_TAA.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:c812051d824a98920aff0d715b1f64e2065948e4d2e0d164cb3f29183ec276c1
-size 195032
+oid sha256:51e3555a5236cf85846b898fd6a883a3eaa8e4580eb01497c6175fb66e2a1db2
+size 195045
diff --git a/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/WindowsEditor/Vulkan/None/4052_TAA.png b/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/WindowsEditor/Vulkan/None/4052_TAA.png
index 5ad4ba31e42..c7629f436b4 100644
--- a/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/WindowsEditor/Vulkan/None/4052_TAA.png
+++ b/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/WindowsEditor/Vulkan/None/4052_TAA.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:da78f222b1a08ccc6d7fea0a4ccba9f647b7ef7ee8e3f9d3680c5ffa8b0071bd
-size 219643
+oid sha256:4d56bec0dd2d98438565872b0510350fb0dc71c47cafcc6b7d01941158a5c876
+size 219775
diff --git a/TestProjects/VisualEffectGraph/Assets/AllTests/VFXTests/GraphicsTests/ParticleRibbonLit/ParticleRibbonLitVolumeProfile.asset b/TestProjects/VisualEffectGraph/Assets/AllTests/VFXTests/GraphicsTests/ParticleRibbonLit/ParticleRibbonLitVolumeProfile.asset
index e4a87301421..f7c31296f6d 100644
--- a/TestProjects/VisualEffectGraph/Assets/AllTests/VFXTests/GraphicsTests/ParticleRibbonLit/ParticleRibbonLitVolumeProfile.asset
+++ b/TestProjects/VisualEffectGraph/Assets/AllTests/VFXTests/GraphicsTests/ParticleRibbonLit/ParticleRibbonLitVolumeProfile.asset
@@ -30,7 +30,7 @@ MonoBehaviour:
m_AdvancedMode: 0
mode:
m_OverrideState: 1
- m_Value: 1
+ m_Value: 0
meteringMode:
m_OverrideState: 0
m_Value: 2
@@ -38,8 +38,8 @@ MonoBehaviour:
m_OverrideState: 0
m_Value: 1
fixedExposure:
- m_OverrideState: 0
- m_Value: 0
+ m_OverrideState: 1
+ m_Value: 7
compensation:
m_OverrideState: 0
m_Value: 0
@@ -86,3 +86,14 @@ MonoBehaviour:
m_OverrideState: 0
m_Value: 1
min: 0.001
+ weightTextureMask:
+ m_OverrideState: 0
+ m_Value: {fileID: 0}
+ histogramPercentages:
+ m_OverrideState: 0
+ m_Value: {x: 40, y: 90}
+ min: 0
+ max: 100
+ histogramUseCurveRemapping:
+ m_OverrideState: 0
+ m_Value: 0
diff --git a/TestProjects/VisualEffectGraph/Assets/ReferenceImages/Linear/WindowsEditor/Direct3D11/None/ParticleRibbonLit.png b/TestProjects/VisualEffectGraph/Assets/ReferenceImages/Linear/WindowsEditor/Direct3D11/None/ParticleRibbonLit.png
index b6f278818f9..bd0370622ae 100644
--- a/TestProjects/VisualEffectGraph/Assets/ReferenceImages/Linear/WindowsEditor/Direct3D11/None/ParticleRibbonLit.png
+++ b/TestProjects/VisualEffectGraph/Assets/ReferenceImages/Linear/WindowsEditor/Direct3D11/None/ParticleRibbonLit.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:a18582acac1cebbc30c724cc7b00a811b6ebdeab3b93f63fdf6624f6b5e12ce5
-size 190375
+oid sha256:c4bab7a0647a60ef231e1e97d05c1afe6b7f454e2197bac183af8e9146790d74
+size 189763
diff --git a/TestProjects/VisualEffectGraph/Assets/ReferenceImages/Linear/WindowsPlayer/Direct3D11/None/ParticleRibbonLit.png b/TestProjects/VisualEffectGraph/Assets/ReferenceImages/Linear/WindowsPlayer/Direct3D11/None/ParticleRibbonLit.png
index b6f278818f9..bd0370622ae 100644
--- a/TestProjects/VisualEffectGraph/Assets/ReferenceImages/Linear/WindowsPlayer/Direct3D11/None/ParticleRibbonLit.png
+++ b/TestProjects/VisualEffectGraph/Assets/ReferenceImages/Linear/WindowsPlayer/Direct3D11/None/ParticleRibbonLit.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:a18582acac1cebbc30c724cc7b00a811b6ebdeab3b93f63fdf6624f6b5e12ce5
-size 190375
+oid sha256:c4bab7a0647a60ef231e1e97d05c1afe6b7f454e2197bac183af8e9146790d74
+size 189763
diff --git a/TestProjects/VisualEffectGraph/Packages/manifest.json b/TestProjects/VisualEffectGraph/Packages/manifest.json
index e5de5c4b889..8ba7ace601c 100644
--- a/TestProjects/VisualEffectGraph/Packages/manifest.json
+++ b/TestProjects/VisualEffectGraph/Packages/manifest.json
@@ -15,7 +15,6 @@
"com.unity.timeline": "1.2.9",
"com.unity.ugui": "1.0.0",
"com.unity.visualeffectgraph": "file:../../../com.unity.visualeffectgraph",
- "com.unity.xr.legacyinputhelpers": "2.1.2",
"com.unity.modules.ai": "1.0.0",
"com.unity.modules.androidjni": "1.0.0",
"com.unity.modules.animation": "1.0.0",
diff --git a/com.unity.render-pipelines.core/Runtime/Debugging/ProfilingScope.cs b/com.unity.render-pipelines.core/Runtime/Debugging/ProfilingScope.cs
index e0e17821416..1a7176e509e 100644
--- a/com.unity.render-pipelines.core/Runtime/Debugging/ProfilingScope.cs
+++ b/com.unity.render-pipelines.core/Runtime/Debugging/ProfilingScope.cs
@@ -98,6 +98,42 @@ public ProfilingSampler(string name)
#endif
}
+ ///
+ /// Begin the profiling block.
+ ///
+ /// Command buffer used by the profiling block.
+ public void Begin(CommandBuffer cmd)
+ {
+ if (cmd != null)
+#if UNITY_USE_RECORDER
+ if (sampler != null)
+ cmd.BeginSample(sampler);
+ else
+ cmd.BeginSample(name);
+#else
+ cmd.BeginSample(name);
+#endif
+ inlineSampler?.Begin();
+ }
+
+ ///
+ /// End the profiling block.
+ ///
+ /// Command buffer used by the profiling block.
+ public void End(CommandBuffer cmd)
+ {
+ if (cmd != null)
+#if UNITY_USE_RECORDER
+ if (sampler != null)
+ cmd.EndSample(sampler);
+ else
+ cmd.EndSample(name);
+#else
+ m_Cmd.EndSample(name);
+#endif
+ inlineSampler?.End();
+ }
+
internal bool IsValid() { return (sampler != null && inlineSampler != null); }
internal CustomSampler sampler { get; private set; }
@@ -187,11 +223,9 @@ public bool enableRecording
///
public struct ProfilingScope : IDisposable
{
- string m_Name;
- CommandBuffer m_Cmd;
- bool m_Disposed;
- CustomSampler m_Sampler;
- CustomSampler m_InlineSampler;
+ CommandBuffer m_Cmd;
+ bool m_Disposed;
+ ProfilingSampler m_Sampler;
///
/// Profiling Scope constructor
@@ -202,29 +236,8 @@ public ProfilingScope(CommandBuffer cmd, ProfilingSampler sampler)
{
m_Cmd = cmd;
m_Disposed = false;
- if (sampler != null)
- {
- m_Name = sampler.name; // Don't use CustomSampler.name because it causes garbage
- m_Sampler = sampler.sampler;
- m_InlineSampler = sampler.inlineSampler;
- }
- else
- {
- m_Name = "NullProfilingSampler"; // Don't use CustomSampler.name because it causes garbage
- m_Sampler = null;
- m_InlineSampler = null;
- }
-
- if (cmd != null)
-#if UNITY_USE_RECORDER
- if (m_Sampler != null)
- cmd.BeginSample(m_Sampler);
- else
- cmd.BeginSample(m_Name);
-#else
- cmd.BeginSample(m_Name);
-#endif
- m_InlineSampler?.Begin();
+ m_Sampler = sampler;
+ m_Sampler?.Begin(m_Cmd);
}
///
@@ -246,16 +259,7 @@ void Dispose(bool disposing)
// this but will generate garbage on every frame (and this struct is used quite a lot).
if (disposing)
{
- if (m_Cmd != null)
-#if UNITY_USE_RECORDER
- if (m_Sampler != null)
- m_Cmd.EndSample(m_Sampler);
- else
- m_Cmd.EndSample(m_Name);
-#else
- m_Cmd.EndSample(m_Name);
-#endif
- m_InlineSampler?.End();
+ m_Sampler?.End(m_Cmd);
}
m_Disposed = true;
diff --git a/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraph.cs b/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraph.cs
index 700251d73a0..8cd7f991fbc 100644
--- a/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraph.cs
+++ b/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraph.cs
@@ -1,8 +1,6 @@
using System;
-using System.Diagnostics;
using System.Collections.Generic;
using UnityEngine.Rendering;
-using UnityEngine.Profiling;
namespace UnityEngine.Experimental.Rendering.RenderGraphModule
{
@@ -95,110 +93,81 @@ public class RenderGraph
///Maximum number of MRTs supported by Render Graph.
public static readonly int kMaxMRTCount = 8;
- [DebuggerDisplay("RenderPass ({name})")]
- internal abstract class RenderPass
- {
- internal RenderFunc GetExecuteDelegate()
- where PassData : class, new() => ((RenderPass)this).renderFunc;
-
- internal abstract void Execute(RenderGraphContext renderGraphContext);
- internal abstract void Release(RenderGraphContext renderGraphContext);
- internal abstract bool HasRenderFunc();
-
- internal string name;
- internal int index;
- internal ProfilingSampler customSampler;
- internal List textureReadList = new List();
- internal List textureWriteList = new List();
- internal List bufferReadList = new List();
- internal List bufferWriteList = new List();
- internal List usedRendererListList = new List();
- internal bool enableAsyncCompute;
- internal TextureHandle depthBuffer { get { return m_DepthBuffer; } }
- internal TextureHandle[] colorBuffers { get { return m_ColorBuffers; } }
- internal int colorBufferMaxIndex { get { return m_MaxColorBufferIndex; } }
-
- protected TextureHandle[] m_ColorBuffers = new TextureHandle[kMaxMRTCount];
- protected TextureHandle m_DepthBuffer;
- protected int m_MaxColorBufferIndex = -1;
-
- internal void Clear()
- {
- name = "";
- index = -1;
- customSampler = null;
- textureReadList.Clear();
- textureWriteList.Clear();
- bufferReadList.Clear();
- bufferWriteList.Clear();
- usedRendererListList.Clear();
- enableAsyncCompute = false;
-
- // Invalidate everything
- m_MaxColorBufferIndex = -1;
- m_DepthBuffer = new TextureHandle();
- for (int i = 0; i < RenderGraph.kMaxMRTCount; ++i)
- {
- m_ColorBuffers[i] = new TextureHandle();
- }
- }
-
- internal void SetColorBuffer(TextureHandle resource, int index)
- {
- Debug.Assert(index < RenderGraph.kMaxMRTCount && index >= 0);
- m_MaxColorBufferIndex = Math.Max(m_MaxColorBufferIndex, index);
- m_ColorBuffers[index] = resource;
- textureWriteList.Add(resource);
- }
+ internal struct CompiledResourceInfo
+ {
+ public List producers;
+ public bool resourceCreated;
+ public int lastReadPassIndex;
+ public int firstWritePassIndex;
+ public int refCount;
- internal void SetDepthBuffer(TextureHandle resource, DepthAccess flags)
+ public void Reset()
{
- m_DepthBuffer = resource;
- if ((flags | DepthAccess.Read) != 0)
- textureReadList.Add(resource);
- if ((flags | DepthAccess.Write) != 0)
- textureWriteList.Add(resource);
-
+ if (producers == null)
+ producers = new List();
+
+ producers.Clear();
+ resourceCreated = false;
+ lastReadPassIndex = -1;
+ firstWritePassIndex = int.MaxValue;
+ refCount = 0;
}
}
- internal sealed class RenderPass : RenderPass
- where PassData : class, new()
+ internal struct CompiledPassInfo
{
- internal PassData data;
- internal RenderFunc renderFunc;
-
- internal override void Execute(RenderGraphContext renderGraphContext)
+ public RenderGraphPass pass;
+ public List textureCreateList;
+ public List textureReleaseList;
+ public int refCount;
+ public bool pruned;
+ public bool hasSideEffect;
+ public int syncToPassIndex; // Index of the pass that needs to be waited for.
+ public int syncFromPassIndex; // Smaller pass index that waits for this pass.
+ public bool needGraphicsFence;
+ public GraphicsFence fence;
+
+ public bool enableAsyncCompute { get { return pass.enableAsyncCompute; } }
+
+ public void Reset(RenderGraphPass pass)
{
- GetExecuteDelegate()(data, renderGraphContext);
- }
+ this.pass = pass;
- internal override void Release(RenderGraphContext renderGraphContext)
- {
- Clear();
- renderGraphContext.renderGraphPool.Release(data);
- data = null;
- renderFunc = null;
- renderGraphContext.renderGraphPool.Release(this);
- }
+ if (textureCreateList == null)
+ {
+ textureCreateList = new List();
+ textureReleaseList = new List();
+ }
- internal override bool HasRenderFunc()
- {
- return renderFunc != null;
+ textureCreateList.Clear();
+ textureReleaseList.Clear();
+ refCount = 0;
+ pruned = false;
+ hasSideEffect = false;
+ syncToPassIndex = -1;
+ syncFromPassIndex = -1;
+ needGraphicsFence = false;
}
}
- RenderGraphResourceRegistry m_Resources;
- RenderGraphObjectPool m_RenderGraphPool = new RenderGraphObjectPool();
- List m_RenderPasses = new List();
- List m_RendererLists = new List();
- RenderGraphDebugParams m_DebugParameters = new RenderGraphDebugParams();
- RenderGraphLogger m_Logger = new RenderGraphLogger();
- RenderGraphDefaultResources m_DefaultResources = new RenderGraphDefaultResources();
+ RenderGraphResourceRegistry m_Resources;
+ RenderGraphObjectPool m_RenderGraphPool = new RenderGraphObjectPool();
+ List m_RenderPasses = new List();
+ List m_RendererLists = new List();
+ RenderGraphDebugParams m_DebugParameters = new RenderGraphDebugParams();
+ RenderGraphLogger m_Logger = new RenderGraphLogger();
+ RenderGraphDefaultResources m_DefaultResources = new RenderGraphDefaultResources();
+ Dictionary m_DefaultProfilingSamplers = new Dictionary();
+
+ // Compiled Render Graph info.
+ DynamicArray m_CompiledTextureInfos = new DynamicArray();
+ DynamicArray m_CompiledBufferInfos = new DynamicArray();
+ DynamicArray m_CompiledPassInfos = new DynamicArray();
+ Stack m_PruningStack = new Stack();
#region Public Interface
- // TODO: Currently only needed by SSAO to sample correctly depth texture mips. Need to figure out a way to hide this behind a proper formalization.
+ // TODO RENDERGRAPH: Currently only needed by SSAO to sample correctly depth texture mips. Need to figure out a way to hide this behind a proper formalization.
///
/// Gets the RTHandleProperties structure associated with the Render Graph's RTHandle System.
///
@@ -347,12 +316,8 @@ public ComputeBufferHandle ImportComputeBuffer(ComputeBuffer computeBuffer)
/// A new instance of a RenderGraphBuilder used to setup the new Render Pass.
public RenderGraphBuilder AddRenderPass(string passName, out PassData passData, ProfilingSampler sampler = null) where PassData : class, new()
{
- var renderPass = m_RenderGraphPool.Get>();
- renderPass.Clear();
- renderPass.index = m_RenderPasses.Count;
- renderPass.data = m_RenderGraphPool.Get();
- renderPass.name = passName;
- renderPass.customSampler = sampler;
+ var renderPass = m_RenderGraphPool.Get>();
+ renderPass.Initialize(m_RenderPasses.Count, m_RenderGraphPool.Get(), passName, sampler != null ? sampler : GetDefaultProfilingSampler(passName));
passData = renderPass.data;
@@ -369,88 +334,363 @@ public ComputeBufferHandle ImportComputeBuffer(ComputeBuffer computeBuffer)
/// Render Graph execution parameters.
public void Execute(ScriptableRenderContext renderContext, CommandBuffer cmd, in RenderGraphExecuteParams parameters)
{
- m_Logger.Initialize();
+ try
+ {
+ m_Logger.Initialize();
- // Update RTHandleSystem with size for this rendering pass.
- m_Resources.SetRTHandleReferenceSize(parameters.renderingWidth, parameters.renderingHeight, parameters.msaaSamples);
+ // Update RTHandleSystem with size for this rendering pass.
+ m_Resources.SetRTHandleReferenceSize(parameters.renderingWidth, parameters.renderingHeight, parameters.msaaSamples);
- LogFrameInformation(parameters.renderingWidth, parameters.renderingHeight);
+ LogFrameInformation(parameters.renderingWidth, parameters.renderingHeight);
- // First pass, traversal and pruning
- for (int passIndex = 0; passIndex < m_RenderPasses.Count; ++passIndex)
+ CompileRenderGraph();
+ ExecuteRenderGraph(renderContext, cmd);
+ }
+ catch (Exception e)
{
- var pass = m_RenderPasses[passIndex];
+ Debug.LogError("Render Graph Execution error");
+ Debug.LogException(e);
+ }
+ finally
+ {
+ ClearCompiledGraph();
- // TODO: Pruning
+ if (m_DebugParameters.logFrameInformation || m_DebugParameters.logResources)
+ Debug.Log(m_Logger.GetLog());
- // Gather all renderer lists
- m_RendererLists.AddRange(pass.usedRendererListList);
+ m_DebugParameters.logFrameInformation = false;
+ m_DebugParameters.logResources = false;
}
+ }
+ #endregion
- // Creates all renderer lists
- m_Resources.CreateRendererLists(m_RendererLists);
- LogRendererListsCreation();
+ #region Internal Interface
+ private RenderGraph()
+ {
- // Second pass, execution
- RenderGraphContext rgContext = new RenderGraphContext();
- rgContext.cmd = cmd;
- rgContext.renderContext = renderContext;
- rgContext.renderGraphPool = m_RenderGraphPool;
- rgContext.resources = m_Resources;
- rgContext.defaultResources = m_DefaultResources;
+ }
- try
+ void ClearCompiledGraph()
+ {
+ ClearRenderPasses();
+ m_Resources.Clear();
+ m_DefaultResources.Clear();
+ m_RendererLists.Clear();
+ m_CompiledBufferInfos.Clear();
+ m_CompiledTextureInfos.Clear();
+ m_CompiledPassInfos.Clear();
+ }
+
+ void InitializeCompilationData()
+ {
+ m_CompiledBufferInfos.Resize(m_Resources.GetComputeBufferResourceCount());
+ for (int i = 0; i < m_CompiledBufferInfos.size; ++i)
+ m_CompiledBufferInfos[i].Reset();
+ m_CompiledTextureInfos.Resize(m_Resources.GetTextureResourceCount());
+ for (int i = 0; i < m_CompiledTextureInfos.size; ++i)
+ m_CompiledTextureInfos[i].Reset();
+ m_CompiledPassInfos.Resize(m_RenderPasses.Count);
+ for (int i = 0; i < m_CompiledPassInfos.size; ++i)
+ m_CompiledPassInfos[i].Reset(m_RenderPasses[i]);
+ }
+
+ void CountReferences()
+ {
+ for (int passIndex = 0; passIndex < m_CompiledPassInfos.size; ++passIndex)
{
- for (int passIndex = 0; passIndex < m_RenderPasses.Count; ++passIndex)
+ ref CompiledPassInfo passInfo = ref m_CompiledPassInfos[passIndex];
+
+ var textureRead = passInfo.pass.textureReadList;
+ foreach (TextureHandle texture in textureRead)
+ {
+ ref CompiledResourceInfo info = ref m_CompiledTextureInfos[texture];
+ info.refCount++;
+ }
+
+ var textureWrite = passInfo.pass.textureWriteList;
+ foreach (TextureHandle texture in textureWrite)
+ {
+ ref CompiledResourceInfo info = ref m_CompiledTextureInfos[texture];
+ info.producers.Add(passIndex);
+ passInfo.refCount++;
+
+ // Writing to an imported texture is considered as a side effect because we don't know what users will do with it outside of render graph.
+ if (m_Resources.IsTextureImported(texture))
+ passInfo.hasSideEffect = true;
+ }
+
+ // Can't share the code with a generic func as TextureHandle and ComputeBufferHandle are both struct and can't inherit from a common struct with a shared API (thanks C#)
+ var bufferRead = passInfo.pass.bufferReadList;
+ foreach (ComputeBufferHandle buffer in bufferRead)
+ {
+ ref CompiledResourceInfo info = ref m_CompiledBufferInfos[buffer];
+ info.refCount++;
+ }
+
+ var bufferWrite = passInfo.pass.bufferWriteList;
+ foreach (ComputeBufferHandle buffer in bufferWrite)
+ {
+ ref CompiledResourceInfo info = ref m_CompiledBufferInfos[buffer];
+ passInfo.refCount++;
+ }
+ }
+ }
+
+ void PruneUnusedPasses(DynamicArray resourceUsageList)
+ {
+ // First gather resources that are never read.
+ m_PruningStack.Clear();
+ for (int i = 0; i < resourceUsageList.size; ++i)
+ {
+ if (resourceUsageList[i].refCount == 0)
{
- var pass = m_RenderPasses[passIndex];
+ m_PruningStack.Push(i);
+ }
+ }
- if (!pass.HasRenderFunc())
+ while (m_PruningStack.Count != 0)
+ {
+ var unusedResource = resourceUsageList[m_PruningStack.Pop()];
+ foreach (var producerIndex in unusedResource.producers)
+ {
+ ref var producerInfo = ref m_CompiledPassInfos[producerIndex];
+ producerInfo.refCount--;
+ if (producerInfo.refCount == 0 && !producerInfo.hasSideEffect)
{
- throw new InvalidOperationException(string.Format("RenderPass {0} was not provided with an execute function.", pass.name));
+ // Producer is not necessary anymore as it produces zero resources
+ // Prune it and decrement refCount of all the textures it reads.
+ producerInfo.pruned = true;
+ foreach (var textureIndex in producerInfo.pass.textureReadList)
+ {
+ ref CompiledResourceInfo resourceInfo = ref resourceUsageList[textureIndex];
+ resourceInfo.refCount--;
+ // If a texture is not used anymore, add it to the stack to be processed in subsequent iteration.
+ if (resourceInfo.refCount == 0)
+ m_PruningStack.Push(textureIndex);
+ }
}
+ }
+ }
+ }
+
+ void PruneUnusedPasses()
+ {
+ PruneUnusedPasses(m_CompiledTextureInfos);
+ PruneUnusedPasses(m_CompiledBufferInfos);
+ LogPrunedPasses();
+ }
+
+ int GetLatestProducerIndex(int passIndex, in CompiledResourceInfo info)
+ {
+ // We want to know the highest pass index below the current pass that writes to the resource.
+ int result = -1;
+ foreach (var producer in info.producers)
+ {
+ // producers are by construction in increasing order.
+ if (producer < passIndex)
+ result = producer;
+ else
+ return result;
+ }
+
+ return result;
+ }
+
+
+ void UpdatePassSynchronization(ref CompiledPassInfo currentPassInfo, ref CompiledPassInfo producerPassInfo, int currentPassIndex, int lastProducer, ref int intLastSyncIndex)
+ {
+ // Current pass needs to wait for pass index lastProducer
+ currentPassInfo.syncToPassIndex = lastProducer;
+ // Update latest pass waiting for the other pipe.
+ intLastSyncIndex = lastProducer;
+
+ // Producer will need a graphics fence that this pass will wait on.
+ producerPassInfo.needGraphicsFence = true;
+ // We update the producer pass with the index of the smallest pass waiting for it.
+ // This will be used to "lock" resource from being reused until the pipe has been synchronized.
+ if (producerPassInfo.syncFromPassIndex == -1)
+ producerPassInfo.syncFromPassIndex = currentPassIndex;
+ }
- using (new ProfilingScope(cmd, pass.customSampler))
+ void UpdateResourceSynchronization(ref int lastGraphicsPipeSync, ref int lastComputePipeSync, int currentPassIndex, in CompiledResourceInfo resource)
+ {
+ int lastProducer = GetLatestProducerIndex(currentPassIndex, resource);
+ if (lastProducer != -1)
+ {
+ ref CompiledPassInfo currentPassInfo = ref m_CompiledPassInfos[currentPassIndex];
+
+ //If the passes are on different pipes, we need synchronization.
+ if (m_CompiledPassInfos[lastProducer].enableAsyncCompute != currentPassInfo.enableAsyncCompute)
+ {
+ // Pass is on compute pipe, need sync with graphics pipe.
+ if (currentPassInfo.enableAsyncCompute)
{
- LogRenderPassBegin(pass);
- using (new RenderGraphLogIndent(m_Logger))
+ if (lastProducer > lastGraphicsPipeSync)
{
- PreRenderPassExecute(passIndex, pass, rgContext);
- pass.Execute(rgContext);
- PostRenderPassExecute(passIndex, pass, rgContext);
+ UpdatePassSynchronization(ref currentPassInfo, ref m_CompiledPassInfos[lastProducer], currentPassIndex, lastProducer, ref lastGraphicsPipeSync);
+ }
+ }
+ else
+ {
+ if (lastProducer > lastComputePipeSync)
+ {
+ UpdatePassSynchronization(ref currentPassInfo, ref m_CompiledPassInfos[lastProducer], currentPassIndex, lastProducer, ref lastComputePipeSync);
}
}
}
}
- catch(Exception e)
+ }
+
+ void UpdateResourceAllocationAndSynchronization()
+ {
+ int lastGraphicsPipeSync = -1;
+ int lastComputePipeSync = -1;
+
+ // First go through all passes.
+ // - Update the last pass read index for each resource.
+ // - Add texture to creation list for passes that first write to a texture.
+ // - Update synchronization points for all resources between compute and graphics pipes.
+ for (int passIndex = 0; passIndex < m_CompiledPassInfos.size; ++passIndex)
{
- Debug.LogError("Render Graph Execution error");
- Debug.LogException(e);
+ ref CompiledPassInfo passInfo = ref m_CompiledPassInfos[passIndex];
+
+ if (passInfo.pruned)
+ continue;
+
+ foreach (TextureHandle texture in passInfo.pass.textureReadList)
+ {
+ ref CompiledResourceInfo resourceInfo = ref m_CompiledTextureInfos[texture];
+ resourceInfo.lastReadPassIndex = Math.Max(resourceInfo.lastReadPassIndex, passIndex);
+ UpdateResourceSynchronization(ref lastGraphicsPipeSync, ref lastComputePipeSync, passIndex, resourceInfo);
+ }
+
+ foreach (TextureHandle texture in passInfo.pass.textureWriteList)
+ {
+ ref CompiledResourceInfo resourceInfo = ref m_CompiledTextureInfos[texture];
+ if (resourceInfo.firstWritePassIndex == int.MaxValue)
+ {
+ resourceInfo.firstWritePassIndex = Math.Min(resourceInfo.firstWritePassIndex, passIndex);
+ passInfo.textureCreateList.Add(texture);
+ }
+ passInfo.refCount++;
+ UpdateResourceSynchronization(ref lastGraphicsPipeSync, ref lastComputePipeSync, passIndex, resourceInfo);
+ }
+
+ foreach (ComputeBufferHandle texture in passInfo.pass.bufferReadList)
+ {
+ UpdateResourceSynchronization(ref lastGraphicsPipeSync, ref lastComputePipeSync, passIndex, m_CompiledBufferInfos[texture]);
+ }
+ foreach (ComputeBufferHandle texture in passInfo.pass.bufferWriteList)
+ {
+ UpdateResourceSynchronization(ref lastGraphicsPipeSync, ref lastComputePipeSync, passIndex, m_CompiledBufferInfos[texture]);
+ }
+
+ // Add transient resources that are only used during this pass.
+ foreach (TextureHandle texture in passInfo.pass.transientTextureList)
+ {
+ passInfo.textureCreateList.Add(texture);
+
+ ref CompiledResourceInfo info = ref m_CompiledTextureInfos[texture];
+ info.lastReadPassIndex = passIndex;
+ }
+
+ // Gather all renderer lists
+ m_RendererLists.AddRange(passInfo.pass.usedRendererListList);
}
- finally
+
+ // Now push textures to the release list of the pass that reads it last.
+ for (int i = 0; i < m_CompiledTextureInfos.size; ++i)
{
- ClearRenderPasses();
- m_Resources.Clear();
- m_DefaultResources.Clear();
- m_RendererLists.Clear();
+ CompiledResourceInfo textureInfo = m_CompiledTextureInfos[i];
+ if (textureInfo.lastReadPassIndex != -1)
+ {
+ // In case of async passes, we need to extend lifetime of resource to the first pass on the graphics pipeline that wait for async passes to be over.
+ // Otherwise, if we freed the resource right away during an async pass, another non async pass could reuse the resource even though the async pipe is not done.
+ if (m_CompiledPassInfos[textureInfo.lastReadPassIndex].enableAsyncCompute)
+ {
+ int currentPassIndex = textureInfo.lastReadPassIndex;
+ int firstWaitingPassIndex = m_CompiledPassInfos[currentPassIndex].syncFromPassIndex;
+ // Find the first async pass that is synchronized by the graphics pipeline (ie: passInfo.syncFromPassIndex != -1)
+ while (firstWaitingPassIndex == -1 && currentPassIndex < m_CompiledPassInfos.size)
+ {
+ currentPassIndex++;
+ if(m_CompiledPassInfos[currentPassIndex].enableAsyncCompute)
+ firstWaitingPassIndex = m_CompiledPassInfos[currentPassIndex].syncFromPassIndex;
+ }
- if (m_DebugParameters.logFrameInformation || m_DebugParameters.logResources)
- Debug.Log(m_Logger.GetLog());
+ // Finally add the release command to the pass before the first pass that waits for the compute pipe.
+ ref CompiledPassInfo passInfo = ref m_CompiledPassInfos[Math.Max(0, firstWaitingPassIndex - 1)];
+ passInfo.textureReleaseList.Add(new TextureHandle(i));
- m_DebugParameters.logFrameInformation = false;
- m_DebugParameters.logResources = false;
+ // Fail safe in case render graph is badly formed.
+ if (currentPassIndex == m_CompiledPassInfos.size)
+ {
+ RenderGraphPass invalidPass = m_RenderPasses[textureInfo.lastReadPassIndex];
+ throw new InvalidOperationException($"Asynchronous pass {invalidPass.name} was never synchronized on the graphics pipeline.");
+ }
+ }
+ else
+ {
+ ref CompiledPassInfo passInfo = ref m_CompiledPassInfos[textureInfo.lastReadPassIndex];
+ passInfo.textureReleaseList.Add(new TextureHandle(i));
+ }
+ }
}
+
+ // Creates all renderer lists
+ m_Resources.CreateRendererLists(m_RendererLists);
}
- #endregion
- #region Internal Interface
- private RenderGraph()
+ // Traverse the render graph:
+ // - Determines when resources are created/released
+ // - Determines async compute pass synchronization
+ // - Prune unused render passes.
+ void CompileRenderGraph()
{
+ InitializeCompilationData();
+ CountReferences();
+ PruneUnusedPasses();
+ UpdateResourceAllocationAndSynchronization();
+ LogRendererListsCreation();
+ }
+
+ // Execute the compiled render graph
+ void ExecuteRenderGraph(ScriptableRenderContext renderContext, CommandBuffer cmd)
+ {
+ RenderGraphContext rgContext = new RenderGraphContext();
+ rgContext.cmd = cmd;
+ rgContext.renderContext = renderContext;
+ rgContext.renderGraphPool = m_RenderGraphPool;
+ rgContext.resources = m_Resources;
+ rgContext.defaultResources = m_DefaultResources;
+ for (int passIndex = 0; passIndex < m_CompiledPassInfos.size; ++passIndex)
+ {
+ ref var passInfo = ref m_CompiledPassInfos[passIndex];
+ if (passInfo.pruned)
+ continue;
+
+ if (!passInfo.pass.HasRenderFunc())
+ {
+ throw new InvalidOperationException(string.Format("RenderPass {0} was not provided with an execute function.", passInfo.pass.name));
+ }
+
+ using (new ProfilingScope(rgContext.cmd, passInfo.pass.customSampler))
+ {
+ LogRenderPassBegin(passInfo);
+ using (new RenderGraphLogIndent(m_Logger))
+ {
+ PreRenderPassExecute(passInfo, ref rgContext);
+ passInfo.pass.Execute(rgContext);
+ PostRenderPassExecute(cmd, ref passInfo, ref rgContext);
+ }
+ }
+ }
}
- void PreRenderPassSetRenderTargets(in RenderPass pass, RenderGraphContext rgContext)
+ void PreRenderPassSetRenderTargets(in CompiledPassInfo passInfo, RenderGraphContext rgContext)
{
+ var pass = passInfo.pass;
if (pass.depthBuffer.IsValid() || pass.colorBufferMaxIndex != -1)
{
var mrtArray = rgContext.renderGraphPool.GetTempArray(pass.colorBufferMaxIndex + 1);
@@ -492,21 +732,61 @@ void PreRenderPassSetRenderTargets(in RenderPass pass, RenderGraphContext rgCont
}
}
- void PreRenderPassExecute(int passIndex, in RenderPass pass, RenderGraphContext rgContext)
+ void PreRenderPassExecute(in CompiledPassInfo passInfo, ref RenderGraphContext rgContext)
{
- // TODO merge clear and setup here if possible
- m_Resources.CreateAndClearTexturesForPass(rgContext, pass.index, pass.textureWriteList);
- PreRenderPassSetRenderTargets(pass, rgContext);
+ // TODO RENDERGRAPH merge clear and setup here if possible
+ RenderGraphPass pass = passInfo.pass;
+
+ // TODO RENDERGRAPH remove this when we do away with auto global texture setup
+ // (can't put it in the profiling scope otherwise it might be executed on compute queue which is not possible for global sets)
m_Resources.PreRenderPassSetGlobalTextures(rgContext, pass.textureReadList);
+
+ foreach (var texture in passInfo.textureCreateList)
+ m_Resources.CreateAndClearTexture(rgContext, texture);
+
+ PreRenderPassSetRenderTargets(passInfo, rgContext);
+
+ // Flush first the current command buffer on the render context.
+ rgContext.renderContext.ExecuteCommandBuffer(rgContext.cmd);
+ rgContext.cmd.Clear();
+
+ if (pass.enableAsyncCompute)
+ {
+ CommandBuffer asyncCmd = CommandBufferPool.Get(pass.name);
+ asyncCmd.SetExecutionFlags(CommandBufferExecutionFlags.AsyncCompute);
+ rgContext.cmd = asyncCmd;
+ }
+
+ // Synchronize with graphics or compute pipe if needed.
+ if (passInfo.syncToPassIndex != -1)
+ {
+ rgContext.cmd.WaitOnAsyncGraphicsFence(m_CompiledPassInfos[passInfo.syncToPassIndex].fence);
+ }
}
- void PostRenderPassExecute(int passIndex, in RenderPass pass, RenderGraphContext rgContext)
+ void PostRenderPassExecute(CommandBuffer mainCmd, ref CompiledPassInfo passInfo, ref RenderGraphContext rgContext)
{
+ RenderGraphPass pass = passInfo.pass;
+
+ if (passInfo.needGraphicsFence)
+ passInfo.fence = rgContext.cmd.CreateAsyncGraphicsFence();
+
+ if (pass.enableAsyncCompute)
+ {
+ // The command buffer has been filled. We can kick the async task.
+ rgContext.renderContext.ExecuteCommandBufferAsync(rgContext.cmd, ComputeQueueType.Background);
+ CommandBufferPool.Release(rgContext.cmd);
+ rgContext.cmd = mainCmd; // Restore the main command buffer.
+ }
+
if (m_DebugParameters.unbindGlobalTextures)
m_Resources.PostRenderPassUnbindGlobalTextures(rgContext, pass.textureReadList);
m_RenderGraphPool.ReleaseAllTempAlloc();
- m_Resources.ReleaseTexturesForPass(rgContext, pass.index, pass.textureReadList, pass.textureWriteList);
+
+ foreach (var texture in passInfo.textureReleaseList)
+ m_Resources.ReleaseTexture(rgContext, texture);
+
pass.Release(rgContext);
}
@@ -520,7 +800,7 @@ void LogFrameInformation(int renderingWidth, int renderingHeight)
if (m_DebugParameters.logFrameInformation)
{
m_Logger.LogLine("==== Staring frame at resolution ({0}x{1}) ====", renderingWidth, renderingHeight);
- m_Logger.LogLine("Number of passes declared: {0}", m_RenderPasses.Count);
+ m_Logger.LogLine("Number of passes declared: {0}\n", m_RenderPasses.Count);
}
}
@@ -528,18 +808,57 @@ void LogRendererListsCreation()
{
if (m_DebugParameters.logFrameInformation)
{
- m_Logger.LogLine("Number of renderer lists created: {0}", m_RendererLists.Count);
+ m_Logger.LogLine("Number of renderer lists created: {0}\n", m_RendererLists.Count);
+ }
+ }
+
+ void LogRenderPassBegin(in CompiledPassInfo passInfo)
+ {
+ if (m_DebugParameters.logFrameInformation)
+ {
+ RenderGraphPass pass = passInfo.pass;
+
+ m_Logger.LogLine("[{0}][{1}] \"{2}\"", pass.index, pass.enableAsyncCompute ? "Compute" : "Graphics", pass.name);
+ using (new RenderGraphLogIndent(m_Logger))
+ {
+ if (passInfo.syncToPassIndex != -1)
+ m_Logger.LogLine("Synchronize with [{0}]", passInfo.syncToPassIndex);
+ }
}
}
- void LogRenderPassBegin(in RenderPass pass)
+ void LogPrunedPasses()
{
if (m_DebugParameters.logFrameInformation)
{
- m_Logger.LogLine("Executing pass \"{0}\" (index: {1})", pass.name, pass.index);
+ m_Logger.LogLine("Pass pruning report:");
+ using (new RenderGraphLogIndent(m_Logger))
+ {
+ for (int i = 0; i < m_CompiledPassInfos.size; ++i)
+ {
+ if (m_CompiledPassInfos[i].pruned)
+ {
+ var pass = m_RenderPasses[i];
+ m_Logger.LogLine("[{0}] {1}", pass.index, pass.name);
+ }
+ }
+ m_Logger.LogLine("\n");
+ }
}
}
+ ProfilingSampler GetDefaultProfilingSampler(string name)
+ {
+ int hash = name.GetHashCode();
+ if (!m_DefaultProfilingSamplers.TryGetValue(hash, out var sampler))
+ {
+ sampler = new ProfilingSampler(name);
+ m_DefaultProfilingSamplers.Add(hash, sampler);
+ }
+
+ return sampler;
+ }
+
#endregion
}
}
diff --git a/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphBuilder.cs b/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphBuilder.cs
index b100ba50abc..404565f4297 100644
--- a/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphBuilder.cs
+++ b/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphBuilder.cs
@@ -8,7 +8,7 @@ namespace UnityEngine.Experimental.Rendering.RenderGraphModule
///
public struct RenderGraphBuilder : IDisposable
{
- RenderGraph.RenderPass m_RenderPass;
+ RenderGraphPass m_RenderPass;
RenderGraphResourceRegistry m_Resources;
bool m_Disposed;
@@ -22,8 +22,9 @@ public struct RenderGraphBuilder : IDisposable
/// An updated resource handle to the input resource.
public TextureHandle UseColorBuffer(TextureHandle input, int index)
{
+ CheckTransientTexture(input);
+
m_RenderPass.SetColorBuffer(input, index);
- m_Resources.UpdateTextureFirstWrite(input, m_RenderPass.index);
return input;
}
@@ -35,11 +36,9 @@ public TextureHandle UseColorBuffer(TextureHandle input, int index)
/// An updated resource handle to the input resource.
public TextureHandle UseDepthBuffer(TextureHandle input, DepthAccess flags)
{
+ CheckTransientTexture(input);
+
m_RenderPass.SetDepthBuffer(input, flags);
- if ((flags | DepthAccess.Read) != 0)
- m_Resources.UpdateTextureLastRead(input, m_RenderPass.index);
- if ((flags | DepthAccess.Write) != 0)
- m_Resources.UpdateTextureFirstWrite(input, m_RenderPass.index);
return input;
}
@@ -50,8 +49,9 @@ public TextureHandle UseDepthBuffer(TextureHandle input, DepthAccess flags)
/// An updated resource handle to the input resource.
public TextureHandle ReadTexture(TextureHandle input)
{
- m_RenderPass.textureReadList.Add(input);
- m_Resources.UpdateTextureLastRead(input, m_RenderPass.index);
+ CheckTransientTexture(input);
+
+ m_RenderPass.AddTextureRead(input);
return input;
}
@@ -62,12 +62,39 @@ public TextureHandle ReadTexture(TextureHandle input)
/// An updated resource handle to the input resource.
public TextureHandle WriteTexture(TextureHandle input)
{
+ CheckTransientTexture(input);
+
// TODO: Manage resource "version" for debugging purpose
- m_RenderPass.textureWriteList.Add(input);
- m_Resources.UpdateTextureFirstWrite(input, m_RenderPass.index);
+ m_RenderPass.AddTextureWrite(input);
return input;
}
+ ///
+ /// Create a new Render Graph Texture resource.
+ /// This texture will only be available for the current pass and will be assumed to be both written and read so users don't need to add explicit read/write declarations.
+ ///
+ /// Texture descriptor.
+ /// A new transient TextureHandle.
+ public TextureHandle CreateTransientTexture(in TextureDesc desc)
+ {
+ var result = m_Resources.CreateTexture(desc, 0, m_RenderPass.index);
+ m_RenderPass.AddTransientTexture(result);
+ return result;
+ }
+
+ ///
+ /// Create a new Render Graph Texture resource using the descriptor from another texture.
+ ///
+ /// Texture from which the descriptor should be used.
+ /// A new transient TextureHandle.
+ public TextureHandle CreateTransientTexture(TextureHandle texture)
+ {
+ var desc = m_Resources.GetTextureResourceDesc(texture);
+ var result = m_Resources.CreateTexture(desc, 0, m_RenderPass.index);
+ m_RenderPass.AddTransientTexture(result);
+ return result;
+ }
+
///
/// Specify a Renderer List resource to use during the pass.
///
@@ -75,7 +102,7 @@ public TextureHandle WriteTexture(TextureHandle input)
/// An updated resource handle to the input resource.
public RendererListHandle UseRendererList(RendererListHandle input)
{
- m_RenderPass.usedRendererListList.Add(input);
+ m_RenderPass.UseRendererList(input);
return input;
}
@@ -86,7 +113,7 @@ public RendererListHandle UseRendererList(RendererListHandle input)
/// An updated resource handle to the input resource.
public ComputeBufferHandle ReadComputeBuffer(ComputeBufferHandle input)
{
- m_RenderPass.bufferReadList.Add(input);
+ m_RenderPass.AddBufferRead(input);
return input;
}
@@ -97,7 +124,7 @@ public ComputeBufferHandle ReadComputeBuffer(ComputeBufferHandle input)
/// An updated resource handle to the input resource.
public ComputeBufferHandle WriteComputeBuffer(ComputeBufferHandle input)
{
- m_RenderPass.bufferWriteList.Add(input);
+ m_RenderPass.AddBufferWrite(input);
return input;
}
@@ -109,7 +136,7 @@ public ComputeBufferHandle WriteComputeBuffer(ComputeBufferHandle input)
/// Render function for the pass.
public void SetRenderFunc(RenderFunc renderFunc) where PassData : class, new()
{
- ((RenderGraph.RenderPass)m_RenderPass).renderFunc = renderFunc;
+ ((RenderGraphPass)m_RenderPass).renderFunc = renderFunc;
}
///
@@ -118,7 +145,7 @@ public ComputeBufferHandle WriteComputeBuffer(ComputeBufferHandle input)
/// Set to true to enable asynchronous compute.
public void EnableAsyncCompute(bool value)
{
- m_RenderPass.enableAsyncCompute = value;
+ m_RenderPass.EnableAsyncCompute(value);
}
///
@@ -131,7 +158,7 @@ public void Dispose()
#endregion
#region Internal Interface
- internal RenderGraphBuilder(RenderGraph.RenderPass renderPass, RenderGraphResourceRegistry resources)
+ internal RenderGraphBuilder(RenderGraphPass renderPass, RenderGraphResourceRegistry resources)
{
m_RenderPass = renderPass;
m_Resources = resources;
@@ -145,6 +172,14 @@ void Dispose(bool disposing)
m_Disposed = true;
}
+
+ void CheckTransientTexture(TextureHandle input)
+ {
+ if (input.transientPassIndex != -1 && input.transientPassIndex != m_RenderPass.index)
+ {
+ throw new ArgumentException($"Trying to use a transient texture (pass index {input.transientPassIndex}) in a different pass (pass index {m_RenderPass.index}.");
+ }
+ }
#endregion
}
}
diff --git a/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphLogger.cs b/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphLogger.cs
index b41e1fcfe29..df946c91743 100644
--- a/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphLogger.cs
+++ b/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphLogger.cs
@@ -25,10 +25,12 @@ public void Dispose()
void Dispose(bool disposing)
{
+ Debug.Assert(m_Logger != null, "RenderGraphLogIndent: logger parameter should not be null.");
+
if (m_Disposed)
return;
- if (disposing)
+ if (disposing && m_Logger != null)
{
m_Logger.DecrementIndentation(m_Indentation);
}
diff --git a/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphPass.cs b/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphPass.cs
new file mode 100644
index 00000000000..7a3c5b5ca67
--- /dev/null
+++ b/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphPass.cs
@@ -0,0 +1,155 @@
+using System;
+using System.Diagnostics;
+using System.Collections.Generic;
+using UnityEngine.Rendering;
+
+namespace UnityEngine.Experimental.Rendering.RenderGraphModule
+{
+ [DebuggerDisplay("RenderPass: {name} (Index:{index} Async:{enableAsyncCompute})")]
+ abstract class RenderGraphPass
+ {
+ public RenderFunc GetExecuteDelegate()
+ where PassData : class, new() => ((RenderGraphPass)this).renderFunc;
+
+ public abstract void Execute(RenderGraphContext renderGraphContext);
+ public abstract void Release(RenderGraphContext renderGraphContext);
+ public abstract bool HasRenderFunc();
+
+ public string name { get; protected set; }
+ public int index { get; protected set; }
+ public ProfilingSampler customSampler { get; protected set; }
+ public bool enableAsyncCompute { get; protected set; }
+
+ public TextureHandle depthBuffer { get; protected set; }
+ public TextureHandle[] colorBuffers { get; protected set; } = new TextureHandle[RenderGraph.kMaxMRTCount];
+ public int colorBufferMaxIndex { get; protected set; } = -1;
+ public int refCount { get; protected set; }
+
+ List m_TextureReadList = new List();
+ List m_TextureWriteList = new List();
+ List m_TransientTextureList = new List();
+ List m_BufferReadList = new List();
+ List m_BufferWriteList = new List();
+ List m_UsedRendererListList = new List();
+
+ public IReadOnlyCollection textureReadList { get { return m_TextureReadList; } }
+ public IReadOnlyCollection textureWriteList { get { return m_TextureWriteList; } }
+ public IReadOnlyCollection transientTextureList { get { return m_TransientTextureList; } }
+ public IReadOnlyCollection bufferReadList { get { return m_BufferReadList; } }
+ public IReadOnlyCollection bufferWriteList { get { return m_BufferWriteList; } }
+ public IReadOnlyCollection usedRendererListList { get { return m_UsedRendererListList; } }
+
+ public void Clear()
+ {
+ name = "";
+ index = -1;
+ customSampler = null;
+ m_TextureReadList.Clear();
+ m_TextureWriteList.Clear();
+ m_BufferReadList.Clear();
+ m_BufferWriteList.Clear();
+ m_TransientTextureList.Clear();
+ m_UsedRendererListList.Clear();
+ enableAsyncCompute = false;
+ refCount = 0;
+
+ // Invalidate everything
+ colorBufferMaxIndex = -1;
+ depthBuffer = new TextureHandle();
+ for (int i = 0; i < RenderGraph.kMaxMRTCount; ++i)
+ {
+ colorBuffers[i] = new TextureHandle();
+ }
+ }
+
+ public void AddTextureWrite(TextureHandle texture)
+ {
+ m_TextureWriteList.Add(texture);
+ refCount++;
+ }
+
+ public void AddTextureRead(TextureHandle texture)
+ {
+ m_TextureReadList.Add(texture);
+ }
+
+ public void AddBufferWrite(ComputeBufferHandle buffer)
+ {
+ m_BufferWriteList.Add(buffer);
+ refCount++;
+ }
+
+ public void AddTransientTexture(TextureHandle texture)
+ {
+ m_TransientTextureList.Add(texture);
+ }
+
+ public void AddBufferRead(ComputeBufferHandle buffer)
+ {
+ m_BufferReadList.Add(buffer);
+ }
+
+ public void UseRendererList(RendererListHandle rendererList)
+ {
+ m_UsedRendererListList.Add(rendererList);
+ }
+
+ public void EnableAsyncCompute(bool value)
+ {
+ enableAsyncCompute = value;
+ }
+
+ public void SetColorBuffer(TextureHandle resource, int index)
+ {
+ Debug.Assert(index < RenderGraph.kMaxMRTCount && index >= 0);
+ colorBufferMaxIndex = Math.Max(colorBufferMaxIndex, index);
+ colorBuffers[index] = resource;
+ AddTextureWrite(resource);
+ }
+
+ public void SetDepthBuffer(TextureHandle resource, DepthAccess flags)
+ {
+ depthBuffer = resource;
+ if ((flags | DepthAccess.Read) != 0)
+ AddTextureRead(resource);
+ if ((flags | DepthAccess.Write) != 0)
+ AddTextureWrite(resource);
+ }
+ }
+
+ [DebuggerDisplay("RenderPass: {name} (Index:{index} Async:{enableAsyncCompute})")]
+ internal sealed class RenderGraphPass : RenderGraphPass
+ where PassData : class, new()
+ {
+ internal PassData data;
+ internal RenderFunc renderFunc;
+
+ public override void Execute(RenderGraphContext renderGraphContext)
+ {
+ GetExecuteDelegate()(data, renderGraphContext);
+ }
+
+ public void Initialize(int passIndex, PassData passData, string passName, ProfilingSampler sampler)
+ {
+ Clear();
+ index = passIndex;
+ data = passData;
+ name = passName;
+ customSampler = sampler;
+ }
+
+ public override void Release(RenderGraphContext renderGraphContext)
+ {
+ Clear();
+ renderGraphContext.renderGraphPool.Release(data);
+ data = null;
+ renderFunc = null;
+ renderGraphContext.renderGraphPool.Release(this);
+ }
+
+ public override bool HasRenderFunc()
+ {
+ return renderFunc != null;
+ }
+ }
+}
diff --git a/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphPass.cs.meta b/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphPass.cs.meta
new file mode 100644
index 00000000000..9f00928f05e
--- /dev/null
+++ b/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphPass.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 377a2b96156b1344eaf58df67e35de17
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphResourceRegistry.cs b/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphResourceRegistry.cs
index 3b8a66efc34..c186e92c47f 100644
--- a/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphResourceRegistry.cs
+++ b/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphResourceRegistry.cs
@@ -18,7 +18,8 @@ public struct TextureHandle
{
bool m_IsValid;
internal int handle { get; private set; }
- internal TextureHandle(int handle) { this.handle = handle; m_IsValid = true; }
+ internal int transientPassIndex { get; private set; }
+ internal TextureHandle(int handle, int transientPassIndex = -1) { this.handle = handle; m_IsValid = true; this.transientPassIndex = transientPassIndex; }
///
/// Conversion to int.
///
@@ -30,6 +31,7 @@ public struct TextureHandle
///
/// True if the handle is valid.
public bool IsValid() => m_IsValid;
+
}
///
@@ -292,8 +294,6 @@ internal struct TextureResource
public bool imported;
public RTHandle rt;
public int cachedHash;
- public int firstWritePassIndex;
- public int lastReadPassIndex;
public int shaderProperty;
public bool wasReleased;
@@ -321,8 +321,6 @@ void Reset()
imported = false;
rt = null;
cachedHash = -1;
- firstWritePassIndex = int.MaxValue;
- lastReadPassIndex = -1;
wasReleased = false;
}
}
@@ -441,6 +439,11 @@ internal TextureHandle ImportTexture(RTHandle rt, int shaderProperty = 0)
return new TextureHandle(newHandle);
}
+ internal bool IsTextureImported(TextureHandle handle)
+ {
+ return handle.IsValid() ? GetTextureResource(handle).imported : false;
+ }
+
internal TextureHandle ImportBackbuffer(RenderTargetIdentifier rt)
{
if (m_CurrentBackbuffer != null)
@@ -452,36 +455,17 @@ internal TextureHandle ImportBackbuffer(RenderTargetIdentifier rt)
return new TextureHandle(newHandle);
}
- internal TextureHandle CreateTexture(in TextureDesc desc, int shaderProperty = 0)
+ internal TextureHandle CreateTexture(in TextureDesc desc, int shaderProperty = 0, int transientPassIndex = -1)
{
ValidateTextureDesc(desc);
int newHandle = m_TextureResources.Add(new TextureResource(desc, shaderProperty));
- return new TextureHandle(newHandle);
- }
-
- internal void UpdateTextureFirstWrite(TextureHandle tex, int passIndex)
- {
- ref var res = ref GetTextureResource(tex);
- res.firstWritePassIndex = Math.Min(passIndex, res.firstWritePassIndex);
-
- //// We increment lastRead index here so that a resource used only for a single pass can be released at the end of said pass.
- //// This will also keep the resource alive as long as it is written to.
- //// Typical example is a depth buffer that may never be explicitly read from but is necessary all along
- ///
- // PROBLEM: Increasing last read on write operation will keep the target alive even if it's not used at all so it's not good.
- // If we don't do it though, it means that client code cannot write "by default" into a target as it will try to write to an already released target.
- // Example:
- // DepthPrepass: Writes to Depth and Normal buffers (pass will create normal buffer)
- // ObjectMotion: Writes to MotionVectors and Normal => Exception because NormalBuffer is already released as it not used.
- // => Solution includes : Shader Combination (without MRT for example) / Dummy Targets
- //res.lastReadPassIndex = Math.Max(passIndex, res.lastReadPassIndex);
+ return new TextureHandle(newHandle, transientPassIndex);
}
- internal void UpdateTextureLastRead(TextureHandle tex, int passIndex)
+ internal int GetTextureResourceCount()
{
- ref var res = ref GetTextureResource(tex);
- res.lastReadPassIndex = Math.Max(passIndex, res.lastReadPassIndex);
+ return m_TextureResources.size;
}
ref TextureResource GetTextureResource(TextureHandle res)
@@ -508,29 +492,36 @@ internal ComputeBufferHandle ImportComputeBuffer(ComputeBuffer computeBuffer)
return new ComputeBufferHandle(newHandle);
}
- internal void CreateAndClearTexturesForPass(RenderGraphContext rgContext, int passIndex, List textures)
+ internal int GetComputeBufferResourceCount()
+ {
+ return m_ComputeBufferResources.size;
+ }
+
+ internal ref ComputeBufferResource GetComputeBufferResource(ComputeBufferHandle res)
{
- foreach (var rgResource in textures)
+ return ref m_ComputeBufferResources[res];
+ }
+
+ internal void CreateAndClearTexture(RenderGraphContext rgContext, TextureHandle texture)
+ {
+ ref var resource = ref GetTextureResource(texture);
+ if (!resource.imported)
{
- ref var resource = ref GetTextureResource(rgResource);
- if (!resource.imported && resource.firstWritePassIndex == passIndex)
- {
- CreateTextureForPass(ref resource);
+ CreateTextureForPass(ref resource);
- if (resource.desc.clearBuffer || m_RenderGraphDebug.clearRenderTargetsAtCreation)
+ if (resource.desc.clearBuffer || m_RenderGraphDebug.clearRenderTargetsAtCreation)
+ {
+ bool debugClear = m_RenderGraphDebug.clearRenderTargetsAtCreation && !resource.desc.clearBuffer;
+ var name = debugClear ? "RenderGraph: Clear Buffer (Debug)" : "RenderGraph: Clear Buffer";
+ using (new ProfilingScope(rgContext.cmd, ProfilingSampler.Get(RenderGraphProfileId.RenderGraphClear)))
{
- bool debugClear = m_RenderGraphDebug.clearRenderTargetsAtCreation && !resource.desc.clearBuffer;
- var name = debugClear ? "RenderGraph: Clear Buffer (Debug)" : "RenderGraph: Clear Buffer";
- using (new ProfilingScope(rgContext.cmd, ProfilingSampler.Get(RenderGraphProfileId.RenderGraphClear)))
- {
- var clearFlag = resource.desc.depthBufferBits != DepthBits.None ? ClearFlag.Depth : ClearFlag.Color;
- var clearColor = debugClear ? Color.magenta : resource.desc.clearColor;
- CoreUtils.SetRenderTarget(rgContext.cmd, resource.rt, clearFlag, clearColor);
- }
+ var clearFlag = resource.desc.depthBufferBits != DepthBits.None ? ClearFlag.Depth : ClearFlag.Color;
+ var clearColor = debugClear ? Color.magenta : resource.desc.clearColor;
+ CoreUtils.SetRenderTarget(rgContext.cmd, resource.rt, clearFlag, clearColor);
}
-
- LogTextureCreation(resource.rt, resource.desc.clearBuffer || m_RenderGraphDebug.clearRenderTargetsAtCreation);
}
+
+ LogTextureCreation(resource.rt, resource.desc.clearBuffer || m_RenderGraphDebug.clearRenderTargetsAtCreation);
}
}
@@ -540,7 +531,7 @@ void CreateTextureForPass(ref TextureResource resource)
int hashCode = desc.GetHashCode();
if(resource.rt != null)
- throw new InvalidOperationException(string.Format("Trying to create an already created texture ({0}). Texture was probably declared for writing more than once.", resource.desc.name));
+ throw new InvalidOperationException(string.Format("Trying to create an already created texture ({0}). Texture was probably declared for writing more than once in the same pass.", resource.desc.name));
resource.rt = null;
if (!TryGetRenderTarget(hashCode, out resource.rt))
@@ -578,7 +569,7 @@ void CreateTextureForPass(ref TextureResource resource)
resource.cachedHash = hashCode;
}
- void SetGlobalTextures(RenderGraphContext rgContext, List textures, bool bindDummyTexture)
+ void SetGlobalTextures(RenderGraphContext rgContext, IReadOnlyCollection textures, bool bindDummyTexture)
{
foreach (var resource in textures)
{
@@ -595,62 +586,35 @@ void SetGlobalTextures(RenderGraphContext rgContext, List texture
}
- internal void PreRenderPassSetGlobalTextures(RenderGraphContext rgContext, List textures)
+ internal void PreRenderPassSetGlobalTextures(RenderGraphContext rgContext, IReadOnlyCollection textures)
{
SetGlobalTextures(rgContext, textures, false);
}
- internal void PostRenderPassUnbindGlobalTextures(RenderGraphContext rgContext, List textures)
+ internal void PostRenderPassUnbindGlobalTextures(RenderGraphContext rgContext, IReadOnlyCollection textures)
{
SetGlobalTextures(rgContext, textures, true);
}
- internal void ReleaseTexturesForPass(RenderGraphContext rgContext, int passIndex, List readTextures, List writtenTextures)
+ internal void ReleaseTexture(RenderGraphContext rgContext, TextureHandle resource)
{
- foreach (var resource in readTextures)
+ ref var resourceDesc = ref GetTextureResource(resource);
+ if (!resourceDesc.imported)
{
- ref var resourceDesc = ref GetTextureResource(resource);
- if (!resourceDesc.imported && resourceDesc.lastReadPassIndex == passIndex)
+ if (m_RenderGraphDebug.clearRenderTargetsAtRelease)
{
- if (m_RenderGraphDebug.clearRenderTargetsAtRelease)
+ using (new ProfilingScope(rgContext.cmd, ProfilingSampler.Get(RenderGraphProfileId.RenderGraphClearDebug)))
{
- using (new ProfilingScope(rgContext.cmd, ProfilingSampler.Get(RenderGraphProfileId.RenderGraphClearDebug)))
- {
- var clearFlag = resourceDesc.desc.depthBufferBits != DepthBits.None ? ClearFlag.Depth : ClearFlag.Color;
- CoreUtils.SetRenderTarget(rgContext.cmd, GetTexture(resource), clearFlag, Color.magenta);
- }
+ var clearFlag = resourceDesc.desc.depthBufferBits != DepthBits.None ? ClearFlag.Depth : ClearFlag.Color;
+ CoreUtils.SetRenderTarget(rgContext.cmd, GetTexture(resource), clearFlag, Color.magenta);
}
-
- ReleaseTextureForPass(resource);
- }
- }
-
- // If a resource was created for only a single pass, we don't want users to have to declare explicitly the read operation.
- // So to do that, we also update lastReadIndex on resource writes.
- // This means that we need to check written resources for destruction too
- foreach (var resource in writtenTextures)
- {
- ref var resourceDesc = ref GetTextureResource(resource);
- // <= because a texture that is only declared as written in a single pass (and read implicitly in the same pass) will have the default lastReadPassIndex at -1
- if (!resourceDesc.imported && resourceDesc.lastReadPassIndex <= passIndex)
- {
- ReleaseTextureForPass(resource);
}
- }
- }
-
- void ReleaseTextureForPass(TextureHandle res)
- {
- ref var resource = ref m_TextureResources[res];
- // This can happen because we release texture in two passes (see ReleaseTexturesForPass) and texture can be present in both passes
- if (resource.rt != null)
- {
- LogTextureRelease(resource.rt);
- ReleaseTextureResource(resource.cachedHash, resource.rt);
- resource.cachedHash = -1;
- resource.rt = null;
- resource.wasReleased = true;
+ LogTextureRelease(resourceDesc.rt);
+ ReleaseTextureResource(resourceDesc.cachedHash, resourceDesc.rt);
+ resourceDesc.cachedHash = -1;
+ resourceDesc.rt = null;
+ resourceDesc.wasReleased = true;
}
}
@@ -761,12 +725,16 @@ internal void Clear()
#if DEVELOPMENT_BUILD || UNITY_EDITOR
if (m_AllocatedTextures.Count != 0)
{
- Debug.LogWarning("RenderGraph: Not all textures were released.");
+ string logMessage = "RenderGraph: Not all textures were released.";
+
List<(int, RTHandle)> tempList = new List<(int, RTHandle)>(m_AllocatedTextures);
foreach (var value in tempList)
{
+ logMessage = $"{logMessage}\n\t{value.Item2.name}";
ReleaseTextureResource(value.Item1, value.Item2);
}
+
+ Debug.LogWarning(logMessage);
}
#endif
}
diff --git a/com.unity.render-pipelines.high-definition/CHANGELOG.md b/com.unity.render-pipelines.high-definition/CHANGELOG.md
index 1dc6efee344..17a49195636 100644
--- a/com.unity.render-pipelines.high-definition/CHANGELOG.md
+++ b/com.unity.render-pipelines.high-definition/CHANGELOG.md
@@ -127,6 +127,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Add a new parameter to control reflections in recursive rendering.
- Added an initial version of SSGI.
- Added CustomPassUtils API to simplify Blur, Copy and DrawRenderers custom passes.
+- Added Histogram guided automatic exposure.
+- Added few exposure debug modes.
### Fixed
- Fix when rescale probe all direction below zero (1219246)
@@ -615,6 +617,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Fixed taaFrameIndex and XR tests 4052 and 4053
- Fixed the prefab integration of custom passes (Prefab Override Highlight not working as expected).
- Cloned volume profile from read only assets are created in the root of the project. (case 1154961)
+- Fixed Wizard check on default volume profile to also check it is not the default one in package.
+- Fix erroneous central depth sampling in TAA.
+- Fixed light layers not correctly disabled when the lightlayers is set to Nothing and Lightlayers isn't enabled in HDRP Asset
### Changed
- Improve MIP selection for decals on Transparents
@@ -743,6 +748,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Ignoring the disable SSR flags for recursive rendering.
- Removed logic in the UI to disable parameters for contact shadows and fog volume components as it was going against the concept of the volume system.
- Fixed the sub surface mask not being taken into account when computing ray traced sub surface scattering.
+- MSAA Within Forward Frame Setting is now enabled by default on Cameras when new Render Pipeline Asset is created
+- 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
diff --git a/com.unity.render-pipelines.high-definition/Documentation~/Decal-Projector.md b/com.unity.render-pipelines.high-definition/Documentation~/Decal-Projector.md
index 37ad65a2972..088a8523a98 100644
--- a/com.unity.render-pipelines.high-definition/Documentation~/Decal-Projector.md
+++ b/com.unity.render-pipelines.high-definition/Documentation~/Decal-Projector.md
@@ -45,4 +45,5 @@ Using the Inspector allows you to change all of the Decal Projector properties,
## Limitations
- If you project a decal onto a transparent surface, HDRP ignores the decal's Texture tiling.
+- When you project a decal onto a surface, the emissive component of the decal affects the surface regardless of whether you enable **Receive Decal** on the surface's Material or not.
- In **Project Settings > Graphics**, if **Instancing Variants** is set to **Strip All**, Unity strips the Decal Shader this component references when you build your Project. This happens even if you include the Shader in the **Always Included Shaders** list. If Unity strips the Shader during the build process, the decal does not appear in your built Application.
diff --git a/com.unity.render-pipelines.high-definition/Documentation~/HDRP-Features.md b/com.unity.render-pipelines.high-definition/Documentation~/HDRP-Features.md
index 73f8f3bbe18..e3c1be83a66 100644
--- a/com.unity.render-pipelines.high-definition/Documentation~/HDRP-Features.md
+++ b/com.unity.render-pipelines.high-definition/Documentation~/HDRP-Features.md
@@ -40,7 +40,6 @@ Use Volumes to localize environmental Scene settings and post-processing effects
HDRP Shaders allow you to use the following features:
- [Opaque/transparent surfaces](Surface-Type.md).
-
- Different blend modes for transparent surfaces.
- Transparent surfaces that work with fog.
- Refraction and distortion for transparent surfaces.
@@ -116,7 +115,7 @@ The Decal Shader allows you to place decals on surfaces in your Scene. To apply

-The Terrain Lit Shader is compatible with the built-in terrain system and supports up to eight layers in a single draw call. This Shader uses the same lighting model as the Lit Shader.
+The Terrain Lit Shader is compatible with the built-in terrain system and supports up to eight layers in a single draw call. This Shader uses the same lighting model as the Lit Shader. For more information, including the full list of Shader properties, see the [Terrain Lit Shader documentation](Terrain-Lit-Shader.md).
diff --git a/com.unity.render-pipelines.high-definition/Documentation~/Images/TerrainLitShader1.png b/com.unity.render-pipelines.high-definition/Documentation~/Images/TerrainLitShader1.png
new file mode 100644
index 00000000000..930b14bca24
--- /dev/null
+++ b/com.unity.render-pipelines.high-definition/Documentation~/Images/TerrainLitShader1.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:33e6168455c6873019999e3206267be8e3f7a93d6ef9e9d9c94c0c13949f2fbf
+size 45539
diff --git a/com.unity.render-pipelines.high-definition/Documentation~/Shadows-in-HDRP.md b/com.unity.render-pipelines.high-definition/Documentation~/Shadows-in-HDRP.md
index d9ccbca8c3c..b80751f1047 100644
--- a/com.unity.render-pipelines.high-definition/Documentation~/Shadows-in-HDRP.md
+++ b/com.unity.render-pipelines.high-definition/Documentation~/Shadows-in-HDRP.md
@@ -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
diff --git a/com.unity.render-pipelines.high-definition/Documentation~/TableOfContents.md b/com.unity.render-pipelines.high-definition/Documentation~/TableOfContents.md
index 3507974c933..3dc362227fe 100644
--- a/com.unity.render-pipelines.high-definition/Documentation~/TableOfContents.md
+++ b/com.unity.render-pipelines.high-definition/Documentation~/TableOfContents.md
@@ -64,6 +64,7 @@
* [Layered Lit Shader](Layered-Lit-Shader)
* [Lit Tessellation Shader](Lit-Tessellation-Shader)
* [Lit Shader](Lit-Shader)
+ * [Terrain Lit Shader](Terrain-Lit-Shader.md)
* [Unlit Shader](Unlit-Shader)
* Shader Graph Master Nodes
* [Customizing Materials Using Master Nodes](Customizing-HDRP-materials-with-Shader-Graph)
diff --git a/com.unity.render-pipelines.high-definition/Documentation~/Terrain-Lit-Shader.md b/com.unity.render-pipelines.high-definition/Documentation~/Terrain-Lit-Shader.md
new file mode 100644
index 00000000000..a2e5d3ac8db
--- /dev/null
+++ b/com.unity.render-pipelines.high-definition/Documentation~/Terrain-Lit-Shader.md
@@ -0,0 +1,46 @@
+# **Terrain Lit Shader**
+
+The High Definition Render Pipeline (HDRP) uses the Terrain Lit Shader for Unity Terrain. This Shader is a simpler version of the [Lit Shader](Lit-Shader.md). A Terrain can use a Terrain Lit Material with up to eight [Terrain Layers](https://docs.unity3d.com/Manual/class-TerrainLayer.html).
+
+
+
+## Creating a Terrain Lit Material
+
+To create a new Terrain Lit Shader Material:
+
+1. Go to your Project window and right-click in the **Assets** folder
+2. Select **Create > Material**. This adds a new Material to your Unity Project’s Asset folder.
+3. Click on the Material to view it in the Inspector.
+4. Click on the **Shader** drop-down and select **HDRP > TerrainLit**.
+
+## Using a Terrain Lit Material
+
+To use a Terrain Lit Material, you must assign it to a Terrain:
+
+1. View the Terrain in the Inspector and click on the cog button to go to the **Terrain Settings** section.
+2. Either drag and drop or use the radio button to assign your Terrain Lit Material to the **Material** property.
+
+
+
+## Material properties
+
+### Surface Options
+
+| **Property** | **Description** |
+| ------------------ | ------------------------------------------------------------ |
+| **Receive Decals** | Enable this checkbox to allow HDRP to draw decals on this Material’s surface. |
+
+### Terrain
+
+| **Property** | **Description** |
+| ----------------------------- | ------------------------------------------------------------ |
+| **Enable Height-based Blend** | Specifies whether HDRP should only render the Terrain Layer with the greatest height value for a particular pixel. When enabled, HDRP takes the height values from the blue channel of the **Mask Map** Texture. When disabled, HDRP blends the Terrain Layers based on the weights painted in the control map Textures. |
+| **- Height Transition** | Controls how much HDRP blends the terrain if multiple Terrain Layers are approximately the same height. |
+| **Enable Per-pixel Normal** | Specifies whether HDRP should sample the normal map Texture on a per-pixel level. When enabled, Unity preserves more geometry details for distant terrain parts. Unity generates a geometry normal map at runtime from the heightmap, rather than the Mesh geometry. This means you can have high-resolution Mesh normals, even if your Mesh is low resolution. It only works if you enable **Draw Instanced** on the terrain. |
+| **Specular Occlusion Mode** | Sets the mode that HDRP uses to calculate specular occlusion.
• **Off**: Disables specular occlusion.
• **From Ambient Occlusion**: Calculates specular occlusion from the ambient occlusion map and the Camera's view direction. |
+
+### Advanced Options
+
+| **Property** | **Description** |
+| ------------------------- | ------------------------------------------------------------ |
+| **Enable GPU Instancing** | Enable this checkbox to tell HDRP to render meshes with the same geometry and Material/Shader in one batch when possible. This makes rendering faster. HDRP can not render Meshes in one batch if they have different Materials, or if the hardware does not support GPU instancing. |
\ No newline at end of file
diff --git a/com.unity.render-pipelines.high-definition/Documentation~/Upgrading-from-2019.3-to-2020.1.md b/com.unity.render-pipelines.high-definition/Documentation~/Upgrading-from-2019.3-to-2020.1.md
index 6cd48c9c77a..aaf04d2c617 100644
--- a/com.unity.render-pipelines.high-definition/Documentation~/Upgrading-from-2019.3-to-2020.1.md
+++ b/com.unity.render-pipelines.high-definition/Documentation~/Upgrading-from-2019.3-to-2020.1.md
@@ -8,4 +8,8 @@ From Unity 2020.1, the HDRP-specific settings of the scene view camera (anti-ali
## Cookie baking
-From Unity 2020.1, Cookie on light are not taken into account for the lightmaps / Lightprobes. This support is always enable with HDRP.
\ No newline at end of file
+From Unity 2020.1, Cookie on light are not taken into account for the lightmaps / Lightprobes. This support is always enable with HDRP.
+
+## Default Volume Profile
+
+From Unity 2020.1, the Default Volume Profile asset has changed so that the Exposure component sets the default Compensation to 0.
\ No newline at end of file
diff --git a/com.unity.render-pipelines.high-definition/Documentation~/Upgrading-from-2020.1-to-2020.2.md b/com.unity.render-pipelines.high-definition/Documentation~/Upgrading-from-2020.1-to-2020.2.md
index 1c53f78fed6..191aa4edc17 100644
--- a/com.unity.render-pipelines.high-definition/Documentation~/Upgrading-from-2020.1-to-2020.2.md
+++ b/com.unity.render-pipelines.high-definition/Documentation~/Upgrading-from-2020.1-to-2020.2.md
@@ -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.
\ No newline at end of file
diff --git a/com.unity.render-pipelines.high-definition/Editor/DefaultScene/HDWizard.Configuration.cs b/com.unity.render-pipelines.high-definition/Editor/DefaultScene/HDWizard.Configuration.cs
index 9c3d3962740..81526d8115c 100644
--- a/com.unity.render-pipelines.high-definition/Editor/DefaultScene/HDWizard.Configuration.cs
+++ b/com.unity.render-pipelines.high-definition/Editor/DefaultScene/HDWizard.Configuration.cs
@@ -460,7 +460,9 @@ bool IsDefaultVolumeProfileAssigned()
return false;
var hdAsset = HDRenderPipeline.currentAsset;
- return hdAsset.defaultVolumeProfile != null && !hdAsset.defaultVolumeProfile.Equals(null);
+ return hdAsset.defaultVolumeProfile != null
+ && !hdAsset.defaultVolumeProfile.Equals(null)
+ && hdAsset.defaultVolumeProfile != hdAsset.renderPipelineEditorResources.defaultSettingsVolumeProfile;
}
void FixDefaultVolumeProfileAssigned(bool fromAsyncUnused)
{
@@ -471,7 +473,19 @@ void FixDefaultVolumeProfileAssigned(bool fromAsyncUnused)
if (hdrpAsset == null)
return;
- EditorDefaultSettings.GetOrAssignDefaultVolumeProfile(hdrpAsset);
+ VolumeProfile defaultSettingsVolumeProfileInPackage = hdrpAsset.renderPipelineEditorResources.defaultSettingsVolumeProfile;
+ string defaultSettingsVolumeProfilePath = "Assets/" + HDProjectSettings.projectSettingsFolderPath + '/' + defaultSettingsVolumeProfileInPackage.name + ".asset";
+
+ //try load one if one already exist
+ VolumeProfile defaultSettingsVolumeProfile = AssetDatabase.LoadAssetAtPath(defaultSettingsVolumeProfilePath);
+ if (defaultSettingsVolumeProfile == null || defaultSettingsVolumeProfile.Equals(null))
+ {
+ //else create it
+ AssetDatabase.CopyAsset(AssetDatabase.GetAssetPath(defaultSettingsVolumeProfileInPackage), defaultSettingsVolumeProfilePath);
+ defaultSettingsVolumeProfile = AssetDatabase.LoadAssetAtPath(defaultSettingsVolumeProfilePath);
+ }
+ hdrpAsset.defaultVolumeProfile = defaultSettingsVolumeProfile;
+
EditorUtility.SetDirty(hdrpAsset);
}
diff --git a/com.unity.render-pipelines.high-definition/Editor/DefaultScene/HDWizard.Window.cs b/com.unity.render-pipelines.high-definition/Editor/DefaultScene/HDWizard.Window.cs
index 614da9582a7..e1f451f0171 100644
--- a/com.unity.render-pipelines.high-definition/Editor/DefaultScene/HDWizard.Window.cs
+++ b/com.unity.render-pipelines.high-definition/Editor/DefaultScene/HDWizard.Window.cs
@@ -108,7 +108,7 @@ public ConfigStyle(string label, string error, string button = resolve, MessageT
error: "Default scene prefab must be set to create HD templated scene!");
public static readonly ConfigStyle hdrpVolumeProfile = new ConfigStyle(
label: "Default volume profile",
- error: "Default volume profile must be assigned in the HDRP asset!");
+ error: "Default volume profile must be assigned in the HDRP asset! Also, for it to be editable, it should be outside of package.");
public static readonly ConfigStyle vrLegacyVRSystem = new ConfigStyle(
label: "Legacy VR System",
diff --git a/com.unity.render-pipelines.high-definition/Editor/Lighting/HDLightUI.cs b/com.unity.render-pipelines.high-definition/Editor/Lighting/HDLightUI.cs
index fe6def1919f..528c612f80b 100644
--- a/com.unity.render-pipelines.high-definition/Editor/Lighting/HDLightUI.cs
+++ b/com.unity.render-pipelines.high-definition/Editor/Lighting/HDLightUI.cs
@@ -756,11 +756,7 @@ static void ShowCookieTextureWarnings(Texture cookie, bool useBaking)
}
}
- #if UNITY_2020_2_OR_NEWER
if (useBaking && !UnityEditor.EditorSettings.enableCookiesInLightmapper)
- #else
- if (useBaking && UnityEditor.EditorSettings.disableCookiesInLightmapper)
- #endif
EditorGUILayout.HelpBox(s_Styles.cookieBaking, MessageType.Warning);
if (cookie.width != cookie.height)
EditorGUILayout.HelpBox(s_Styles.cookieNonPOT, MessageType.Warning);
diff --git a/com.unity.render-pipelines.high-definition/Editor/PostProcessing/ExposureEditor.cs b/com.unity.render-pipelines.high-definition/Editor/PostProcessing/ExposureEditor.cs
index a08652ed5d8..35c7c585bbe 100644
--- a/com.unity.render-pipelines.high-definition/Editor/PostProcessing/ExposureEditor.cs
+++ b/com.unity.render-pipelines.high-definition/Editor/PostProcessing/ExposureEditor.cs
@@ -1,4 +1,5 @@
using UnityEditor.Rendering;
+using UnityEngine;
using UnityEngine.Rendering.HighDefinition;
namespace UnityEditor.Rendering.HighDefinition
@@ -22,6 +23,9 @@ sealed class ExposureEditor : VolumeComponentEditor
SerializedDataParameter m_WeightTextureMask;
+ SerializedDataParameter m_HistogramPercentages;
+ SerializedDataParameter m_HistogramCurveRemapping;
+
public override void OnEnable()
{
var o = new PropertyFetcher(serializedObject);
@@ -41,6 +45,10 @@ public override void OnEnable()
m_AdaptationSpeedLightToDark = Unpack(o.Find(x => x.adaptationSpeedLightToDark));
m_WeightTextureMask = Unpack(o.Find(x => x.weightTextureMask));
+
+ m_HistogramPercentages = Unpack(o.Find(x => x.histogramPercentages));
+ m_HistogramCurveRemapping = Unpack(o.Find(x => x.histogramUseCurveRemapping));
+
}
public override void OnInspectorGUI()
@@ -65,10 +73,11 @@ public override void OnInspectorGUI()
if(m_MeteringMode.value.intValue == (int)MeteringMode.MaskWeighted)
PropertyField(m_WeightTextureMask);
- PropertyField(m_LuminanceSource);
+ // Temporary hiding the field since we don't support anything but color buffer for now.
+ //PropertyField(m_LuminanceSource);
- if (m_LuminanceSource.value.intValue == (int)LuminanceSource.LightingBuffer)
- EditorGUILayout.HelpBox("Luminance source buffer isn't supported yet.", MessageType.Warning);
+ //if (m_LuminanceSource.value.intValue == (int)LuminanceSource.LightingBuffer)
+ // EditorGUILayout.HelpBox("Luminance source buffer isn't supported yet.", MessageType.Warning);
if (mode == (int)ExposureMode.CurveMapping)
PropertyField(m_CurveMap);
@@ -76,7 +85,19 @@ public override void OnInspectorGUI()
PropertyField(m_Compensation);
PropertyField(m_LimitMin);
PropertyField(m_LimitMax);
-
+
+ if(mode == (int)ExposureMode.AutomaticHistogram)
+ {
+ EditorGUILayout.Space();
+ EditorGUILayout.LabelField("Histogram", EditorStyles.miniLabel);
+ PropertyField(m_HistogramPercentages);
+ PropertyField(m_HistogramCurveRemapping, EditorGUIUtility.TrTextContent("Use Curve Remapping"));
+ if (m_HistogramCurveRemapping.value.boolValue)
+ {
+ PropertyField(m_CurveMap);
+ }
+ }
+
EditorGUILayout.Space();
EditorGUILayout.LabelField("Adaptation", EditorStyles.miniLabel);
diff --git a/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/HDRenderPipelineUI.Skin.cs b/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/HDRenderPipelineUI.Skin.cs
index 031d4d40558..bf7f0ccc463 100644
--- a/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/HDRenderPipelineUI.Skin.cs
+++ b/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/HDRenderPipelineUI.Skin.cs
@@ -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.");
diff --git a/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/HDRenderPipelineUI.cs b/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/HDRenderPipelineUI.cs
index 1a35e081067..e7862542a9d 100644
--- a/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/HDRenderPipelineUI.cs
+++ b/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/HDRenderPipelineUI.cs
@@ -383,6 +383,15 @@ static void Drawer_SectionShadows(SerializedHDRenderPipelineAsset serialized, Ed
serialized.renderPipelineSettings.hdShadowInitParams.shadowResolutionPunctual.ValueGUI(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;
+ }
+ CoreEditorUtils.DrawEnumPopup(serialized.renderPipelineSettings.hdShadowInitParams.cachedPunctualShadowAtlasResolution, typeof(ShadowResolutionValue), Styles.cachedShadowAtlasResolution);
+ --EditorGUI.indentLevel;
}
m_ShowAreaLightSection = EditorGUILayout.Foldout(m_ShowAreaLightSection, Styles.areaShadowsSubTitle, true);
@@ -405,6 +414,15 @@ static void Drawer_SectionShadows(SerializedHDRenderPipelineAsset serialized, Ed
serialized.renderPipelineSettings.hdShadowInitParams.shadowResolutionArea.ValueGUI(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;
+
}
}
diff --git a/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/Settings/SerializedHDShadowInitParameters.cs b/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/Settings/SerializedHDShadowInitParameters.cs
index 1e14c79ab8b..64847791ef9 100644
--- a/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/Settings/SerializedHDShadowInitParameters.cs
+++ b/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/Settings/SerializedHDShadowInitParameters.cs
@@ -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;
@@ -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);
diff --git a/com.unity.render-pipelines.high-definition/Editor/RenderPipelineResources/DefaultSettingsVolumeProfile.asset b/com.unity.render-pipelines.high-definition/Editor/RenderPipelineResources/DefaultSettingsVolumeProfile.asset
index a5fe482a2e2..e72cfee66ed 100644
--- a/com.unity.render-pipelines.high-definition/Editor/RenderPipelineResources/DefaultSettingsVolumeProfile.asset
+++ b/com.unity.render-pipelines.high-definition/Editor/RenderPipelineResources/DefaultSettingsVolumeProfile.asset
@@ -87,10 +87,18 @@ MonoBehaviour:
m_OverrideState: 0
m_Value: 50
min: 0
+ minDistance:
+ m_OverrideState: 0
+ m_Value: 0
+ min: 0
fadeDistance:
m_OverrideState: 0
m_Value: 5
min: 0
+ fadeInDistance:
+ m_OverrideState: 0
+ m_Value: 0
+ min: 0
m_SampleCount:
m_OverrideState: 1
m_Value: 12
@@ -322,8 +330,8 @@ MonoBehaviour:
m_OverrideState: 0
m_Value: 0
compensation:
- m_OverrideState: 1
- m_Value: 1
+ m_OverrideState: 0
+ m_Value: 0
limitMin:
m_OverrideState: 0
m_Value: -10
@@ -367,6 +375,9 @@ MonoBehaviour:
m_OverrideState: 0
m_Value: 1
min: 0.001
+ weightTextureMask:
+ m_OverrideState: 0
+ m_Value: {fileID: 0}
--- !u!114 &7502528774814404555
MonoBehaviour:
m_ObjectHideFlags: 3
diff --git a/com.unity.render-pipelines.high-definition/Runtime/Debug/DebugDisplay.cs b/com.unity.render-pipelines.high-definition/Runtime/Debug/DebugDisplay.cs
index 2c960245d8e..629e439c2eb 100644
--- a/com.unity.render-pipelines.high-definition/Runtime/Debug/DebugDisplay.cs
+++ b/com.unity.render-pipelines.high-definition/Runtime/Debug/DebugDisplay.cs
@@ -218,6 +218,7 @@ public class DebugData
internal int renderingFulscreenDebugModeEnumIndex;
internal int terrainTextureEnumIndex;
internal int colorPickerDebugModeEnumIndex;
+ internal int exposureDebugModeEnumIndex;
internal int msaaSampleDebugModeEnumIndex;
internal int debugCameraToFreezeEnumIndex;
internal int volumeComponentEnumIndex;
@@ -407,7 +408,7 @@ public bool IsDebugDisplayEnabled()
/// True if any material debug display is enabled.
public bool IsDebugMaterialDisplayEnabled()
{
- return data.materialDebugSettings.IsDebugDisplayEnabled();
+ return data.materialDebugSettings.IsDebugDisplayEnabled();
}
///
@@ -419,6 +420,15 @@ public bool IsDebugFullScreenEnabled()
return data.fullScreenDebugMode != FullScreenDebugMode.None;
}
+ ///
+ /// Returns true if any full screen exposure debug display is enabled.
+ ///
+ /// True if any full screen exposure debug display is enabled.
+ public bool IsDebugExposureModeEnabled()
+ {
+ return data.lightingDebugSettings.exposureDebugMode != ExposureDebugMode.None;
+ }
+
///
/// Returns true if material validation is enabled.
///
@@ -626,6 +636,15 @@ internal void SetProbeVolumeAtlasSliceMode(ProbeVolumeAtlasSliceMode value)
data.lightingDebugSettings.probeVolumeAtlasSliceMode = value;
}
+ ///
+ /// Set the current Exposure Debug Mode.
+ ///
+ /// Desired Probe Volume Debug Mode.
+ internal void SetExposureDebugMode(ExposureDebugMode value)
+ {
+ data.lightingDebugSettings.exposureDebugMode = value;
+ }
+
///
/// Set the current Mip Map Debug Mode.
///
@@ -843,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);
}
@@ -872,6 +893,44 @@ void RegisterLightingDebug()
}
});
+ var exposureFoldout = new DebugUI.Foldout
+ {
+ displayName = "Exposure ",
+ children =
+ {
+ new DebugUI.EnumField
+ {
+ displayName = "Debug Mode",
+ getter = () => (int) data.lightingDebugSettings.exposureDebugMode,
+ setter = value => SetExposureDebugMode((ExposureDebugMode) value),
+ autoEnum = typeof(ExposureDebugMode), onValueChanged = RefreshLightingDebug,
+ getIndex = () => data.exposureDebugModeEnumIndex,
+ setIndex = value => data.exposureDebugModeEnumIndex = value
+ }
+ }
+ };
+
+ if (data.lightingDebugSettings.exposureDebugMode == ExposureDebugMode.HistogramView)
+ {
+ exposureFoldout.children.Add(
+ new DebugUI.BoolField()
+ {
+ displayName = "Show Tonemap curve",
+ getter = () => data.lightingDebugSettings.showTonemapCurveAlongHistogramView,
+ setter = value => data.lightingDebugSettings.showTonemapCurveAlongHistogramView = value
+ });
+ }
+
+ exposureFoldout.children.Add(
+ new DebugUI.FloatField
+ {
+ displayName = "Debug Exposure Compensation",
+ getter = () => data.lightingDebugSettings.debugExposure,
+ setter = value => data.lightingDebugSettings.debugExposure = value
+ });
+
+ lighting.children.Add(exposureFoldout);
+
lighting.children.Add(new DebugUI.EnumField { displayName = "Debug Mode", getter = () => (int)data.lightingDebugSettings.debugLightingMode, setter = value => SetDebugLightingMode((DebugLightingMode)value), autoEnum = typeof(DebugLightingMode), onValueChanged = RefreshLightingDebug, getIndex = () => data.lightingDebugModeEnumIndex, setIndex = value => { data.ResetExclusiveEnumIndices(); data.lightingDebugModeEnumIndex = value; } });
lighting.children.Add(new DebugUI.BitField { displayName = "Hierarchy Debug Mode", getter = () => data.lightingDebugSettings.debugLightFilterMode, setter = value => SetDebugLightFilterMode((DebugLightFilterMode)value), enumType = typeof(DebugLightFilterMode), onValueChanged = RefreshLightingDebug, });
@@ -1125,8 +1184,6 @@ void RegisterLightingDebug()
list.Add(new DebugUI.FloatField { displayName = "Debug Overlay Screen Ratio", getter = () => data.debugOverlayRatio, setter = v => data.debugOverlayRatio = v, min = () => 0.1f, max = () => 1f});
- list.Add(new DebugUI.FloatField { displayName = "Debug Exposure Compensation", getter = () => data.lightingDebugSettings.debugExposure, setter = value => data.lightingDebugSettings.debugExposure = value });
-
m_DebugLightingItems = list.ToArray();
var panel = DebugManager.instance.GetPanel(k_PanelLighting, true);
panel.children.Add(m_DebugLightingItems);
diff --git a/com.unity.render-pipelines.high-definition/Runtime/Debug/DebugDisplay.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Debug/DebugDisplay.hlsl
index 9b5f009aa19..1ed2109002a 100644
--- a/com.unity.render-pipelines.high-definition/Runtime/Debug/DebugDisplay.hlsl
+++ b/com.unity.render-pipelines.high-definition/Runtime/Debug/DebugDisplay.hlsl
@@ -180,6 +180,7 @@ void DrawInteger(int intValue, float3 fontColor, uint2 currentUnormCoord, inout
fixedUnormCoord.x += numEntries * DEBUG_FONT_TEXT_SCALE_WIDTH;
// 3. Display the number
+ [unroll] // Needed to supress warning as some odd code gen is happening here. Is bad for perf, but it is a debug display.
for (uint j = 0; j < maxStringSize; ++j)
{
// Numeric value incurrent font start on the second row at 0
@@ -213,7 +214,7 @@ void DrawInteger(int intValue, float3 fontColor, uint2 currentUnormCoord, inout
DrawInteger(intValue, fontColor, currentUnormCoord, fixedUnormCoord, color, 0, false);
}
-void DrawFloat(float floatValue, float3 fontColor, uint2 currentUnormCoord, inout uint2 fixedUnormCoord, inout float3 color)
+void DrawFloatExplicitPrecision(float floatValue, float3 fontColor, uint2 currentUnormCoord, uint digitCount, inout uint2 fixedUnormCoord, inout float3 color)
{
if (IsNaN(floatValue))
{
@@ -227,12 +228,17 @@ void DrawFloat(float floatValue, float3 fontColor, uint2 currentUnormCoord, inou
bool forceNegativeSign = floatValue >= 0.0f ? false : true;
DrawInteger(intValue, fontColor, currentUnormCoord, fixedUnormCoord, color, 0, forceNegativeSign);
DrawCharacter('.', fontColor, currentUnormCoord, fixedUnormCoord, color);
- int fracValue = int(frac(abs(floatValue)) * 1e6); // 6 digit
- int leading0 = 6 - (int(log10(fracValue)) + 1); // Counting leading0 to add in front of the float
+ int fracValue = int(frac(abs(floatValue)) * pow(10, digitCount));
+ int leading0 = digitCount - (int(log10(fracValue)) + 1); // Counting leading0 to add in front of the float
DrawInteger(fracValue, fontColor, currentUnormCoord, fixedUnormCoord, color, leading0, false);
}
}
+void DrawFloat(float floatValue, float3 fontColor, uint2 currentUnormCoord, inout uint2 fixedUnormCoord, inout float3 color)
+{
+ DrawFloatExplicitPrecision(floatValue, fontColor, currentUnormCoord, 6, fixedUnormCoord, color);
+}
+
// Debug rendering is performed at the end of the frame (after post-processing).
// Debug textures are never flipped upside-down automatically. Therefore, we must always flip manually.
bool ShouldFlipDebugTexture()
diff --git a/com.unity.render-pipelines.high-definition/Runtime/Debug/DebugExposure.shader b/com.unity.render-pipelines.high-definition/Runtime/Debug/DebugExposure.shader
new file mode 100644
index 00000000000..bdd95e18d60
--- /dev/null
+++ b/com.unity.render-pipelines.high-definition/Runtime/Debug/DebugExposure.shader
@@ -0,0 +1,552 @@
+Shader "Hidden/HDRP/DebugExposure"
+{
+ HLSLINCLUDE
+
+ #include "Packages/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Components/Tonemapping.cs.hlsl"
+ #include "Packages/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/ExposureCommon.hlsl"
+ #include "Packages/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/HistogramExposureCommon.hlsl"
+ #define DEBUG_DISPLAY
+ #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Debug/DebugDisplay.hlsl"
+ #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/ACES.hlsl"
+ #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
+ #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"
+
+ #pragma vertex Vert
+ #pragma target 4.5
+ #pragma only_renderers d3d11 playstation xboxone vulkan metal switch
+
+ #define PERCENTILE_AS_BARS 0
+
+ // Contains the scene color post-processed (tonemapped etc.)
+ TEXTURE2D_X(_DebugFullScreenTexture);
+
+ // Tonemap related
+ TEXTURE3D(_LogLut3D);
+ SAMPLER(sampler_LogLut3D);
+
+ float4 _ExposureDebugParams;
+ float4 _LogLut3D_Params; // x: 1 / lut_size, y: lut_size - 1, z: contribution, w: unused
+ // Custom tonemapping settings
+ float4 _CustomToneCurve;
+ float4 _ToeSegmentA;
+ float4 _ToeSegmentB;
+ float4 _MidSegmentA;
+ float4 _MidSegmentB;
+ float4 _ShoSegmentA;
+ float4 _ShoSegmentB;
+
+ #define _DrawTonemapCurve _ExposureDebugParams.x
+ #define _TonemapType _ExposureDebugParams.y
+
+
+ struct Attributes
+ {
+ uint vertexID : SV_VertexID;
+ };
+
+ struct Varyings
+ {
+ float4 positionCS : SV_POSITION;
+ float2 texcoord : TEXCOORD0;
+ };
+
+ Varyings Vert(Attributes input)
+ {
+ Varyings output;
+ output.positionCS = GetFullScreenTriangleVertexPosition(input.vertexID);
+ output.texcoord = GetNormalizedFullScreenTriangleTexCoord(input.vertexID);
+
+ return output;
+ }
+
+ float3 Tonemap(float3 colorLinear)
+ {
+ if(_TonemapType == TONEMAPPINGMODE_NEUTRAL)
+ {
+ colorLinear = NeutralTonemap(colorLinear);
+ }
+ if (_TonemapType == TONEMAPPINGMODE_ACES)
+ {
+ // Note: input is actually ACEScg (AP1 w/ linear encoding)
+ float3 aces = ACEScg_to_ACES(colorLinear);
+ colorLinear = AcesTonemap(aces);
+ }
+ if (_TonemapType == TONEMAPPINGMODE_CUSTOM) // Custom
+ {
+ colorLinear = CustomTonemap(colorLinear, _CustomToneCurve.xyz, _ToeSegmentA, _ToeSegmentB.xy, _MidSegmentA, _MidSegmentB.xy, _ShoSegmentA, _ShoSegmentB.xy);
+ }
+ if (_TonemapType == TONEMAPPINGMODE_EXTERNAL) // External
+ {
+ float3 colorLutSpace = saturate(LinearToLogC(colorLinear));
+ float3 colorLut = ApplyLut3D(TEXTURE3D_ARGS(_LogLut3D, sampler_LogLut3D), colorLutSpace, _LogLut3D_Params.xy);
+ colorLinear = lerp(colorLinear, colorLut, _LogLut3D_Params.z);
+ }
+
+ return colorLinear;
+ }
+
+ float3 ToHeat(float value)
+ {
+ float3 r = value * 2.1f - float3(1.8f, 1.14f, 0.3f);
+ return 1.0f - r * r;
+ }
+
+ float GetEVAtLocation(float2 uv)
+ {
+ return ComputeEV100FromAvgLuminance(max(SampleLuminance(uv), 1e-4));
+ }
+
+ // Returns true if it drew the location of the indicator.
+ void DrawHeatSideBar(float2 uv, float2 startSidebar, float2 endSidebar, float evValueRange, float3 indicatorColor, float2 sidebarSize, float extremeMargin, inout float3 sidebarColor)
+ {
+ float2 extremesSize = float2(extremeMargin, 0);
+ float2 borderSize = 2 * _ScreenSize.zw * _RTHandleScale.xy;
+ int indicatorHalfSize = 5;
+
+
+ if (all(uv > startSidebar) && all(uv < endSidebar))
+ {
+ float inRange = (uv.x - startSidebar.x) / (endSidebar.x - startSidebar.x);
+ evValueRange = clamp(evValueRange, 0.0f, 1.0f);
+ int distanceInPixels = abs(evValueRange - inRange) * sidebarSize.x * _ScreenSize.x;
+ if (distanceInPixels < indicatorHalfSize)
+ {
+ sidebarColor = indicatorColor;
+ }
+ else if (distanceInPixels < indicatorHalfSize + 1)
+ {
+ sidebarColor = 0.0f;
+ }
+ else
+ {
+ sidebarColor = ToHeat(inRange);
+ }
+ }
+ else if (all(uv > startSidebar - extremesSize) && all(uv < endSidebar))
+ {
+ sidebarColor = float3(0,0,0);
+ }
+ else if (all(uv > startSidebar) && all(uv < endSidebar + extremesSize))
+ {
+ sidebarColor = float3(1, 1, 1);
+ }
+ else if(all(uv > startSidebar - (extremesSize + borderSize)) && all(uv < endSidebar + (extremesSize + borderSize)))
+ {
+ sidebarColor = 0.0f;
+ }
+ }
+
+ float GetHistogramValue(float coord, out bool isEdge)
+ {
+ float barSize = _ScreenSize.x / HISTOGRAM_BINS;
+ float bin = coord / barSize;
+
+ float locWithinBin = barSize * frac(bin);
+
+ isEdge = locWithinBin < 1 || locWithinBin > (barSize - 1);
+ return UnpackWeight(_HistogramBuffer[(uint)(bin)]);
+ }
+
+ float ComputePercentile(float2 uv, float histSum, out float minPercentileBin, out float maxPercentileBin)
+ {
+ float sumBelowValue = 0.0f;
+ float sumForMin = 0.0f;
+ float sumForMax = 0.0f;
+
+ minPercentileBin = -1;
+ maxPercentileBin = -1;
+
+ float ev = GetEVAtLocation(uv);
+
+ for (int i = 0; i < HISTOGRAM_BINS; ++i)
+ {
+ float evAtBin = BinLocationToEV(i);
+ float evAtNextBin = BinLocationToEV(i+1);
+
+ float histVal = UnpackWeight(_HistogramBuffer[i]);
+
+ if (ev >= evAtBin)
+ {
+ sumBelowValue += histVal;
+ }
+
+ //TODO: This could be more precise, now it locks to bin location
+ if (minPercentileBin < 0)
+ {
+ sumForMin += histVal;
+ if (sumForMin / histSum >= _HistogramMinPercentile)
+ {
+
+ minPercentileBin = i;
+ }
+ }
+
+ if (maxPercentileBin < 0)
+ {
+ sumForMax += histVal;
+ if (sumForMax / histSum > _HistogramMaxPercentile)
+ {
+ maxPercentileBin = i;
+ }
+ }
+ }
+
+ return sumBelowValue / histSum;
+ }
+
+ void DrawHistogramIndicatorBar(float coord, float uvXLocation, float widthNDC, float3 color, inout float3 outColor)
+ {
+ float halfWidthInScreen = widthNDC * _ScreenSize.x;
+ float minScreenPos = (uvXLocation - widthNDC * 0.5) * _ScreenSize.x;
+ float maxScreenPos = (uvXLocation + widthNDC * 0.5) * _ScreenSize.x;
+
+ if (coord > minScreenPos && coord < maxScreenPos)
+ {
+ outColor = color;
+ }
+ }
+
+ void DrawTriangleIndicator(float2 coord, float labelBarHeight, float uvXLocation, float widthNDC, float3 color, inout float3 outColor)
+ {
+ float halfWidthInScreen = widthNDC * _ScreenSize.x;
+ float arrowStart = labelBarHeight * 0.4f;
+
+ float heightInIndicator = ((coord.y - arrowStart) / (labelBarHeight - arrowStart));
+ float indicatorWidth = 1.0f - heightInIndicator;
+
+ float minScreenPos = (uvXLocation - widthNDC * indicatorWidth * 0.5) * _ScreenSize.x;
+ float maxScreenPos = (uvXLocation + widthNDC * indicatorWidth * 0.5) * _ScreenSize.x;
+
+ uint triangleBorder = 2;
+ if (coord.x > minScreenPos && coord.x < maxScreenPos && coord.y >= arrowStart)
+ {
+ outColor = color;
+ }
+ else if (coord.x > minScreenPos - triangleBorder && coord.x < maxScreenPos + triangleBorder && coord.y > arrowStart - triangleBorder)
+ {
+ outColor = 0;
+ }
+ }
+
+ void DrawHistogramFrame(float2 uv, uint2 unormCoord, float frameHeight, float3 backgroundColor, float alpha, float maxHist, float minPercentLoc, float maxPercentLoc, inout float3 outColor)
+ {
+ float2 borderSize = 2 * _ScreenSize.zw * _RTHandleScale.xy;
+ float heightLabelBar = (DEBUG_FONT_TEXT_WIDTH * 1.25) * _ScreenSize.w * _RTHandleScale.y;
+
+ if (uv.y > frameHeight) return;
+
+ // ---- Draw General frame ----
+ if (uv.x < borderSize.x || uv.x >(1.0f - borderSize.x))
+ {
+ outColor = 0.0;
+ return;
+ }
+ else if (uv.y > frameHeight - borderSize.y || uv.y < borderSize.y)
+ {
+ outColor = 0.0;
+ return;
+ }
+ else
+ {
+ outColor = lerp(outColor, backgroundColor, alpha);
+ }
+
+ // ---- Draw label bar -----
+ if (uv.y < heightLabelBar)
+ {
+ outColor = outColor * 0.075f;
+ }
+
+ // ---- Draw Buckets frame ----
+
+ bool isEdgeOfBin = false;
+ float val = GetHistogramValue(unormCoord.x, isEdgeOfBin);
+ val /= maxHist;
+
+ val *= 0.95*(frameHeight - heightLabelBar);
+ val += heightLabelBar;
+
+ if (uv.y < val && uv.y > heightLabelBar)
+ {
+ isEdgeOfBin = isEdgeOfBin || (uv.y > val - _ScreenSize.w);
+#if PERCENTILE_AS_BARS == 0
+ uint bin = uint((unormCoord.x * (HISTOGRAM_BINS)) / (_ScreenSize.x));
+ if (bin <= uint(minPercentLoc) && minPercentLoc > 0)
+ {
+ outColor.rgb = float3(0, 0, 1);
+ }
+ else if(bin >= uint(maxPercentLoc) && maxPercentLoc > 0)
+ {
+ outColor.rgb = float3(1, 0, 0);
+ }
+ else
+#endif
+ outColor.rgb = float3(1.0f, 1.0f, 1.0f);
+ if (isEdgeOfBin) outColor.rgb = 0;
+ }
+
+ // ---- Draw labels ----
+
+ // Number of labels
+ int labelCount = 12;
+ float oneOverLabelCount = rcp(labelCount);
+ float labelDeltaScreenSpace = _ScreenSize.x * oneOverLabelCount;
+
+ int minLabelLocationX = DEBUG_FONT_TEXT_WIDTH * 0.25;
+ int maxLabelLocationX = _ScreenSize.x - (DEBUG_FONT_TEXT_WIDTH * 3);
+
+ int labelLocationY = 0.0f;
+
+ [unroll]
+ for (int i = 0; i <= labelCount; ++i)
+ {
+ float t = oneOverLabelCount * i;
+ float labelValue = lerp(ParamExposureLimitMin, ParamExposureLimitMax, t);
+ uint2 labelLoc = uint2((uint)lerp(minLabelLocationX, maxLabelLocationX, t), labelLocationY);
+ DrawFloatExplicitPrecision(labelValue, float3(1.0f, 1.0f, 1.0f), unormCoord, 1, labelLoc, outColor.rgb);
+ }
+
+ // ---- Draw indicators ----
+ float currExposure = _ExposureTexture[int2(0, 0)].y;
+ float targetExposure = _ExposureDebugTexture[int2(0, 0)].x;
+
+ float evInRange = (currExposure - ParamExposureLimitMin) / (ParamExposureLimitMax - ParamExposureLimitMin);
+ float targetEVInRange = (targetExposure - ParamExposureLimitMin) / (ParamExposureLimitMax - ParamExposureLimitMin);
+
+ float halfIndicatorSize = 0.007f;
+ float halfWidthInScreen = halfIndicatorSize * _ScreenSize.x;
+
+ float labelFrameHeightScreen = heightLabelBar * (_ScreenSize.y / _RTHandleScale.y);
+
+ if (uv.y < heightLabelBar)
+ {
+ DrawTriangleIndicator(float2(unormCoord.xy), labelFrameHeightScreen, targetEVInRange, halfIndicatorSize, float3(0.9f, 0.75f, 0.1f), outColor);
+ DrawTriangleIndicator(float2(unormCoord.xy), labelFrameHeightScreen, evInRange, halfIndicatorSize, float3(0.15f, 0.15f, 0.1f), outColor);
+
+ // Find location for percentiles bars.
+#if PERCENTILE_AS_BARS
+ DrawHistogramIndicatorBar(float(unormCoord.x), minPercentLoc, 0.003f, float3(0, 0, 1), outColor);
+ DrawHistogramIndicatorBar(float(unormCoord.x), maxPercentLoc, 0.003f, float3(1, 0, 0), outColor);
+#endif
+ }
+
+ // ---- Draw Tonemap curve ----
+ if (_DrawTonemapCurve)
+ {
+ float exposureAtLoc = lerp(ParamExposureLimitMin, ParamExposureLimitMax, uv.x);
+ const float K = 12.5; // Reflected-light meter calibration constant
+ float luminanceFromExposure = _ExposureTexture[int2(0, 0)].x * (exp2(exposureAtLoc) * (K / 100.0f));
+
+ val = saturate(Luminance(Tonemap(luminanceFromExposure)));
+ val *= 0.95 * (frameHeight - heightLabelBar);
+ val += heightLabelBar;
+
+ float curveWidth = 4 * _ScreenSize.w;
+
+ if (uv.y < val && uv.y >(val - curveWidth))
+ {
+ outColor = outColor * 0.1 + 0.9 * 0;
+ }
+ }
+ }
+
+ float3 FragMetering(Varyings input) : SV_Target
+ {
+ UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
+ float2 uv = input.texcoord.xy;
+ float3 color = SAMPLE_TEXTURE2D_X_LOD(_DebugFullScreenTexture, s_linear_clamp_sampler, uv, 0.0).xyz;
+ float weight = WeightSample(input.positionCS.xy, _ScreenSize.xy);
+
+ float pipFraction = 0.33f;
+ uint borderSize = 3;
+ float2 topRight = pipFraction * _ScreenSize.xy;
+
+ if (all(input.positionCS.xy < topRight))
+ {
+ float2 scaledUV = uv / pipFraction;
+ float3 pipColor = SAMPLE_TEXTURE2D_X_LOD(_SourceTexture, s_linear_clamp_sampler, scaledUV, 0.0).xyz;
+ float weight = WeightSample(scaledUV.xy * _ScreenSize.xy / _RTHandleScale.xy, _ScreenSize.xy);
+
+ return pipColor * weight;
+ }
+ else if (all(input.positionCS.xy < (topRight + borderSize)))
+ {
+ return float3(0.33f, 0.33f, 0.33f);
+ }
+ else
+ {
+ return color;
+ }
+ }
+
+ float3 FragSceneEV100(Varyings input) : SV_Target
+ {
+ UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
+ float2 uv = input.texcoord.xy;
+
+ float3 textColor = 0.0f;
+
+ float2 sidebarSize = float2(0.9, 0.02) * _RTHandleScale.xy;
+
+ float heightLabelBar = (DEBUG_FONT_TEXT_WIDTH * 1.25f) * _ScreenSize.w * _RTHandleScale.y;
+
+ float2 sidebarBottomLeft = float2(0.05 * _RTHandleScale.x, heightLabelBar);
+ float2 endPointSidebar = sidebarBottomLeft + sidebarSize;
+
+ float3 outputColor = 0;
+ float ev = GetEVAtLocation(uv);
+
+ float evInRange = (ev - ParamExposureLimitMin) / (ParamExposureLimitMax - ParamExposureLimitMin);
+
+ if (ev < ParamExposureLimitMax && ev > ParamExposureLimitMin)
+ {
+ outputColor = ToHeat(evInRange);
+ }
+ else if (ev > ParamExposureLimitMax)
+ {
+ outputColor = 1.0f;
+ }
+ else if (ev < ParamExposureLimitMin)
+ {
+ outputColor = 0.0f;
+ }
+
+ // Get value at indicator
+ float2 indicatorUV = _MousePixelCoord.zw;
+ float indicatorEV = GetEVAtLocation(indicatorUV);
+ float indicatorEVRange = (indicatorEV - ParamExposureLimitMin) / (ParamExposureLimitMax - ParamExposureLimitMin);
+
+ float extremeMargin = 5 * _ScreenSize.z * _RTHandleScale.x;
+ DrawHeatSideBar(uv, sidebarBottomLeft, endPointSidebar, indicatorEVRange, 0.66f, sidebarSize, extremeMargin, outputColor);
+
+ int2 unormCoord = input.positionCS.xy;
+
+ // Label bar
+ float2 borderSize = 2 * _ScreenSize.zw * _RTHandleScale.xy;
+ if (uv.y < heightLabelBar &&
+ uv.x >= (sidebarBottomLeft.x - borderSize.x) && uv.x <= (borderSize.x + endPointSidebar.x))
+ {
+ outputColor = outputColor * 0.075f;
+ }
+
+ // Number of labels
+ int labelCount = 8;
+ float oneOverLabelCount = rcp(labelCount);
+ float labelDeltaScreenSpace = _ScreenSize.x * oneOverLabelCount;
+
+ int minLabelLocationX = (sidebarBottomLeft.x - borderSize.x) * (_ScreenSize.x / _RTHandleScale.x) + DEBUG_FONT_TEXT_WIDTH * 0.25;
+ int maxLabelLocationX = (borderSize.x + endPointSidebar.x) * (_ScreenSize.x / _RTHandleScale.x) - (DEBUG_FONT_TEXT_WIDTH * 3);
+
+ int labelLocationY = 0.0f;
+
+ [unroll]
+ for (int i = 0; i <= labelCount; ++i)
+ {
+ float t = oneOverLabelCount * i;
+ float labelValue = lerp(ParamExposureLimitMin, ParamExposureLimitMax, t);
+ uint2 labelLoc = uint2((uint)lerp(minLabelLocationX, maxLabelLocationX, t), labelLocationY);
+ DrawFloatExplicitPrecision(labelValue, float3(1.0f, 1.0f, 1.0f), unormCoord, 1, labelLoc, outputColor.rgb);
+ }
+
+ int displayTextOffsetX = DEBUG_FONT_TEXT_WIDTH;
+ int2 textLocation = int2(_MousePixelCoord.x + displayTextOffsetX, _MousePixelCoord.y);
+ DrawFloatExplicitPrecision(indicatorEV, textColor, unormCoord, 1, textLocation, outputColor.rgb);
+ textLocation = _MousePixelCoord.xy;
+ DrawCharacter('X', float3(0.0f, 0.0f, 0.0f), unormCoord, textLocation, outputColor.rgb);
+
+ return outputColor;
+ }
+
+
+
+ float3 FragHistogram(Varyings input) : SV_Target
+ {
+ UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
+ float2 uv = input.texcoord.xy;
+
+ float3 color = SAMPLE_TEXTURE2D_X_LOD(_DebugFullScreenTexture, s_linear_clamp_sampler, uv, 0.0).xyz;
+ float weight = WeightSample(input.positionCS.xy, _ScreenSize.xy);
+
+ float3 outputColor = color;
+
+ // Get some overall info from the histogram
+ float maxValue = 0;
+ float sum = 0;
+ for (int i = 0; i < HISTOGRAM_BINS; ++i)
+ {
+ float histogramVal = UnpackWeight(_HistogramBuffer[i]);
+ maxValue = max(histogramVal, maxValue);
+ sum += histogramVal;
+ }
+
+ float minPercentileBin = 0;
+ float maxPercentileBin = 0;
+ float percentile = ComputePercentile(uv, sum, minPercentileBin, maxPercentileBin);
+
+ if (percentile < _HistogramMinPercentile)
+ {
+ outputColor = (input.positionCS.x + input.positionCS.y) % 2 == 0 ? float3(0.0f, 0.0f, 1.0) : color*0.33;
+ }
+ if (percentile > _HistogramMaxPercentile)
+ {
+ outputColor = (input.positionCS.x + input.positionCS.y) % 2 == 0 ? float3(1.0, 0.0f, 0.0f) : color * 0.33;
+ }
+
+ float histFrameHeight = 0.2 * _RTHandleScale.y;
+ float minPercentileLoc = max(minPercentileBin, 0);
+ float maxPercentileLoc = min(maxPercentileBin, HISTOGRAM_BINS - 1);
+#if PERCENTILE_AS_BARS
+ minPercentileLoc /= (HISTOGRAM_BINS - 1);
+ maxPercentileLoc /= (HISTOGRAM_BINS - 1);
+#endif
+
+ DrawHistogramFrame(uv, input.positionCS.xy, histFrameHeight, float3(0.125,0.125,0.125), 0.4f, maxValue, minPercentileLoc, maxPercentileLoc, outputColor);
+
+
+ return outputColor;
+ }
+
+ ENDHLSL
+
+ SubShader
+ {
+ Tags{ "RenderPipeline" = "HDRenderPipeline" }
+ Pass
+ {
+ ZWrite Off
+ ZTest Always
+ Blend Off
+ Cull Off
+
+ HLSLPROGRAM
+ #pragma fragment FragSceneEV100
+ ENDHLSL
+ }
+
+ Pass
+ {
+ ZWrite Off
+ ZTest Always
+ Blend Off
+ Cull Off
+
+ HLSLPROGRAM
+ #pragma fragment FragMetering
+ ENDHLSL
+ }
+
+ Pass
+ {
+ ZWrite Off
+ ZTest Always
+ Blend Off
+ Cull Off
+
+ HLSLPROGRAM
+ #pragma fragment FragHistogram
+ ENDHLSL
+ }
+
+ }
+ Fallback Off
+}
diff --git a/com.unity.render-pipelines.high-definition/Runtime/Debug/DebugExposure.shader.meta b/com.unity.render-pipelines.high-definition/Runtime/Debug/DebugExposure.shader.meta
new file mode 100644
index 00000000000..4383c002901
--- /dev/null
+++ b/com.unity.render-pipelines.high-definition/Runtime/Debug/DebugExposure.shader.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 0ef322534f047a34c96d29419d56d17a
+ShaderImporter:
+ externalObjects: {}
+ defaultTextures: []
+ nonModifiableTextures: []
+ preprocessorOverride: 0
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/com.unity.render-pipelines.high-definition/Runtime/Debug/LightingDebug.cs b/com.unity.render-pipelines.high-definition/Runtime/Debug/LightingDebug.cs
index 2ca520cafff..b45ea0a39f2 100644
--- a/com.unity.render-pipelines.high-definition/Runtime/Debug/LightingDebug.cs
+++ b/com.unity.render-pipelines.high-definition/Runtime/Debug/LightingDebug.cs
@@ -162,12 +162,34 @@ public enum ShadowMapDebugMode
VisualizeDirectionalLightAtlas,
/// Display area lights shadow atlas as an overlay.
VisualizeAreaLightAtlas,
+ /// Display punctual lights cached shadow atlas as an overlay.
+ VisualizeCachedPunctualLightAtlas,
+ /// Display area lights cached shadow atlas as an overlay.
+ VisualizeCachedAreaLightAtlas,
/// Display a single light shadow map as an overlay.
VisualizeShadowMap,
/// Replace rendering with a black and white view of the shadow of a single light in the scene.
SingleShadow,
}
+ ///
+ /// Exposure debug mode.
+ ///
+ [GenerateHLSL]
+ public enum ExposureDebugMode
+ {
+ /// No exposure debug.
+ None,
+ /// Display the EV100 values of the scene, color-coded.
+ SceneEV100Values,
+ /// Display the Histogram used for exposure.
+ HistogramView,
+ /// Visualize the scene color weighted as the metering mode selected.
+ MeteringWeighted,
+
+ }
+
+
///
/// Probe Volume Debug Modes.
///
@@ -291,8 +313,12 @@ public bool IsDebugDisplayEnabled()
/// Maximum number of lights against which the light overdraw gradient is displayed.
public uint maxDebugLightCount = 24;
+ /// Exposure debug mode.
+ public ExposureDebugMode exposureDebugMode = ExposureDebugMode.None;
/// Exposure compensation to apply on current scene exposure.
public float debugExposure = 0.0f;
+ /// Whether to show tonemap curve in the histogram debug view or not.
+ public bool showTonemapCurveAlongHistogramView = true;
/// Display the light cookies atlas.
public bool displayCookieAtlas = false;
diff --git a/com.unity.render-pipelines.high-definition/Runtime/Debug/LightingDebug.cs.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Debug/LightingDebug.cs.hlsl
index 28338f1bab5..65580776c7c 100644
--- a/com.unity.render-pipelines.high-definition/Runtime/Debug/LightingDebug.cs.hlsl
+++ b/com.unity.render-pipelines.high-definition/Runtime/Debug/LightingDebug.cs.hlsl
@@ -59,8 +59,18 @@
#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
+//
+#define EXPOSUREDEBUGMODE_NONE (0)
+#define EXPOSUREDEBUGMODE_SCENE_EV100VALUES (1)
+#define EXPOSUREDEBUGMODE_HISTOGRAM_VIEW (2)
+#define EXPOSUREDEBUGMODE_METERING_WEIGHTED (3)
//
// UnityEngine.Rendering.HighDefinition.ProbeVolumeDebugMode: static fields
diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/Light/HDAdditionalLightData.Types.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/Light/HDAdditionalLightData.Types.cs
index f7af4cc618f..d733242139f 100644
--- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/Light/HDAdditionalLightData.Types.cs
+++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/Light/HDAdditionalLightData.Types.cs
@@ -234,6 +234,11 @@ public HDLightType type
{
if (type != value)
{
+ if (m_ShadowUpdateMode != ShadowUpdateMode.EveryFrame)
+ {
+ HDShadowManager.cachedShadowManager.EvictLight(this);
+ }
+
switch (value)
{
case HDLightType.Directional:
@@ -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))
diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/Light/HDAdditionalLightData.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/Light/HDAdditionalLightData.cs
index f4f1c6073fa..0c8f8dec753 100644
--- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/Light/HDAdditionalLightData.cs
+++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/Light/HDAdditionalLightData.cs
@@ -1330,6 +1330,18 @@ public ShadowUpdateMode shadowUpdateMode
if (m_ShadowUpdateMode == value)
return;
+ if (m_ShadowUpdateMode != ShadowUpdateMode.EveryFrame && value == ShadowUpdateMode.EveryFrame)
+ {
+ if(!preserveCachedShadow)
+ {
+ HDShadowManager.cachedShadowManager.EvictLight(this);
+ }
+ }
+ else if(legacyLight.shadows != LightShadows.None && m_ShadowUpdateMode == ShadowUpdateMode.EveryFrame && value != ShadowUpdateMode.EveryFrame)
+ {
+ HDShadowManager.cachedShadowManager.RegisterLight(this);
+ }
+
m_ShadowUpdateMode = value;
}
}
@@ -1373,6 +1385,24 @@ public float barnDoorLength
}
}
+ [SerializeField]
+ bool m_preserveCachedShadow = false;
+ ///
+ /// Controls whether the cached shadow maps for this light is preserved upon disabling the light.
+ /// If this field is set to true, then the light will maintain its space in the cached shadow atlas until it is destroyed.
+ ///
+ public bool preserveCachedShadow
+ {
+ get => m_preserveCachedShadow;
+ set
+ {
+ if (m_preserveCachedShadow == value)
+ return;
+
+ m_preserveCachedShadow = value;
+ }
+ }
+
///
/// True if the light affects volumetric fog, false otherwise
///
@@ -1445,16 +1475,13 @@ internal int shadowPrecision
bool m_WillRenderScreenSpaceShadow;
bool m_WillRenderRayTracedShadow;
int[] m_ShadowRequestIndices;
- bool m_ShadowMapRenderedSinceLastRequest = false;
- // Data for cached shadow maps.
- Vector2 m_CachedShadowResolution = new Vector2(0,0);
- Vector3 m_CachedViewPos = new Vector3(0, 0, 0);
- int[] m_CachedResolutionRequestIndices = new int[6];
- bool m_CachedDataIsValid = true;
- // This is useful to detect whether the atlas has been repacked since the light was last seen
- int m_AtlasShapeID = 0;
+ // Data for cached shadow maps
+ [System.NonSerialized]
+ internal int lightIdxForCachedShadows = -1;
+ Vector3 m_CachedViewPos = new Vector3(0, 0, 0);
+
[System.NonSerialized]
Plane[] m_ShadowFrustumPlanes = new Plane[6];
@@ -1627,24 +1654,21 @@ public int areaLightEmissiveMeshLayer
}
}
- private void DisableCachedShadowSlot()
- {
- if (WillRenderShadowMap() && !ShadowIsUpdatedEveryFrame())
- {
- HDShadowManager.instance.MarkCachedShadowSlotsAsEmpty(shadowMapType, GetInstanceID());
- HDShadowManager.instance.PruneEmptyCachedSlots(shadowMapType); // We invalidate it all to be sure.
- m_ShadowMapRenderedSinceLastRequest = false;
- }
- }
void OnDestroy()
{
- DisableCachedShadowSlot();
+ if(lightIdxForCachedShadows >= 0) // If it is within the cached system we need to evict it.
+ HDShadowManager.cachedShadowManager.EvictLight(this);
}
void OnDisable()
{
- DisableCachedShadowSlot();
+ // If it is within the cached system we need to evict it, unless user explicitly requires not to.
+ if (!preserveCachedShadow && lightIdxForCachedShadows >= 0)
+ {
+ HDShadowManager.cachedShadowManager.EvictLight(this);
+ }
+
SetEmissiveMeshRendererEnabled(false);
s_overlappingHDLights.Remove(this);
}
@@ -1672,20 +1696,18 @@ int GetShadowRequestCount(HDShadowSettings shadowSettings, HDLightType lightType
public void RequestShadowMapRendering()
{
if(shadowUpdateMode == ShadowUpdateMode.OnDemand)
- m_ShadowMapRenderedSinceLastRequest = false;
+ HDShadowManager.cachedShadowManager.ScheduleShadowUpdate(this);
}
- internal bool ShouldRenderShadows()
+
+ ///
+ /// Some lights render more than one shadow maps (e.g. cascade shadow maps or point lights). This method is used to request the rendering of specific shadow map
+ /// when Update Mode is set to On Demand. For example, to request the update of a second cascade, shadowIndex should be 1.
+ /// Note: if shadowIndex is a 0-based index and it must be lower than the number of shadow maps a light renders (i.e. cascade count for directional lights, 6 for point lights).
+ ///
+ public void RequestSubShadowMapRendering(int shadowIndex)
{
- switch (shadowUpdateMode)
- {
- case ShadowUpdateMode.EveryFrame:
- return true;
- case ShadowUpdateMode.OnDemand:
- return !m_ShadowMapRenderedSinceLastRequest;
- case ShadowUpdateMode.OnEnable:
- return !m_ShadowMapRenderedSinceLastRequest;
- }
- return true;
+ if (shadowUpdateMode == ShadowUpdateMode.OnDemand)
+ HDShadowManager.cachedShadowManager.ScheduleShadowUpdate(this, shadowIndex);
}
internal bool ShadowIsUpdatedEveryFrame()
@@ -1705,6 +1727,11 @@ internal void EvaluateShadowState(HDCamera hdCamera, in ProcessedLightData proce
// If the shadow is too far away, we don't render it
m_WillRenderShadowMap &= processedLight.lightType == HDLightType.Directional || processedLight.distanceToCamera < shadowFadeDistance;
+ if (processedLight.lightType == HDLightType.Area && areaLightShape != AreaLightShape.Rectangle)
+ {
+ m_WillRenderShadowMap = false;
+ }
+
// First we reset the ray tracing and screen space shadow data
m_WillRenderScreenSpaceShadow = false;
m_WillRenderRayTracedShadow = false;
@@ -1742,7 +1769,7 @@ internal void EvaluateShadowState(HDCamera hdCamera, in ProcessedLightData proce
}
}
- private int GetResolutionFromSettings(ShadowMapType shadowMapType, HDShadowInitParameters initParameters)
+ internal int GetResolutionFromSettings(ShadowMapType shadowMapType, HDShadowInitParameters initParameters)
{
switch (shadowMapType)
{
@@ -1787,17 +1814,9 @@ internal void ReserveShadowMap(Camera camera, HDShadowManager shadowManager, HDS
viewPortRescaling |= (shadowType == ShadowMapType.PunctualAtlas && initParameters.punctualLightShadowAtlas.useDynamicViewportRescale);
viewPortRescaling |= (shadowType == ShadowMapType.AreaLightAtlas && initParameters.areaLightShadowAtlas.useDynamicViewportRescale);
- bool shadowsAreCached = !ShouldRenderShadows();
- if (shadowsAreCached)
- {
- viewportSize = m_CachedShadowResolution;
- }
- else
- {
- m_CachedShadowResolution = viewportSize;
- }
+ bool shadowIsInCacheSystem = !ShadowIsUpdatedEveryFrame();
- if (viewPortRescaling && !shadowsAreCached)
+ if (viewPortRescaling && !shadowIsInCacheSystem)
{
// resize viewport size by the normalized size of the light on screen
float screenArea = screenRect.width * screenRect.height;
@@ -1816,11 +1835,11 @@ internal void ReserveShadowMap(Camera camera, HDShadowManager shadowManager, HDS
shadowManager.UpdateDirectionalShadowResolution((int)viewportSize.x, shadowSettings.cascadeShadowSplitCount.value);
int count = GetShadowRequestCount(shadowSettings, lightType);
- bool needsCachedSlotsInAtlas = shadowsAreCached && !(ShadowIsUpdatedEveryFrame() || lightType == HDLightType.Directional);
+ bool needResolutionRequestNow = !shadowIsInCacheSystem || lightType == HDLightType.Directional;
for (int index = 0; index < count; index++)
{
- m_ShadowRequestIndices[index] = shadowManager.ReserveShadowResolutions(needsCachedSlotsInAtlas ? new Vector2(resolution, resolution) : viewportSize, shadowType, GetInstanceID(), index, needsCachedSlotsInAtlas, out m_CachedResolutionRequestIndices[index]);
+ m_ShadowRequestIndices[index] = shadowManager.ReserveShadowResolutions(shadowIsInCacheSystem ? new Vector2(resolution, resolution) : viewportSize, shadowMapType, GetInstanceID(), index, needResolutionRequestNow);
}
}
@@ -1885,18 +1904,17 @@ internal int UpdateShadowRequest(HDCamera hdCamera, HDShadowManager manager, HDS
shadowRequestCount = 0;
HDLightType lightType = type;
- ShadowMapType shadowType = GetShadowMapType(lightType);
int count = GetShadowRequestCount(shadowSettings, lightType);
- bool shadowIsCached = !ShouldRenderShadows() && !lightingDebugSettings.clearShadowAtlas;
- bool isUpdatedEveryFrame = ShadowIsUpdatedEveryFrame();
-
- bool hasCachedSlotInAtlas = !(ShadowIsUpdatedEveryFrame() || legacyLight.type == LightType.Directional);
- bool shouldUseRequestFromCachedList = shadowIsCached && hasCachedSlotInAtlas && !manager.AtlasHasResized(shadowType);
- bool cachedDataIsValid = shadowIsCached && m_CachedDataIsValid && (manager.GetAtlasShapeID(shadowType) == m_AtlasShapeID) && manager.CachedDataIsValid(shadowType);
- cachedDataIsValid = cachedDataIsValid || (legacyLight.type == LightType.Directional);
- shadowIsCached = shadowIsCached && (hasCachedSlotInAtlas && cachedDataIsValid || legacyLight.type == LightType.Directional);
+ bool shadowIsInCachedSystem = !ShadowIsUpdatedEveryFrame();
+ // Note if we are in cached system, but if a placement has not been found by this point we bail out shadows
+ bool shadowHasAtlasPlacement = true;
+ if (shadowIsInCachedSystem)
+ {
+ // If we force evicted the light, it will have lightIdxForCachedShadows == -1
+ shadowHasAtlasPlacement = !HDShadowManager.cachedShadowManager.LightIsPendingPlacement(this, shadowMapType) && (lightIdxForCachedShadows != -1);
+ }
for (int index = 0; index < count; index++)
{
@@ -1905,17 +1923,28 @@ internal int UpdateShadowRequest(HDCamera hdCamera, HDShadowManager manager, HDS
Matrix4x4 invViewProjection = Matrix4x4.identity;
int shadowRequestIndex = m_ShadowRequestIndices[index];
- HDShadowResolutionRequest resolutionRequest = manager.GetResolutionRequest(shadowType, shouldUseRequestFromCachedList, shouldUseRequestFromCachedList ? m_CachedResolutionRequestIndices[index] : shadowRequestIndex);
+ HDShadowResolutionRequest resolutionRequest = manager.GetResolutionRequest(shadowRequestIndex);
if (resolutionRequest == null)
continue;
+ int cachedShadowID = lightIdxForCachedShadows + index;
+ bool shadowNeedsRendering = shadowHasAtlasPlacement;
+
+ if (shadowIsInCachedSystem && shadowHasAtlasPlacement)
+ {
+ shadowNeedsRendering = HDShadowManager.cachedShadowManager.ShadowIsPendingUpdate(cachedShadowID, shadowMapType);
+ HDShadowManager.cachedShadowManager.UpdateResolutionRequest(ref resolutionRequest, cachedShadowID, shadowMapType);
+ }
+
+ shadowRequest.isInCachedAtlas = shadowIsInCachedSystem;
+
Vector2 viewportSize = resolutionRequest.resolution;
if (shadowRequestIndex == -1)
continue;
- if (shadowIsCached)
+ if (!shadowNeedsRendering)
{
shadowRequest.cachedShadowData.cacheTranslationDelta = cameraPos - m_CachedViewPos;
shadowRequest.shouldUseCachedShadow = true;
@@ -1928,7 +1957,6 @@ internal int UpdateShadowRequest(HDCamera hdCamera, HDShadowManager manager, HDS
{
m_CachedViewPos = cameraPos;
shadowRequest.shouldUseCachedShadow = false;
- m_ShadowMapRenderedSinceLastRequest = true;
// Write per light type matrices, splitDatas and culling parameters
switch (lightType)
@@ -1976,11 +2004,14 @@ internal int UpdateShadowRequest(HDCamera hdCamera, HDShadowManager manager, HDS
}
shadowRequest.atlasViewport = resolutionRequest.atlasViewport;
- manager.UpdateShadowRequest(shadowRequestIndex, shadowRequest);
- shadowRequest.shouldUseCachedShadow = shadowRequest.shouldUseCachedShadow && cachedDataIsValid;
+ manager.UpdateShadowRequest(shadowRequestIndex, shadowRequest, shadowIsInCachedSystem);
- m_CachedDataIsValid = manager.CachedDataIsValid(shadowType);
- m_AtlasShapeID = manager.GetAtlasShapeID(shadowType);
+ if(shadowIsInCachedSystem && shadowNeedsRendering)
+ {
+ // Handshake with the cached shadow manager to notify about the rendering.
+ // Technically the rendering has not happened yet, but it is scheduled.
+ HDShadowManager.cachedShadowManager.MarkShadowAsRendered(cachedShadowID, shadowMapType);
+ }
// Store the first shadow request id to return it
if (firstShadowRequestIndex == -1)
@@ -1989,7 +2020,7 @@ internal int UpdateShadowRequest(HDCamera hdCamera, HDShadowManager manager, HDS
shadowRequestCount++;
}
- return firstShadowRequestIndex;
+ return shadowHasAtlasPlacement ? firstShadowRequestIndex : -1;
}
void SetCommonShadowRequestSettings(HDShadowRequest shadowRequest, VisibleLight visibleLight, Vector3 cameraPos, Matrix4x4 invViewProjection, Vector2 viewportSize, int lightIndex, HDLightType lightType, HDShadowFilteringQuality filteringQuality)
@@ -2390,8 +2421,8 @@ public static void InitDefaultHDAdditionalLightData(HDAdditionalLightData lightD
void OnValidate()
{
UpdateBounds();
- DisableCachedShadowSlot();
- m_ShadowMapRenderedSinceLastRequest = false;
+
+ RefreshCachedShadow();
#if UNITY_EDITOR
// If modification are due to change on prefab asset that are non overridden on this prefab instance
@@ -2726,7 +2757,19 @@ internal void UpdateAllLightValues(bool fromTimeLine)
UpdateAreaLightEmissiveMesh(fromTimeLine: fromTimeLine);
}
-#endregion
+ internal void RefreshCachedShadow()
+ {
+ bool wentThroughCachedShadowSystem = lightIdxForCachedShadows >= 0;
+ if (wentThroughCachedShadowSystem)
+ HDShadowManager.cachedShadowManager.EvictLight(this);
+
+ if (!ShadowIsUpdatedEveryFrame() && legacyLight.shadows != LightShadows.None)
+ {
+ HDShadowManager.cachedShadowManager.RegisterLight(this);
+ }
+ }
+
+ #endregion
#region User API functions
@@ -2866,19 +2909,40 @@ public void SetLightDimmer(float dimmer = 1, float volumetricDimmer = 1)
/// Set the shadow resolution.
///
/// Must be between 16 and 16384
- public void SetShadowResolution(int resolution) => shadowResolution.@override = resolution;
+ public void SetShadowResolution(int resolution)
+ {
+ if (shadowResolution.@override != resolution)
+ {
+ shadowResolution.@override = resolution;
+ RefreshCachedShadow();
+ }
+ }
///
/// Set the shadow resolution quality level.
///
/// The quality level to use
- public void SetShadowResolutionLevel(int level) => shadowResolution.level = level;
+ public void SetShadowResolutionLevel(int level)
+ {
+ if (shadowResolution.level != level)
+ {
+ shadowResolution.level = level;
+ RefreshCachedShadow();
+ }
+ }
///
/// Set whether the shadow resolution use the override value.
///
/// True to use the override value, false otherwise.
- public void SetShadowResolutionOverride(bool useOverride) => shadowResolution.useOverride = useOverride;
+ public void SetShadowResolutionOverride(bool useOverride)
+ {
+ if (shadowResolution.useOverride != useOverride)
+ {
+ shadowResolution.useOverride = useOverride;
+ RefreshCachedShadow();
+ }
+ }
///
/// Set the near plane of the shadow.
@@ -3051,8 +3115,11 @@ ShadowMapType shadowMapType
void OnEnable()
{
- if (shadowUpdateMode == ShadowUpdateMode.OnEnable)
- m_ShadowMapRenderedSinceLastRequest = false;
+ if (shadowUpdateMode != ShadowUpdateMode.EveryFrame && legacyLight.shadows != LightShadows.None)
+ {
+ HDShadowManager.cachedShadowManager.RegisterLight(this);
+ }
+
SetEmissiveMeshRendererEnabled(true);
}
diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/HDShadow.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/HDShadow.hlsl
index 8d916005488..3734e9e0704 100644
--- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/HDShadow.hlsl
+++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/HDShadow.hlsl
@@ -45,7 +45,14 @@ float GetPunctualShadowAttenuation(HDShadowContext shadowContext, float2 positio
sd.atlasOffset = shadowContext.shadowDatas[shadowDataIndex + CubeMapFaceID(-L)].atlasOffset;
}
- return EvalShadow_PunctualDepth(sd, _ShadowmapAtlas, s_linear_clamp_compare_sampler, positionSS, positionWS, normalWS, L, L_dist, perspecive);
+ if (sd.isInCachedAtlas > 0) // This is a scalar branch.
+ {
+ return EvalShadow_PunctualDepth(sd, _CachedShadowmapAtlas, s_linear_clamp_compare_sampler, positionSS, positionWS, normalWS, L, L_dist, perspecive);
+ }
+ else
+ {
+ return EvalShadow_PunctualDepth(sd, _ShadowmapAtlas, s_linear_clamp_compare_sampler, positionSS, positionWS, normalWS, L, L_dist, perspecive);
+ }
}
float GetPunctualShadowClosestDistance(HDShadowContext shadowContext, SamplerState sampl, real3 positionWS, int shadowDataIndex, float3 L, float3 lightPositionWS, bool pointLight)
@@ -67,13 +74,31 @@ float GetPunctualShadowClosestDistance(HDShadowContext shadowContext, SamplerSta
sd.rot2 = shadowContext.shadowDatas[shadowDataIndex + CubeMapFaceID(-L)].rot2;
}
- return EvalShadow_SampleClosestDistance_Punctual(sd, _ShadowmapAtlas, sampl, positionWS, L, lightPositionWS);
+ if (sd.isInCachedAtlas > 0) // This is a scalar branch.
+ {
+ return EvalShadow_SampleClosestDistance_Punctual(sd, _CachedShadowmapAtlas, sampl, positionWS, L, lightPositionWS);
+ }
+ else
+ {
+ return EvalShadow_SampleClosestDistance_Punctual(sd, _ShadowmapAtlas, sampl, positionWS, L, lightPositionWS);
+ }
}
float GetAreaLightAttenuation(HDShadowContext shadowContext, float2 positionSS, float3 positionWS, float3 normalWS, int shadowDataIndex, float3 L, float L_dist)
{
+#if (defined(PLATFORM_SUPPORTS_WAVE_INTRINSICS) && !defined(LIGHTLOOP_DISABLE_TILE_AND_CLUSTER))
+ shadowDataIndex = WaveReadLaneFirst(shadowDataIndex);
+#endif
HDShadowData sd = shadowContext.shadowDatas[shadowDataIndex];
- return EvalShadow_AreaDepth(sd, _ShadowmapAreaAtlas, positionSS, positionWS, normalWS, L, L_dist, true);
+
+ if (sd.isInCachedAtlas > 0) // This is a scalar branch.
+ {
+ return EvalShadow_AreaDepth(sd, _CachedAreaLightShadowmapAtlas, positionSS, positionWS, normalWS, L, L_dist, true);
+ }
+ else
+ {
+ return EvalShadow_AreaDepth(sd, _ShadowmapAreaAtlas, positionSS, positionWS, normalWS, L, L_dist, true);
+ }
}
diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs
index 05dec005554..8ed3f8f2455 100644
--- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs
+++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs
@@ -775,10 +775,7 @@ void InitShadowSystem(HDRenderPipelineAsset hdAsset, RenderPipelineResources def
m_ShadowManager = HDShadowManager.instance;
m_ShadowManager.InitShadowManager(
defaultResources,
- m_ShadowInitParameters.directionalShadowsDepthBits,
- m_ShadowInitParameters.punctualLightShadowAtlas,
- m_ShadowInitParameters.areaLightShadowAtlas,
- m_ShadowInitParameters.maxShadowRequests,
+ m_ShadowInitParameters,
defaultResources.shaders.shadowClearPS
);
}
@@ -1194,7 +1191,7 @@ internal void GetDirectionalLightData(CommandBuffer cmd, HDCamera hdCamera, Visi
var lightData = new DirectionalLightData();
- lightData.lightLayers = additionalLightData.GetLightLayers();
+ lightData.lightLayers = hdCamera.frameSettings.IsEnabled(FrameSettingsField.LightLayers) ? additionalLightData.GetLightLayers() : uint.MaxValue;
// Light direction for directional is opposite to the forward direction
lightData.forward = light.GetForward();
@@ -1346,7 +1343,7 @@ internal void GetLightData(CommandBuffer cmd, HDCamera hdCamera, HDShadowSetting
var lightType = processedData.lightType;
var visibleLightAxisAndPosition = light.GetAxisAndPosition();
- lightData.lightLayers = additionalLightData.GetLightLayers();
+ lightData.lightLayers = hdCamera.frameSettings.IsEnabled(FrameSettingsField.LightLayers) ? additionalLightData.GetLightLayers() : uint.MaxValue;
lightData.lightType = gpuLightType;
@@ -1843,7 +1840,7 @@ internal bool GetEnvLightData(CommandBuffer cmd, HDCamera hdCamera, in Processed
return false;
InfluenceVolume influence = probe.influenceVolume;
- envLightData.lightLayers = probe.lightLayersAsUInt;
+ envLightData.lightLayers = hdCamera.frameSettings.IsEnabled(FrameSettingsField.LightLayers) ? probe.lightLayersAsUInt : uint.MaxValue;
envLightData.influenceShapeType = influence.envShape;
envLightData.weight = processedProbe.weight;
envLightData.multiplier = probe.multiplier * m_indirectLightingController.indirectSpecularIntensity.value;
@@ -2569,6 +2566,8 @@ bool PrepareLightsForGPU(CommandBuffer cmd, HDCamera hdCamera, CullingResults cu
var debugLightFilter = debugDisplaySettings.GetDebugLightFilterMode();
var hasDebugLightFilter = debugLightFilter != DebugLightFilterMode.None;
+ HDShadowManager.cachedShadowManager.AssignSlotsInAtlases();
+
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.PrepareLightsForGPU)))
{
Camera camera = hdCamera.camera;
@@ -2631,8 +2630,6 @@ bool PrepareLightsForGPU(CommandBuffer cmd, HDCamera hdCamera, CullingResults cu
PrepareGPUProbeData(cmd, hdCamera, cullResults, hdProbeCullingResults, processedProbesCount);
}
- HDShadowManager.instance.CheckForCulledCachedShadows();
-
if (decalDatasCount > 0)
{
for (int i = 0; i < decalDatasCount; i++)
@@ -4137,6 +4134,10 @@ static void RenderShadowsDebugOverlay(in DebugParameters debugParameters, in HDS
parameters.shadowManager.DisplayShadowAtlas(atlasTextures.punctualShadowAtlas, cmd, parameters.debugShadowMapMaterial, x, y, overlaySize, overlaySize, lightingDebug.shadowMinValue, lightingDebug.shadowMaxValue, mpb);
HDUtils.NextOverlayCoord(ref x, ref y, overlaySize, overlaySize, hdCamera);
break;
+ case ShadowMapDebugMode.VisualizeCachedPunctualLightAtlas:
+ parameters.shadowManager.DisplayCachedPunctualShadowAtlas(atlasTextures.cachedPunctualShadowAtlas, cmd, parameters.debugShadowMapMaterial, x, y, overlaySize, overlaySize, lightingDebug.shadowMinValue, lightingDebug.shadowMaxValue, mpb);
+ HDUtils.NextOverlayCoord(ref x, ref y, overlaySize, overlaySize, hdCamera);
+ break;
case ShadowMapDebugMode.VisualizeDirectionalLightAtlas:
parameters.shadowManager.DisplayShadowCascadeAtlas(atlasTextures.cascadeShadowAtlas, cmd, parameters.debugShadowMapMaterial, x, y, overlaySize, overlaySize, lightingDebug.shadowMinValue, lightingDebug.shadowMaxValue, mpb);
HDUtils.NextOverlayCoord(ref x, ref y, overlaySize, overlaySize, hdCamera);
@@ -4145,6 +4146,10 @@ static void RenderShadowsDebugOverlay(in DebugParameters debugParameters, in HDS
parameters.shadowManager.DisplayAreaLightShadowAtlas(atlasTextures.areaShadowAtlas, cmd, parameters.debugShadowMapMaterial, x, y, overlaySize, overlaySize, lightingDebug.shadowMinValue, lightingDebug.shadowMaxValue, mpb);
HDUtils.NextOverlayCoord(ref x, ref y, overlaySize, overlaySize, hdCamera);
break;
+ case ShadowMapDebugMode.VisualizeCachedAreaLightAtlas:
+ parameters.shadowManager.DisplayCachedAreaShadowAtlas(atlasTextures.cachedAreaShadowAtlas, cmd, parameters.debugShadowMapMaterial, x, y, overlaySize, overlaySize, lightingDebug.shadowMinValue, lightingDebug.shadowMaxValue, mpb);
+ HDUtils.NextOverlayCoord(ref x, ref y, overlaySize, overlaySize, hdCamera);
+ break;
default:
break;
}
diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ScreenSpaceLighting/AmbientOcclusion.RenderGraph.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ScreenSpaceLighting/AmbientOcclusion.RenderGraph.cs
index 926a23f2f84..49bd699a153 100644
--- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ScreenSpaceLighting/AmbientOcclusion.RenderGraph.cs
+++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ScreenSpaceLighting/AmbientOcclusion.RenderGraph.cs
@@ -30,7 +30,7 @@ public TextureHandle Render(RenderGraph renderGraph, HDCamera hdCamera, TextureH
historyRT.referenceSize.y * historyRT.scaleFactor.y);
var rtScaleForHistory = hdCamera.historyRTHandleProperties.rtHandleScale;
- var aoParameters = PrepareRenderAOParameters(hdCamera, renderGraph.rtHandleProperties, historySize * rtScaleForHistory, frameCount);
+ var aoParameters = PrepareRenderAOParameters(hdCamera, historySize * rtScaleForHistory, frameCount);
var packedData = RenderAO(renderGraph, aoParameters, depthPyramid);
result = DenoiseAO(renderGraph, aoParameters, motionVectors, packedData, currentHistory, outputHistory);
@@ -102,8 +102,8 @@ TextureHandle DenoiseAO( RenderGraph renderGraph,
passData.parameters = parameters;
passData.packedData = builder.ReadTexture(aoPackedData);
passData.motionVectors = builder.ReadTexture(motionVectors);
- passData.packedDataBlurred = builder.WriteTexture(renderGraph.CreateTexture(
- new TextureDesc(Vector2.one * scaleFactor, true, true) { colorFormat = GraphicsFormat.R32_UInt, enableRandomWrite = true, name = "AO Packed blurred data" } ));
+ passData.packedDataBlurred = builder.CreateTransientTexture(
+ new TextureDesc(Vector2.one * scaleFactor, true, true) { colorFormat = GraphicsFormat.R32_UInt, enableRandomWrite = true, name = "AO Packed blurred data" } );
passData.currentHistory = builder.ReadTexture(currentHistory); // can also be written on first frame, but since it's an imported resource, it doesn't matter in term of lifetime.
passData.outputHistory = builder.WriteTexture(outputHistory);
diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ScreenSpaceLighting/AmbientOcclusion.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ScreenSpaceLighting/AmbientOcclusion.cs
index 25273975777..74170876cb9 100644
--- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ScreenSpaceLighting/AmbientOcclusion.cs
+++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ScreenSpaceLighting/AmbientOcclusion.cs
@@ -300,7 +300,7 @@ struct RenderAOParameters
public ShaderVariablesAmbientOcclusion cb;
}
- RenderAOParameters PrepareRenderAOParameters(HDCamera camera, RTHandleProperties rtHandleProperties, Vector2 historySize, int frameCount)
+ RenderAOParameters PrepareRenderAOParameters(HDCamera camera, Vector2 historySize, int frameCount)
{
var parameters = new RenderAOParameters();
@@ -567,7 +567,7 @@ internal void Dispatch(CommandBuffer cmd, HDCamera camera, int frameCount)
currentHistory.referenceSize.y * currentHistory.scaleFactor.y);
var rtScaleForHistory = camera.historyRTHandleProperties.rtHandleScale;
- var aoParameters = PrepareRenderAOParameters(camera, RTHandles.rtHandleProperties, historySize * rtScaleForHistory, frameCount);
+ var aoParameters = PrepareRenderAOParameters(camera, historySize * rtScaleForHistory, frameCount);
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.HorizonSSAO)))
{
RenderAO(aoParameters, m_PackedDataTex, m_Resources, cmd);
diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDCachedShadowAtlas.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDCachedShadowAtlas.cs
new file mode 100644
index 00000000000..243ae194781
--- /dev/null
+++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDCachedShadowAtlas.cs
@@ -0,0 +1,524 @@
+using System;
+using System.Collections.Generic;
+using System.Collections;
+using UnityEngine.Experimental.Rendering;
+
+namespace UnityEngine.Rendering.HighDefinition
+{
+ class HDCachedShadowAtlas : HDShadowAtlas
+ {
+ static private int s_InitialCapacity = 256;
+
+ // Constants.
+ private const int m_MinSlotSize = 64;
+ private const int m_MaxShadowsPerLight = 6;
+
+
+ private int m_NextLightID = 0;
+ private bool m_CanTryPlacement = false;
+
+ struct CachedShadowRecord
+ {
+ internal int shadowIndex;
+ internal int viewportSize; // We assume only square shadows maps.
+ internal Vector4 offsetInAtlas; // When is registered xy is the offset in the texture atlas, in UVs, the zw is the entry offset in the C# representation.
+ }
+
+ private int m_AtlasResolutionInSlots; // Atlas Resolution / m_MinSlotSize
+
+ private bool m_NeedOptimalPacking = true; // Whenever this is set to true, the pending lights are sorted before insertion.
+
+ private List m_AtlasSlots; // One entry per slot (of size m_MinSlotSize) true if occupied, false if free.
+
+ // Note: Some of these could be simple lists, but since we might need to search by index some of them and we want to avoid GC alloc, a dictionary is easier.
+ // This also mean slightly worse performance, however hopefully the number of cached shadow lights is not huge at any tie.
+ private Dictionary m_PlacedShadows;
+ private Dictionary m_ShadowsPendingRendering;
+ private Dictionary m_RegisteredLightDataPendingPlacement;
+ private Dictionary m_RecordsPendingPlacement; // Note: this is different from m_RegisteredLightDataPendingPlacement because it contains records that were allocated in the system
+ // but they lost their spot (e.g. post defrag). They don't have a light associated anymore if not by index, so we keep a separate collection.
+
+ private List m_TempListForPlacement;
+
+
+ private ShadowMapType m_ShadowType;
+
+ // ------------------------------------------------------------------------------------------
+ // Init Functions
+ // ------------------------------------------------------------------------------------------
+ public HDCachedShadowAtlas(ShadowMapType type)
+ {
+ m_PlacedShadows = new Dictionary(s_InitialCapacity);
+ m_ShadowsPendingRendering = new Dictionary(s_InitialCapacity);
+ m_TempListForPlacement = new List(s_InitialCapacity);
+
+ m_RegisteredLightDataPendingPlacement = new Dictionary(s_InitialCapacity);
+ m_RecordsPendingPlacement = new Dictionary(s_InitialCapacity);
+
+ m_ShadowType = type;
+ }
+
+ public override void InitAtlas(RenderPipelineResources renderPipelineResources, int width, int height, int atlasShaderID, Material clearMaterial, int maxShadowRequests, HDShadowInitParameters initParams, BlurAlgorithm blurAlgorithm = BlurAlgorithm.None, FilterMode filterMode = FilterMode.Bilinear, DepthBits depthBufferBits = DepthBits.Depth16, RenderTextureFormat format = RenderTextureFormat.Shadowmap, string name = "")
+ {
+ base.InitAtlas(renderPipelineResources, width, height, atlasShaderID, clearMaterial, maxShadowRequests, initParams, blurAlgorithm, filterMode, depthBufferBits, format, name);
+
+ m_AtlasResolutionInSlots = HDUtils.DivRoundUp(width, m_MinSlotSize);
+ m_AtlasSlots = new List(m_AtlasResolutionInSlots * m_AtlasResolutionInSlots);
+ for (int i = 0; i < m_AtlasResolutionInSlots * m_AtlasResolutionInSlots; ++i)
+ {
+ m_AtlasSlots.Add(false);
+ }
+
+ // Note: If changing the characteristics of the atlas via HDRP asset, the lights OnEnable will not be called again so we are missing them, however we can explicitly
+ // put them back up for placement. If this is the first Init of the atlas, the lines below do nothing.
+ DefragmentAtlasAndReRender(initParams);
+ m_CanTryPlacement = true;
+ m_NeedOptimalPacking = true;
+ }
+ // ------------------------------------------------------------------------------------------
+
+
+
+ // ------------------------------------------------------------------------------------------
+ // Functions to access and deal with the C# representation of the atlas
+ // ------------------------------------------------------------------------------------------
+ private bool IsEntryEmpty(int x, int y)
+ {
+ return (m_AtlasSlots[y * m_AtlasResolutionInSlots + x] == false);
+ }
+ private bool IsEntryFull(int x, int y)
+ {
+ return (m_AtlasSlots[y * m_AtlasResolutionInSlots + x]);
+ }
+
+ // Always fill slots in a square shape, for example : if x = 1 and y = 2, if numEntries = 2 it will fill {(1,2),(2,2),(1,3),(2,3)}
+ private void FillEntries(int x, int y, int numEntries)
+ {
+ MarkEntries(x, y, numEntries, true);
+ }
+
+ private void MarkEntries(int x, int y, int numEntries, bool value)
+ {
+ for (int j = y; j < y + numEntries; ++j)
+ {
+ for (int i = x; i < x + numEntries; ++i)
+ {
+ m_AtlasSlots[j * m_AtlasResolutionInSlots + i] = value;
+ }
+ }
+ }
+
+ // Checks if we have a square slot available starting from (x,y) and of size numEntries.
+ private bool CheckSlotAvailability(int x, int y, int numEntries)
+ {
+ for (int j = y; j < y + numEntries; ++j)
+ {
+ for (int i = x; i < x + numEntries; ++i)
+ {
+ if (i >= m_AtlasResolutionInSlots || j >= m_AtlasResolutionInSlots || IsEntryFull(i, j))
+ {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ internal bool FindSlotInAtlas(int resolution, out int x, out int y)
+ {
+ int numEntries = HDUtils.DivRoundUp(resolution, m_MinSlotSize);
+
+ for (int j = 0; j < m_AtlasResolutionInSlots; ++j)
+ {
+ for (int i = 0; i < m_AtlasResolutionInSlots; ++i)
+ {
+ if (CheckSlotAvailability(i, j, numEntries))
+ {
+ x = i;
+ y = j;
+ return true;
+ }
+ }
+ }
+
+ x = 0;
+ y = 0;
+ return false;
+ }
+
+ internal bool GetSlotInAtlas(int resolution, out int x, out int y)
+ {
+ if (FindSlotInAtlas(resolution, out x, out y))
+ {
+ int numEntries = HDUtils.DivRoundUp(resolution, m_MinSlotSize);
+ FillEntries(x, y, numEntries);
+ return true;
+ }
+
+ return false;
+ }
+ // ---------------------------------------------------------------------------------------
+
+ // ------------------------------------------------------------------------------------------
+ // Entry and exit points to the atlas
+ // ------------------------------------------------------------------------------------------
+
+ internal int GetNextLightIdentifier()
+ {
+ int outputId = m_NextLightID;
+ m_NextLightID += m_MaxShadowsPerLight; // We give unique identifiers to each
+ return outputId;
+ }
+
+ internal void RegisterLight(HDAdditionalLightData lightData)
+ {
+ // If we are trying to register something that we have already placed, we do nothing
+ if (lightData.lightIdxForCachedShadows >= 0 && m_PlacedShadows.ContainsKey(lightData.lightIdxForCachedShadows))
+ return;
+
+ // We register only if not already pending placement and if enabled.
+ if (!m_RegisteredLightDataPendingPlacement.ContainsKey(lightData.lightIdxForCachedShadows) && lightData.isActiveAndEnabled)
+ {
+#if UNITY_2020_2_OR_NEWER
+ lightData.legacyLight.useViewFrustumForShadowCasterCull = false;
+#endif
+ lightData.lightIdxForCachedShadows = GetNextLightIdentifier();
+
+ m_RegisteredLightDataPendingPlacement.Add(lightData.lightIdxForCachedShadows, lightData);
+ m_CanTryPlacement = true;
+ }
+ }
+
+ internal void EvictLight(HDAdditionalLightData lightData)
+ {
+ m_RegisteredLightDataPendingPlacement.Remove(lightData.lightIdxForCachedShadows);
+
+ int numberOfShadows = (lightData.type == HDLightType.Point) ? 6 : 1;
+
+ int lightIdx = lightData.lightIdxForCachedShadows;
+ lightData.lightIdxForCachedShadows = -1;
+
+ for (int i = 0; i < numberOfShadows; ++i)
+ {
+ bool valueFound = false;
+ int shadowIdx = lightIdx + i;
+
+ m_RecordsPendingPlacement.Remove(shadowIdx);
+
+ valueFound = m_PlacedShadows.TryGetValue(shadowIdx, out CachedShadowRecord recordToRemove);
+
+ if (valueFound)
+ {
+#if UNITY_2020_2_OR_NEWER
+ lightData.legacyLight.useViewFrustumForShadowCasterCull = true;
+#endif
+ m_PlacedShadows.Remove(shadowIdx);
+ m_ShadowsPendingRendering.Remove(shadowIdx);
+
+ MarkEntries((int)recordToRemove.offsetInAtlas.z, (int)recordToRemove.offsetInAtlas.w, HDUtils.DivRoundUp(recordToRemove.viewportSize, m_MinSlotSize), false);
+ m_CanTryPlacement = true;
+ }
+ }
+ }
+
+ // ------------------------------------------------------------------------------------------
+
+
+ // ------------------------------------------------------------------------------------------
+ // Atlassing on the actual textures
+ // ------------------------------------------------------------------------------------------
+
+
+ void InsertionSort(ref List list, int startIndex, int lastIndex)
+ {
+ int i = startIndex;
+
+ while (i < lastIndex)
+ {
+ var curr = list[i];
+
+ int j = i - 1;
+
+ // Sort in descending order.
+ while ((j >= 0) && ((curr.viewportSize > list[j].viewportSize)))
+ {
+ list[j + 1] = list[j];
+ j--;
+ }
+
+ list[j + 1] = curr;
+ i++;
+ }
+ }
+
+ private void AddLightListToRecordList(Dictionary lightList, HDShadowInitParameters initParams, ref List recordList)
+ {
+ foreach (var currentLightData in lightList.Values)
+ {
+ int resolution = 0;
+
+ resolution = currentLightData.GetResolutionFromSettings(m_ShadowType, initParams);
+
+ HDLightType lightType = currentLightData.type;
+ int numberOfShadows = (lightType == HDLightType.Point) ? 6 : 1;
+
+ for (int i = 0; i < numberOfShadows; ++i)
+ {
+ CachedShadowRecord record;
+ record.shadowIndex = currentLightData.lightIdxForCachedShadows + i;
+ record.viewportSize = resolution;
+ record.offsetInAtlas = new Vector4(-1, -1, -1, -1); // Will be set later.
+
+ recordList.Add(record);
+ }
+ }
+ }
+
+ private bool PlaceMultipleShadows(int startIdx, int numberOfShadows)
+ {
+ int firstShadowIdx = m_TempListForPlacement[startIdx].shadowIndex;
+
+ Vector2Int[] placements = new Vector2Int[m_MaxShadowsPerLight];
+ int successfullyPlaced = 0;
+
+ for (int j = 0; j < numberOfShadows; ++j)
+ {
+ var record = m_TempListForPlacement[startIdx + j];
+
+ Debug.Assert(firstShadowIdx + j == record.shadowIndex);
+
+ int x, y;
+ if (GetSlotInAtlas(record.viewportSize, out x, out y))
+ {
+ successfullyPlaced++;
+ placements[j] = new Vector2Int(x, y);
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ // If they all fit, we actually placed them, otherwise we mark the slot that we temp filled as free and go on.
+ if(successfullyPlaced == numberOfShadows) // Success.
+ {
+ for (int j = 0; j < numberOfShadows; ++j)
+ {
+ var record = m_TempListForPlacement[startIdx + j];
+
+ record.offsetInAtlas = new Vector4(placements[j].x * m_MinSlotSize, placements[j].y * m_MinSlotSize, placements[j].x, placements[j].y);
+
+ m_ShadowsPendingRendering.Add(record.shadowIndex, record);
+ m_PlacedShadows.Add(record.shadowIndex, record);
+ }
+
+ return true;
+ }
+ else if(successfullyPlaced > 0) // Couldn't place them all, but we placed something, so we revert those placements.
+ {
+ int numEntries = HDUtils.DivRoundUp(m_TempListForPlacement[startIdx].viewportSize, m_MinSlotSize);
+ for (int j=0; j 0 && m_CanTryPlacement)
+ {
+ m_TempListForPlacement.Clear();
+
+ m_TempListForPlacement.AddRange(m_RecordsPendingPlacement.Values);
+ AddLightListToRecordList(m_RegisteredLightDataPendingPlacement, initParameters, ref m_TempListForPlacement);
+
+ if (m_NeedOptimalPacking)
+ {
+ InsertionSort(ref m_TempListForPlacement, 0, m_TempListForPlacement.Count);
+ m_NeedOptimalPacking = false;
+ }
+
+ PerformPlacement();
+
+ m_CanTryPlacement = false; // It is pointless we try the placement every frame if no modifications to the amount of light registered happened.
+ }
+ }
+
+ internal void DefragmentAtlasAndReRender(HDShadowInitParameters initParams)
+ {
+ m_TempListForPlacement.Clear();
+
+ m_TempListForPlacement.AddRange(m_PlacedShadows.Values);
+ m_TempListForPlacement.AddRange(m_RecordsPendingPlacement.Values);
+ AddLightListToRecordList(m_RegisteredLightDataPendingPlacement, initParams, ref m_TempListForPlacement);
+
+ for (int i = 0; i < m_AtlasResolutionInSlots * m_AtlasResolutionInSlots; ++i)
+ {
+ m_AtlasSlots[i] = false;
+ }
+
+ // Clear the other state lists.
+ m_PlacedShadows.Clear();
+ m_ShadowsPendingRendering.Clear();
+ m_RecordsPendingPlacement.Clear(); // We'll reset what records are pending.
+
+ // Sort in order to obtain a more optimal packing.
+ InsertionSort(ref m_TempListForPlacement, 0, m_TempListForPlacement.Count);
+
+ PerformPlacement();
+
+ // This is fairly inefficient, but simple and this function should be called very rarely.
+ // We need to add to pending the records that were placed but were not in m_RegisteredLightDataPendingPlacement
+ // but they don't have a place yet.
+ foreach (var record in m_TempListForPlacement)
+ {
+ if (!m_PlacedShadows.ContainsKey(record.shadowIndex)) // If we couldn't place it
+ {
+ int parentLightIdx = record.shadowIndex - (record.shadowIndex % m_MaxShadowsPerLight);
+ if (!m_RegisteredLightDataPendingPlacement.ContainsKey(parentLightIdx)) // Did not come originally from m_RegisteredLightDataPendingPlacement
+ {
+ if (!m_RecordsPendingPlacement.ContainsKey(record.shadowIndex))
+ m_RecordsPendingPlacement.Add(record.shadowIndex, record);
+ }
+ }
+ }
+
+ m_CanTryPlacement = false;
+ }
+
+ // ------------------------------------------------------------------------------------------
+
+
+ // ------------------------------------------------------------------------------------------
+ // Functions to query and change state of a shadow
+ // ------------------------------------------------------------------------------------------
+ internal bool LightIsPendingPlacement(HDAdditionalLightData lightData)
+ {
+ return (m_RegisteredLightDataPendingPlacement.ContainsKey(lightData.lightIdxForCachedShadows) ||
+ m_RecordsPendingPlacement.ContainsKey(lightData.lightIdxForCachedShadows));
+ }
+
+ internal bool ShadowIsPendingRendering(int shadowIdx)
+ {
+ return m_ShadowsPendingRendering.ContainsKey(shadowIdx);
+ }
+
+ internal void ScheduleShadowUpdate(HDAdditionalLightData lightData)
+ {
+ if (!lightData.isActiveAndEnabled) return;
+
+ int lightIdx = lightData.lightIdxForCachedShadows;
+ Debug.Assert(lightIdx >= 0);
+
+ if (!m_PlacedShadows.ContainsKey(lightIdx))
+ {
+ if (m_RegisteredLightDataPendingPlacement.ContainsKey(lightIdx))
+ return;
+
+ RegisterLight(lightData);
+ }
+ else
+ {
+ int numberOfShadows = (lightData.type == HDLightType.Point) ? 6 : 1;
+ for (int i = 0; i < numberOfShadows; ++i)
+ {
+ int shadowIdx = lightIdx + i;
+ ScheduleShadowUpdate(shadowIdx);
+ }
+ }
+ }
+
+ internal void ScheduleShadowUpdate(int shadowIdx)
+ {
+ // It needs to be placed already, otherwise needs to go through the registering cycle first.
+ CachedShadowRecord shadowRecord;
+ bool recordFound = m_PlacedShadows.TryGetValue(shadowIdx, out shadowRecord);
+ Debug.Assert(recordFound);
+ // Return to avoid error when assert is skipped.
+ if (!recordFound) return;
+
+ // It already schedule for update we do nothing;
+ if (m_ShadowsPendingRendering.ContainsKey(shadowIdx))
+ return;
+
+ // Put the record up for rendering
+ m_ShadowsPendingRendering.Add(shadowIdx, shadowRecord);
+ }
+
+ internal void MarkAsRendered(int shadowIdx)
+ {
+ if (m_ShadowsPendingRendering.ContainsKey(shadowIdx))
+ {
+ m_ShadowsPendingRendering.Remove(shadowIdx);
+ }
+ }
+
+ // Used to update the resolution request processed by the light loop
+ internal void UpdateResolutionRequest(ref HDShadowResolutionRequest request, int shadowIdx)
+ {
+ CachedShadowRecord record;
+ bool valueFound = m_PlacedShadows.TryGetValue(shadowIdx, out record);
+
+ if (!valueFound)
+ {
+ Debug.LogWarning("Trying to render a cached shadow map that doesn't have a slot in the atlas yet.");
+ }
+
+ request.atlasViewport = new Rect(record.offsetInAtlas.x, record.offsetInAtlas.y, record.viewportSize, record.viewportSize);
+ request.resolution = new Vector2(record.viewportSize, record.viewportSize);
+ }
+
+ // ------------------------------------------------------------------------------------------
+ }
+}
+
+
diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDCachedShadowAtlas.cs.meta b/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDCachedShadowAtlas.cs.meta
new file mode 100644
index 00000000000..8db859613f5
--- /dev/null
+++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDCachedShadowAtlas.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 30d3e37aa9eea124490d8fc2afcb5c8e
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDCachedShadowManager.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDCachedShadowManager.cs
new file mode 100644
index 00000000000..dde24b128bd
--- /dev/null
+++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDCachedShadowManager.cs
@@ -0,0 +1,306 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine.Experimental.Rendering;
+using UnityEngine.Serialization;
+
+namespace UnityEngine.Rendering.HighDefinition
+{
+ // Note: The punctual and area light shadows have a specific atlas, however because there can be only be only one directional light casting shadow
+ // we use this cached shadow manager only as a source of utilities functions, but the data is stored in the dynamic shadow atlas.
+
+ ///
+ /// The class responsible to handle cached shadow maps (shadows with Update mode set to OnEnable or OnDemand).
+ ///
+ public class HDCachedShadowManager
+ {
+ private static HDCachedShadowManager s_Instance = new HDCachedShadowManager();
+ public static HDCachedShadowManager instance { get { return s_Instance; } }
+
+ // Data for cached directional light shadows.
+ private const int m_MaxShadowCascades = 4;
+ private bool[] m_DirectionalShadowPendingUpdate = new bool[m_MaxShadowCascades];
+
+ // Cached atlas
+ internal HDCachedShadowAtlas punctualShadowAtlas;
+ internal HDCachedShadowAtlas areaShadowAtlas;
+ // Cache here to be able to compute resolutions.
+ private HDShadowInitParameters m_InitParams;
+
+ // ------------------------ Debug API -------------------------------
+#if UNITY_EDITOR
+ internal void PrintLightStatusInCachedAtlas()
+ {
+ bool headerPrinted = false;
+ var lights = GameObject.FindObjectsOfType();
+ foreach (var light in lights)
+ {
+ ShadowMapType shadowMapType = light.GetShadowMapType(light.type);
+ if (instance.LightIsPendingPlacement(light, shadowMapType))
+ {
+ if (!headerPrinted)
+ {
+ Debug.Log(" ===== Lights pending placement in the cached shadow atlas: ===== ");
+ headerPrinted = true;
+ }
+ Debug.Log("\t Name: " + light.name + " Type: " + light.type + " Resolution: " + light.GetResolutionFromSettings(shadowMapType, m_InitParams));
+ }
+ }
+
+ headerPrinted = false;
+ foreach (var light in lights)
+ {
+ ShadowMapType shadowMapType = light.GetShadowMapType(light.type);
+ if (!(instance.LightIsPendingPlacement(light, light.GetShadowMapType(light.type))) && light.lightIdxForCachedShadows != -1)
+ {
+ if (!headerPrinted)
+ {
+ Debug.Log("===== Lights placed in cached shadow atlas: ===== ");
+ headerPrinted = true;
+ }
+ Debug.Log("\t Name: " + light.name + " Type: " + light.type + " Resolution: " + light.GetResolutionFromSettings(shadowMapType, m_InitParams));
+ }
+ }
+ }
+#endif
+ // ------------------------ Public API -------------------------------
+
+ ///
+ /// This function verifies if a shadow map of resolution shadowResolution for a light of type lightType would fit in the atlas when inserted.
+ ///
+ /// The resolution of the hypothetical shadow map that we are assessing.
+ /// The type of the light that cast the hypothetical shadow map that we are assessing.
+ /// True if the shadow map would fit in the atlas, false otherwise.
+ public bool WouldFitInAtlas(int shadowResolution, HDLightType lightType)
+ {
+ bool fits = true;
+ int x, y;
+
+ if (lightType == HDLightType.Point)
+ {
+ for (int i = 0; i < 6; ++i)
+ {
+ fits = fits && HDShadowManager.cachedShadowManager.punctualShadowAtlas.FindSlotInAtlas(shadowResolution, out x, out y);
+ }
+ }
+
+ if (lightType == HDLightType.Spot)
+ fits = fits && HDShadowManager.cachedShadowManager.punctualShadowAtlas.FindSlotInAtlas(shadowResolution, out x, out y);
+
+ if (lightType == HDLightType.Area)
+ fits = fits && HDShadowManager.cachedShadowManager.areaShadowAtlas.FindSlotInAtlas(shadowResolution, out x, out y);
+
+ return fits;
+ }
+
+ ///
+ /// If a light is added after a scene is loaded, its placement in the atlas might be not optimal and the suboptimal placement might prevent a light to find a place in the atlas.
+ /// This function will force a defragmentation of the atlas containing lights of type lightType and redistributes the shadows inside so that the placement is optimal. Note however that this will also mark the shadow maps
+ /// as dirty and they will be re-rendered as soon the light will come into view for the first time after this function call.
+ ///
+ /// The type of the light contained in the atlas that need defragmentation.
+ public void DefragAtlas(HDLightType lightType)
+ {
+ if (lightType == HDLightType.Area)
+ instance.areaShadowAtlas.DefragmentAtlasAndReRender(instance.m_InitParams);
+ if (lightType == HDLightType.Point || lightType == HDLightType.Spot)
+ instance.punctualShadowAtlas.DefragmentAtlasAndReRender(instance.m_InitParams);
+ }
+
+ ///
+ /// This function can be used to evict a light from its atlas. The slots occupied by such light will be available to be occupied by other shadows.
+ /// Note that eviction happens automatically upon light destruction and, if lightData.preserveCachedShadow is false, upon disabling of the light.
+ ///
+ /// The light to evict from the atlas.
+ public void ForceEvictLight(HDAdditionalLightData lightData)
+ {
+ EvictLight(lightData);
+ lightData.lightIdxForCachedShadows = -1;
+ }
+
+ ///
+ /// This function can be used to register a light to the cached shadow system if not already registered. It is necessary to call this function if a light has been
+ /// evicted with ForceEvictLight and it needs to be registered again. Please note that a light is automatically registered when enabled or when the shadow update changes
+ /// from EveryFrame to OnDemand or OnEnable.
+ ///
+ /// The light to register.
+ public void ForceRegisterLight(HDAdditionalLightData lightData)
+ {
+ // Note: this is for now just calling the internal API, but having a separate API helps with future
+ // changes to the process.
+ RegisterLight(lightData);
+ }
+
+ // ------------------------------------------------------------------------------------------------------------------
+
+ private void MarkAllDirectionalShadowsForUpdate()
+ {
+ for (int i = 0; i < m_MaxShadowCascades; ++i)
+ {
+ m_DirectionalShadowPendingUpdate[i] = true;
+ }
+ }
+
+ private HDCachedShadowManager()
+ {
+ punctualShadowAtlas = new HDCachedShadowAtlas(ShadowMapType.PunctualAtlas);
+ if (ShaderConfig.s_AreaLights == 1)
+ areaShadowAtlas = new HDCachedShadowAtlas(ShadowMapType.AreaLightAtlas);
+ }
+
+ internal void InitPunctualShadowAtlas(RenderPipelineResources renderPipelineResources, int width, int height, int atlasShaderID, Material clearMaterial, int maxShadowRequests, HDShadowInitParameters initParams,
+ HDShadowAtlas.BlurAlgorithm blurAlgorithm = HDShadowAtlas.BlurAlgorithm.None, FilterMode filterMode = FilterMode.Bilinear, DepthBits depthBufferBits = DepthBits.Depth16, RenderTextureFormat format = RenderTextureFormat.Shadowmap, string name = "")
+ {
+ m_InitParams = initParams;
+ punctualShadowAtlas.InitAtlas(renderPipelineResources, width, height, atlasShaderID, clearMaterial, maxShadowRequests, initParams, blurAlgorithm, filterMode, depthBufferBits, format, name);
+ }
+
+ internal void InitAreaLightShadowAtlas(RenderPipelineResources renderPipelineResources, int width, int height, int atlasShaderID, Material clearMaterial, int maxShadowRequests, HDShadowInitParameters initParams,
+ HDShadowAtlas.BlurAlgorithm blurAlgorithm = HDShadowAtlas.BlurAlgorithm.None, FilterMode filterMode = FilterMode.Bilinear, DepthBits depthBufferBits = DepthBits.Depth16, RenderTextureFormat format = RenderTextureFormat.Shadowmap, string name = "")
+ {
+ m_InitParams = initParams;
+ areaShadowAtlas.InitAtlas(renderPipelineResources, width, height, atlasShaderID, clearMaterial, maxShadowRequests, initParams, blurAlgorithm, filterMode, depthBufferBits, format, name);
+ }
+
+ internal void RegisterLight(HDAdditionalLightData lightData)
+ {
+ HDLightType lightType = lightData.type;
+
+ if (lightType == HDLightType.Directional)
+ {
+ lightData.lightIdxForCachedShadows = 0;
+ MarkAllDirectionalShadowsForUpdate();
+ }
+
+ if (lightType == HDLightType.Spot || lightType == HDLightType.Point)
+ {
+ punctualShadowAtlas.RegisterLight(lightData);
+ }
+
+ if (ShaderConfig.s_AreaLights == 1 && lightType == HDLightType.Area && lightData.areaLightShape == AreaLightShape.Rectangle)
+ {
+ areaShadowAtlas.RegisterLight(lightData);
+ }
+ }
+
+ internal void EvictLight(HDAdditionalLightData lightData)
+ {
+ HDLightType lightType = lightData.type;
+
+ if (lightType == HDLightType.Directional)
+ {
+ lightData.lightIdxForCachedShadows = -1;
+ MarkAllDirectionalShadowsForUpdate();
+ }
+
+ if (lightType == HDLightType.Spot || lightType == HDLightType.Point)
+ {
+ punctualShadowAtlas.EvictLight(lightData);
+ }
+
+ if (ShaderConfig.s_AreaLights == 1 && lightType == HDLightType.Area)
+ {
+ areaShadowAtlas.EvictLight(lightData);
+ }
+ }
+
+ internal void AssignSlotsInAtlases()
+ {
+ punctualShadowAtlas.AssignOffsetsInAtlas(m_InitParams);
+ if(ShaderConfig.s_AreaLights == 1)
+ areaShadowAtlas.AssignOffsetsInAtlas(m_InitParams);
+ }
+
+ internal bool ShadowIsPendingUpdate(int shadowIdx, ShadowMapType shadowMapType)
+ {
+ if (shadowMapType == ShadowMapType.PunctualAtlas)
+ return punctualShadowAtlas.ShadowIsPendingRendering(shadowIdx);
+ if (shadowMapType == ShadowMapType.AreaLightAtlas)
+ return areaShadowAtlas.ShadowIsPendingRendering(shadowIdx);
+ if (shadowMapType == ShadowMapType.CascadedDirectional)
+ return m_DirectionalShadowPendingUpdate[shadowIdx];
+
+ return false;
+ }
+
+ internal void MarkShadowAsRendered(int shadowIdx, ShadowMapType shadowMapType)
+ {
+ if (shadowMapType == ShadowMapType.PunctualAtlas)
+ punctualShadowAtlas.MarkAsRendered(shadowIdx);
+ if (shadowMapType == ShadowMapType.AreaLightAtlas)
+ areaShadowAtlas.MarkAsRendered(shadowIdx);
+ if (shadowMapType == ShadowMapType.CascadedDirectional)
+ m_DirectionalShadowPendingUpdate[shadowIdx] = false;
+ }
+
+ internal void UpdateResolutionRequest(ref HDShadowResolutionRequest request, int shadowIdx, ShadowMapType shadowMapType)
+ {
+ if (shadowMapType == ShadowMapType.PunctualAtlas)
+ punctualShadowAtlas.UpdateResolutionRequest(ref request, shadowIdx);
+ else if (shadowMapType == ShadowMapType.AreaLightAtlas)
+ areaShadowAtlas.UpdateResolutionRequest(ref request, shadowIdx);
+ }
+
+ internal void UpdateDebugSettings(LightingDebugSettings lightingDebugSettings)
+ {
+ punctualShadowAtlas.UpdateDebugSettings(lightingDebugSettings);
+ if (ShaderConfig.s_AreaLights == 1)
+ areaShadowAtlas.UpdateDebugSettings(lightingDebugSettings);
+ }
+
+ internal void ScheduleShadowUpdate(HDAdditionalLightData light)
+ {
+ var lightType = light.type;
+ if (lightType == HDLightType.Point || lightType == HDLightType.Spot)
+ punctualShadowAtlas.ScheduleShadowUpdate(light);
+ else if (lightType == HDLightType.Area)
+ areaShadowAtlas.ScheduleShadowUpdate(light);
+ else if (lightType == HDLightType.Directional)
+ {
+ MarkAllDirectionalShadowsForUpdate();
+ }
+ }
+
+ internal void ScheduleShadowUpdate(HDAdditionalLightData light, int subShadowIndex)
+ {
+ var lightType = light.type;
+ if (lightType == HDLightType.Spot)
+ punctualShadowAtlas.ScheduleShadowUpdate(light);
+ if (lightType == HDLightType.Area)
+ areaShadowAtlas.ScheduleShadowUpdate(light);
+ if (lightType == HDLightType.Point)
+ {
+ Debug.Assert(subShadowIndex < 6);
+ punctualShadowAtlas.ScheduleShadowUpdate(light.lightIdxForCachedShadows + subShadowIndex);
+ }
+ if (lightType == HDLightType.Directional)
+ {
+ Debug.Assert(subShadowIndex < m_MaxShadowCascades);
+ m_DirectionalShadowPendingUpdate[subShadowIndex] = true;
+ }
+ }
+
+ internal bool LightIsPendingPlacement(HDAdditionalLightData light, ShadowMapType shadowMapType)
+ {
+ if (shadowMapType == ShadowMapType.PunctualAtlas)
+ return punctualShadowAtlas.LightIsPendingPlacement(light);
+ if (shadowMapType == ShadowMapType.AreaLightAtlas)
+ return areaShadowAtlas.LightIsPendingPlacement(light);
+
+ return false;
+ }
+
+ internal void ClearShadowRequests()
+ {
+ punctualShadowAtlas.Clear();
+ if (ShaderConfig.s_AreaLights == 1)
+ areaShadowAtlas.Clear();
+ }
+
+ internal void Dispose()
+ {
+ punctualShadowAtlas.Release();
+ if (ShaderConfig.s_AreaLights == 1)
+ areaShadowAtlas.Release();
+ }
+ }
+}
diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDCachedShadowManager.cs.meta b/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDCachedShadowManager.cs.meta
new file mode 100644
index 00000000000..a589a9254dc
--- /dev/null
+++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDCachedShadowManager.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: fa1ac5557d2a7d24ab8433d01dd9998f
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDDynamicShadowAtlas.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDDynamicShadowAtlas.cs
new file mode 100644
index 00000000000..67f95deea0f
--- /dev/null
+++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDDynamicShadowAtlas.cs
@@ -0,0 +1,174 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine.Experimental.Rendering;
+
+namespace UnityEngine.Rendering.HighDefinition
+{
+ partial class HDDynamicShadowAtlas : HDShadowAtlas
+ {
+ readonly List m_ShadowResolutionRequests = new List();
+
+ float m_RcpScaleFactor = 1;
+ HDShadowResolutionRequest[] m_SortedRequestsCache;
+
+ public HDDynamicShadowAtlas(RenderPipelineResources renderPipelineResources, int width, int height, int atlasShaderID, Material clearMaterial, int maxShadowRequests, HDShadowInitParameters initParams, BlurAlgorithm blurAlgorithm = BlurAlgorithm.None, FilterMode filterMode = FilterMode.Bilinear, DepthBits depthBufferBits = DepthBits.Depth16, RenderTextureFormat format = RenderTextureFormat.Shadowmap, string name = "")
+ : base(renderPipelineResources, width, height, atlasShaderID, clearMaterial, maxShadowRequests, initParams, blurAlgorithm, filterMode, depthBufferBits, format, name)
+ {
+ m_SortedRequestsCache = new HDShadowResolutionRequest[Mathf.CeilToInt(maxShadowRequests)];
+ }
+
+
+ internal void ReserveResolution(HDShadowResolutionRequest shadowRequest)
+ {
+ m_ShadowResolutionRequests.Add(shadowRequest);
+ }
+
+ // Stable (unlike List.Sort) sorting algorithm which, unlike Linq's, doesn't use JIT (lol).
+ // Sorts in place. Very efficient (O(n)) for already sorted data.
+ void InsertionSort(HDShadowResolutionRequest[] array, int startIndex, int lastIndex)
+ {
+ int i = startIndex + 1;
+
+ while (i < lastIndex)
+ {
+ var curr = array[i];
+
+ int j = i - 1;
+
+ // Sort in descending order.
+ while ((j >= 0) && ((curr.resolution.x > array[j].resolution.x) ||
+ (curr.resolution.y > array[j].resolution.y)))
+ {
+ array[j + 1] = array[j];
+ j--;
+ }
+
+ array[j + 1] = curr;
+ i++;
+ }
+ }
+
+ private bool AtlasLayout(bool allowResize, HDShadowResolutionRequest[] fullShadowList, int requestsCount)
+ {
+ float curX = 0, curY = 0, curH = 0, xMax = width, yMax = height;
+ m_RcpScaleFactor = 1;
+ for (int i = 0; i < requestsCount; ++i)
+ {
+ var shadowRequest = fullShadowList[i];
+ // shadow atlas layouting
+ Rect viewport = new Rect(Vector2.zero, shadowRequest.resolution);
+ curH = Mathf.Max(curH, viewport.height);
+
+ if (curX + viewport.width > xMax)
+ {
+ curX = 0;
+ curY += curH;
+ curH = viewport.height;
+ }
+ if (curY + curH > yMax)
+ {
+ if (allowResize)
+ {
+ LayoutResize();
+ return true;
+ }
+
+ return false;
+ }
+ viewport.x = curX;
+ viewport.y = curY;
+ shadowRequest.atlasViewport = viewport;
+ shadowRequest.resolution = viewport.size;
+ curX += viewport.width;
+ }
+
+ return true;
+ }
+
+ internal bool Layout(bool allowResize = true)
+ {
+ int n = (m_ShadowResolutionRequests != null) ? m_ShadowResolutionRequests.Count : 0;
+ int i = 0;
+ for (; i < m_ShadowResolutionRequests.Count; ++i)
+ {
+ m_SortedRequestsCache[i] = m_ShadowResolutionRequests[i];
+ }
+
+ InsertionSort(m_SortedRequestsCache, 0, i);
+
+ return AtlasLayout(allowResize, m_SortedRequestsCache, requestsCount: i);
+ }
+
+ void LayoutResize()
+ {
+ int index = 0;
+ float currentX = 0;
+ float currentY = 0;
+ float currentMaxY = 0;
+ float currentMaxX = 0;
+
+ // Place shadows in a square shape
+ while (index < m_ShadowResolutionRequests.Count)
+ {
+ float y = 0;
+ float currentMaxXCache = currentMaxX;
+ do
+ {
+ Rect r = new Rect(Vector2.zero, m_ShadowResolutionRequests[index].resolution);
+ r.x = currentMaxX;
+ r.y = y;
+ y += r.height;
+ currentY = Mathf.Max(currentY, y);
+ currentMaxXCache = Mathf.Max(currentMaxXCache, currentMaxX + r.width);
+ m_ShadowResolutionRequests[index].atlasViewport = r;
+ index++;
+ } while (y < currentMaxY && index < m_ShadowResolutionRequests.Count);
+ currentMaxY = Mathf.Max(currentMaxY, currentY);
+ currentMaxX = currentMaxXCache;
+ if (index >= m_ShadowResolutionRequests.Count)
+ continue;
+ float x = 0;
+ float currentMaxYCache = currentMaxY;
+ do
+ {
+ Rect r = new Rect(Vector2.zero, m_ShadowResolutionRequests[index].resolution);
+ r.x = x;
+ r.y = currentMaxY;
+ x += r.width;
+ currentX = Mathf.Max(currentX, x);
+ currentMaxYCache = Mathf.Max(currentMaxYCache, currentMaxY + r.height);
+ m_ShadowResolutionRequests[index].atlasViewport = r;
+ index++;
+ } while (x < currentMaxX && index < m_ShadowResolutionRequests.Count);
+ currentMaxX = Mathf.Max(currentMaxX, currentX);
+ currentMaxY = currentMaxYCache;
+ }
+
+ float maxResolution = Math.Max(currentMaxX, currentMaxY);
+ Vector4 scale = new Vector4(width / maxResolution, height / maxResolution, width / maxResolution, height / maxResolution);
+ m_RcpScaleFactor = Mathf.Min(scale.x, scale.y);
+
+ // Scale down every shadow rects to fit with the current atlas size
+ foreach (var r in m_ShadowResolutionRequests)
+ {
+ Vector4 s = new Vector4(r.atlasViewport.x, r.atlasViewport.y, r.atlasViewport.width, r.atlasViewport.height);
+ Vector4 reScaled = Vector4.Scale(s, scale);
+
+ r.atlasViewport = new Rect(reScaled.x, reScaled.y, reScaled.z, reScaled.w);
+ r.resolution = r.atlasViewport.size;
+ }
+ }
+
+ public void DisplayAtlas(RTHandle atlasTexture, CommandBuffer cmd, Material debugMaterial, Rect atlasViewport, float screenX, float screenY, float screenSizeX, float screenSizeY, float minValue, float maxValue, MaterialPropertyBlock mpb)
+ {
+ base.DisplayAtlas(atlasTexture, cmd, debugMaterial, atlasViewport, screenX, screenY, screenSizeX, screenSizeY, minValue, maxValue, mpb, m_RcpScaleFactor);
+ }
+
+ public override void Clear()
+ {
+ base.Clear();
+ m_ShadowResolutionRequests.Clear();
+ }
+ }
+}
+
diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDDynamicShadowAtlas.cs.meta b/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDDynamicShadowAtlas.cs.meta
new file mode 100644
index 00000000000..462d99d9be8
--- /dev/null
+++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDDynamicShadowAtlas.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 163a41e10a94f234f833ed7a4fb29a63
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDShadowAlgorithms.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDShadowAlgorithms.hlsl
index d52b1c8d43f..09a6f0ea95c 100644
--- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDShadowAlgorithms.hlsl
+++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDShadowAlgorithms.hlsl
@@ -7,15 +7,15 @@
// Keep in sync with both HDShadowManager::GetDirectionalShadowAlgorithm() and GetPunctualFilterWidthInTexels() in C# as well!
#ifdef SHADOW_LOW
-#define PUNCTUAL_FILTER_ALGORITHM(sd, posSS, posTC, tex, samp, bias) SampleShadow_PCF_Tent_3x3(_ShadowAtlasSize.zwxy, posTC, tex, samp, bias)
+#define PUNCTUAL_FILTER_ALGORITHM(sd, posSS, posTC, tex, samp, bias) SampleShadow_PCF_Tent_3x3(sd.isInCachedAtlas ? _CachedShadowAtlasSize.zwxy : _ShadowAtlasSize.zwxy, posTC, tex, samp, bias)
#define DIRECTIONAL_FILTER_ALGORITHM(sd, posSS, posTC, tex, samp, bias) SampleShadow_PCF_Tent_5x5(_CascadeShadowAtlasSize.zwxy, posTC, tex, samp, bias)
#elif defined(SHADOW_MEDIUM)
-#define PUNCTUAL_FILTER_ALGORITHM(sd, posSS, posTC, tex, samp, bias) SampleShadow_PCF_Tent_5x5(_ShadowAtlasSize.zwxy, posTC, tex, samp, bias)
+#define PUNCTUAL_FILTER_ALGORITHM(sd, posSS, posTC, tex, samp, bias) SampleShadow_PCF_Tent_5x5(sd.isInCachedAtlas ? _CachedShadowAtlasSize.zwxy : _ShadowAtlasSize.zwxy, posTC, tex, samp, bias)
#define DIRECTIONAL_FILTER_ALGORITHM(sd, posSS, posTC, tex, samp, bias) SampleShadow_PCF_Tent_7x7(_CascadeShadowAtlasSize.zwxy, posTC, tex, samp, bias)
// Note: currently quality settings for PCSS need to be expose in UI and is control in HDLightUI.cs file IsShadowSettings
#elif defined(SHADOW_HIGH)
-#define PUNCTUAL_FILTER_ALGORITHM(sd, posSS, posTC, tex, samp, bias) SampleShadow_PCSS(posTC, posSS, sd.shadowMapSize.xy * _ShadowAtlasSize.zw, sd.atlasOffset, sd.shadowFilterParams0.x, sd.shadowFilterParams0.w, asint(sd.shadowFilterParams0.y), asint(sd.shadowFilterParams0.z), tex, samp, s_point_clamp_sampler, bias, sd.zBufferParam, true)
-#define DIRECTIONAL_FILTER_ALGORITHM(sd, posSS, posTC, tex, samp, bias) SampleShadow_PCSS(posTC, posSS, sd.shadowMapSize.xy * _CascadeShadowAtlasSize.zw, sd.atlasOffset, sd.shadowFilterParams0.x, sd.shadowFilterParams0.w, asint(sd.shadowFilterParams0.y), asint(sd.shadowFilterParams0.z), tex, samp, s_point_clamp_sampler, bias, sd.zBufferParam, false)
+#define PUNCTUAL_FILTER_ALGORITHM(sd, posSS, posTC, tex, samp, bias) SampleShadow_PCSS(posTC, posSS, sd.shadowMapSize.xy * (sd.isInCachedAtlas ? _CachedShadowAtlasSize.zw : _ShadowAtlasSize.zw), sd.atlasOffset, sd.shadowFilterParams0.x, sd.shadowFilterParams0.w, asint(sd.shadowFilterParams0.y), asint(sd.shadowFilterParams0.z), tex, samp, s_point_clamp_sampler, bias, sd.zBufferParam, true, (sd.isInCachedAtlas ? _CachedShadowAtlasSize.xz : _ShadowAtlasSize.xz))
+#define DIRECTIONAL_FILTER_ALGORITHM(sd, posSS, posTC, tex, samp, bias) SampleShadow_PCSS(posTC, posSS, sd.shadowMapSize.xy * _CascadeShadowAtlasSize.zw, sd.atlasOffset, sd.shadowFilterParams0.x, sd.shadowFilterParams0.w, asint(sd.shadowFilterParams0.y), asint(sd.shadowFilterParams0.z), tex, samp, s_point_clamp_sampler, bias, sd.zBufferParam, false, _CascadeShadowAtlasSize.xz)
#endif
#ifndef PUNCTUAL_FILTER_ALGORITHM
@@ -122,16 +122,17 @@ float3 EvalShadow_NormalBias(float worldTexelSize, float normalBias, float3 norm
//
float EvalShadow_PunctualDepth(HDShadowData sd, Texture2D tex, SamplerComparisonState samp, float2 positionSS, float3 positionWS, float3 normalWS, float3 L, float L_dist, bool perspective)
{
+ float2 texelSize = sd.isInCachedAtlas ? _CachedShadowAtlasSize.zw : _ShadowAtlasSize.zw;
positionWS = positionWS + sd.cacheTranslationDelta.xyz;
/* bias the world position */
float worldTexelSize = EvalShadow_WorldTexelSize(sd.worldTexelSize, L_dist, true);
float3 normalBias = EvalShadow_NormalBias(worldTexelSize, sd.normalBias, normalWS);
positionWS += normalBias;
/* get shadowmap texcoords */
- float3 posTC = EvalShadow_GetTexcoordsAtlas(sd, _ShadowAtlasSize.zw, positionWS, perspective);
+ float3 posTC = EvalShadow_GetTexcoordsAtlas(sd, texelSize, positionWS, perspective);
/* sample the texture */
// We need to do the check on min/max coordinates because if the shadow spot angle is smaller than the actual cone, then we could have artifacts due to the clamp sampler.
- float2 maxCoord = (sd.shadowMapSize.xy - 0.5f) * _ShadowAtlasSize.zw + sd.atlasOffset;
+ float2 maxCoord = (sd.shadowMapSize.xy - 0.5f) * texelSize + sd.atlasOffset;
float2 minCoord = sd.atlasOffset;
return any(posTC.xy > maxCoord || posTC.xy < minCoord) ? 1.0f : PUNCTUAL_FILTER_ALGORITHM(sd, positionSS, posTC, tex, samp, FIXED_UNIFORM_BIAS);
}
@@ -141,14 +142,16 @@ float EvalShadow_PunctualDepth(HDShadowData sd, Texture2D tex, SamplerComparison
//
float EvalShadow_AreaDepth(HDShadowData sd, Texture2D tex, float2 positionSS, float3 positionWS, float3 normalWS, float3 L, float L_dist, bool perspective)
{
+ float2 texelSize = sd.isInCachedAtlas ? _CachedAreaShadowAtlasSize.zw : _AreaShadowAtlasSize.zw;
+
positionWS = positionWS + sd.cacheTranslationDelta.xyz;
/* get shadowmap texcoords */
- float3 posTC = EvalShadow_GetTexcoordsAtlas(sd, _AreaShadowAtlasSize.zw, positionWS, perspective);
+ float3 posTC = EvalShadow_GetTexcoordsAtlas(sd, texelSize, positionWS, perspective);
int blurPassesScale = (1 + min(4, sd.shadowFilterParams0.w) * 4.0f);// This is needed as blurring might cause some leaks. It might be overclipping, but empirically is a good value.
- float2 maxCoord = (sd.shadowMapSize.xy - 0.5f * blurPassesScale) * _AreaShadowAtlasSize.zw + sd.atlasOffset;
- float2 minCoord = sd.atlasOffset + _AreaShadowAtlasSize.zw * blurPassesScale;
+ float2 maxCoord = (sd.shadowMapSize.xy - 0.5f * blurPassesScale) * texelSize + sd.atlasOffset;
+ float2 minCoord = sd.atlasOffset + texelSize * blurPassesScale;
if (any(posTC.xy > maxCoord || posTC.xy < minCoord))
{
@@ -307,8 +310,10 @@ float EvalShadow_CascadedDepth_Dither(HDShadowContext shadowContext, Texture2D t
// TODO: optimize this using LinearEyeDepth() to avoid having to pass the shadowToWorld matrix
float EvalShadow_SampleClosestDistance_Punctual(HDShadowData sd, Texture2D tex, SamplerState sampl, float3 positionWS, float3 L, float3 lightPositionWS)
{
+ float2 texelSize = sd.isInCachedAtlas ? _CachedShadowAtlasSize.zw : _ShadowAtlasSize.zw;
+
float4 closestNDC = { 0,0,0,1 };
- float2 texelIdx = EvalShadow_GetTexcoordsAtlas(sd, _ShadowAtlasSize.zw, positionWS, closestNDC.xy, true);
+ float2 texelIdx = EvalShadow_GetTexcoordsAtlas(sd, texelSize, positionWS, closestNDC.xy, true);
// sample the shadow map
closestNDC.z = SAMPLE_TEXTURE2D_LOD(tex, sampl, texelIdx, 0).x;
diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDShadowAtlas.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDShadowAtlas.cs
index 2a747077acd..38ceccc8740 100644
--- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDShadowAtlas.cs
+++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDShadowAtlas.cs
@@ -4,28 +4,24 @@
namespace UnityEngine.Rendering.HighDefinition
{
- partial class HDShadowAtlas
+ abstract partial class HDShadowAtlas
{
public enum BlurAlgorithm
- {
+ {
None,
EVSM, // exponential variance shadow maps
IM // Improved Moment shadow maps
}
public RTHandle renderTarget { get { return m_Atlas; } }
- readonly List m_ShadowResolutionRequests = new List();
readonly List m_ShadowRequests = new List();
- readonly List m_ListOfCachedShadowRequests = new List();
-
public int width { get; private set; }
public int height { get; private set; }
RTHandle m_Atlas;
Material m_ClearMaterial;
LightingDebugSettings m_LightingDebugSettings;
- float m_RcpScaleFactor = 1;
FilterMode m_FilterMode;
DepthBits m_DepthBufferBits;
RenderTextureFormat m_Format;
@@ -38,20 +34,10 @@ public enum BlurAlgorithm
RTHandle[] m_AtlasMoments = null;
RTHandle m_IntermediateSummedAreaTexture;
RTHandle m_SummedAreaTexture;
- HDShadowResolutionRequest[] m_SortedRequestsCache;
-
-
- public int frameOfCacheValidity { get; private set; }
- public int atlasShapeID { get; private set; }
- // TODO: This whole caching system needs to be refactored. At the moment there is lots of unecessary data being copied often.
- HDShadowResolutionRequest[] m_CachedResolutionRequests;
- int m_CachedResolutionRequestsCounter = 0;
+ public HDShadowAtlas() { }
- bool m_HasResizedAtlas = false;
- int frameCounter = 0;
-
- public HDShadowAtlas(RenderPipelineResources renderPipelineResources, int width, int height, int atlasShaderID, Material clearMaterial, int maxShadowRequests, BlurAlgorithm blurAlgorithm = BlurAlgorithm.None, FilterMode filterMode = FilterMode.Bilinear, DepthBits depthBufferBits = DepthBits.Depth16, RenderTextureFormat format = RenderTextureFormat.Shadowmap, string name = "")
+ public virtual void InitAtlas(RenderPipelineResources renderPipelineResources, int width, int height, int atlasShaderID, Material clearMaterial, int maxShadowRequests, HDShadowInitParameters initParams, BlurAlgorithm blurAlgorithm = BlurAlgorithm.None, FilterMode filterMode = FilterMode.Bilinear, DepthBits depthBufferBits = DepthBits.Depth16, RenderTextureFormat format = RenderTextureFormat.Shadowmap, string name = "")
{
this.width = width;
this.height = height;
@@ -64,16 +50,14 @@ public HDShadowAtlas(RenderPipelineResources renderPipelineResources, int width,
m_BlurAlgorithm = blurAlgorithm;
m_RenderPipelineResources = renderPipelineResources;
- m_SortedRequestsCache = new HDShadowResolutionRequest[Mathf.CeilToInt(maxShadowRequests*1.5f)];
- m_CachedResolutionRequests = new HDShadowResolutionRequest[maxShadowRequests];
- for(int i=0; i= 0) && ((curr.resolution.x > array[j].resolution.x) ||
- (curr.resolution.y > array[j].resolution.y)))
- {
- array[j + 1] = array[j];
- j--;
- }
-
- array[j + 1] = curr;
- i++;
- }
- }
-
- internal HDShadowResolutionRequest GetCachedRequest(int cachedIndex)
- {
- if (cachedIndex < 0 || cachedIndex >= m_ListOfCachedShadowRequests.Count)
- return null;
-
- return m_ListOfCachedShadowRequests[cachedIndex];
- }
-
- internal bool HasResizedThisFrame()
- {
- return m_HasResizedAtlas;
- }
-
- internal void MarkCulledShadowMapAsEmptySlots()
- {
- for(int i=0; i 0)
- {
- m_ListOfCachedShadowRequests[i].emptyRequest = true;
- }
- }
-
- frameCounter++;
- }
-
- internal void PruneDeadCachedLightSlots()
- {
- m_ListOfCachedShadowRequests.RemoveAll(x => (x.emptyRequest));
- frameOfCacheValidity = 0; // Invalidate cached data.
- }
-
- internal void MarkCachedShadowSlotAsEmpty(int lightID)
- {
- var subList = m_ListOfCachedShadowRequests.FindAll(x => x.lightID == lightID);
- for (int i = 0; i < subList.Count; ++i)
- {
- subList[i].emptyRequest = true;
- }
- }
-
- internal int RegisterCachedLight(HDShadowResolutionRequest request)
- {
-
- // Since we are starting caching light resolution requests, it means that data cached from now on will be valid.
- frameOfCacheValidity++;
-
- // If it is already registered, we do nothing.
- int shadowIndex = -1;
- for(int i=0; i< m_ListOfCachedShadowRequests.Count; ++i)
- {
- if(!m_ListOfCachedShadowRequests[i].emptyRequest && m_ListOfCachedShadowRequests[i].lightID == request.lightID && m_ListOfCachedShadowRequests[i].indexInLight == request.indexInLight)
- {
- shadowIndex = i;
- break;
- }
- }
-
- if (shadowIndex == -1)
- {
- // First we search if we have a hole we can fill with it.
- float resolutionOfNewLight = request.atlasViewport.width;
- request.lastFrameActive = frameCounter;
-
- int holeWithRightSize = -1;
- for (int i = 0; i < m_ListOfCachedShadowRequests.Count; ++i)
- {
- var currReq = m_ListOfCachedShadowRequests[i];
- if (currReq.emptyRequest && // Is empty
- request.atlasViewport.width <= currReq.atlasViewport.width && // fits the request
- (currReq.atlasViewport.width - request.atlasViewport.width) <= currReq.atlasViewport.width * 0.1f) // but is not much smaller.
- {
- holeWithRightSize = i;
- break;
- }
- }
-
- if (holeWithRightSize >= 0)
- {
- m_ListOfCachedShadowRequests[holeWithRightSize] = request.ShallowCopy();
- return holeWithRightSize;
- }
- else
- {
-
- // We need to resort the list, so we use the occasion to reset the pool. This feels suboptimal, but it is the easiest way to comply with the pooling system.
- // TODO: Make this cleaner and more efficient.
- m_CachedResolutionRequestsCounter = 0;
- for (int i=0; i xMax)
- {
- curX = 0;
- curY += curH;
- curH = viewport.height;
- }
- if (curY + curH > yMax)
- {
- if(enteredWithPrunedCachedList)
- {
- // We need to resize. We invalidate the data and clear stored list of cached.
- frameOfCacheValidity = 0;
- m_ListOfCachedShadowRequests.Clear();
- // Since we emptied the cached list, we can start from scratch in the pool
- m_CachedResolutionRequestsCounter = 0;
-
- if (allowResize)
- {
- LayoutResize();
- m_HasResizedAtlas = true;
- return true;
- }
-
- return false;
- }
- else
- {
- // We can still prune
- PruneDeadCachedLightSlots();
- // Remove cached slots from the currently sorted list (instead of rebuilding it).
- // Since it is ordered, the order post deletion is guaranteed.
- int newIndex = 0;
- for(int j=0; j= m_ShadowResolutionRequests.Count)
- continue;
- float x = 0;
- float currentMaxYCache = currentMaxY;
- do
- {
- Rect r = new Rect(Vector2.zero, m_ShadowResolutionRequests[index].resolution);
- r.x = x;
- r.y = currentMaxY;
- x += r.width;
- currentX = Mathf.Max(currentX, x);
- currentMaxYCache = Mathf.Max(currentMaxYCache, currentMaxY + r.height);
- m_ShadowResolutionRequests[index].atlasViewport = r;
- index++;
- } while (x < currentMaxX && index < m_ShadowResolutionRequests.Count);
- currentMaxX = Mathf.Max(currentMaxX, currentX);
- currentMaxY = currentMaxYCache;
- }
-
- float maxResolution = Math.Max(currentMaxX, currentMaxY);
- Vector4 scale = new Vector4(width / maxResolution, height / maxResolution, width / maxResolution, height / maxResolution);
- m_RcpScaleFactor = Mathf.Min(scale.x, scale.y);
-
- // Scale down every shadow rects to fit with the current atlas size
- foreach (var r in m_ShadowResolutionRequests)
- {
- Vector4 s = new Vector4(r.atlasViewport.x, r.atlasViewport.y, r.atlasViewport.width, r.atlasViewport.height);
- Vector4 reScaled = Vector4.Scale(s, scale);
-
- r.atlasViewport = new Rect(reScaled.x, reScaled.y, reScaled.z, reScaled.w);
- r.resolution = r.atlasViewport.size;
- }
-
- atlasShapeID++;
- }
-
public void RenderShadows(CullingResults cullResults, in ShaderVariablesGlobal globalCB, FrameSettings frameSettings, ScriptableRenderContext renderContext, CommandBuffer cmd)
{
if (m_ShadowRequests.Count == 0)
@@ -578,6 +256,9 @@ unsafe static void EVSMBlurMoments( RenderShadowsParameters parameters,
int requestIdx = 0;
foreach (var shadowRequest in parameters.shadowRequests)
{
+ if (shadowRequest.shouldUseCachedShadow)
+ continue;
+
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.RenderEVSMShadowMapsBlur)))
{
int downsampledWidth = Mathf.CeilToInt(shadowRequest.atlasViewport.width * 0.5f);
@@ -699,7 +380,7 @@ static void IMBlurMoment( RenderShadowsParameters parameters,
}
}
- public void DisplayAtlas(RTHandle atlasTexture, CommandBuffer cmd, Material debugMaterial, Rect atlasViewport, float screenX, float screenY, float screenSizeX, float screenSizeY, float minValue, float maxValue, MaterialPropertyBlock mpb)
+ public virtual void DisplayAtlas(RTHandle atlasTexture, CommandBuffer cmd, Material debugMaterial, Rect atlasViewport, float screenX, float screenY, float screenSizeX, float screenSizeY, float minValue, float maxValue, MaterialPropertyBlock mpb, float scaleFactor = 1)
{
if (atlasTexture == null)
return;
@@ -712,14 +393,13 @@ public void DisplayAtlas(RTHandle atlasTexture, CommandBuffer cmd, Material debu
mpb.SetTexture("_AtlasTexture", atlasTexture);
mpb.SetVector("_TextureScaleBias", scaleBias);
mpb.SetVector("_ValidRange", validRange);
- mpb.SetFloat("_RcpGlobalScaleFactor", m_RcpScaleFactor);
+ mpb.SetFloat("_RcpGlobalScaleFactor", scaleFactor);
cmd.SetViewport(new Rect(screenX, screenY, screenSizeX, screenSizeY));
cmd.DrawProcedural(Matrix4x4.identity, debugMaterial, debugMaterial.FindPass("RegularShadow"), MeshTopology.Triangles, 3, 1, mpb);
}
- public void Clear()
+ public virtual void Clear()
{
- m_ShadowResolutionRequests.Clear();
m_ShadowRequests.Clear();
}
diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDShadowContext.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDShadowContext.hlsl
index 3dfe1536d2a..fa5b99b316e 100644
--- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDShadowContext.hlsl
+++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDShadowContext.hlsl
@@ -18,8 +18,10 @@ struct HDShadowContext
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDShadowAlgorithms.hlsl"
TEXTURE2D(_ShadowmapAtlas);
+TEXTURE2D(_CachedShadowmapAtlas);
TEXTURE2D(_ShadowmapCascadeAtlas);
TEXTURE2D(_ShadowmapAreaAtlas);
+TEXTURE2D(_CachedAreaLightShadowmapAtlas);
StructuredBuffer _HDShadowDatas;
// Only the first element is used since we only support one directional light
diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDShadowManager.RenderGraph.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDShadowManager.RenderGraph.cs
index 52738760492..b63a145def5 100644
--- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDShadowManager.RenderGraph.cs
+++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDShadowManager.RenderGraph.cs
@@ -6,8 +6,10 @@ namespace UnityEngine.Rendering.HighDefinition
internal struct ShadowResult
{
public TextureHandle punctualShadowResult;
+ public TextureHandle cachedPunctualShadowResult;
public TextureHandle directionalShadowResult;
public TextureHandle areaShadowResult;
+ public TextureHandle cachedAreaShadowResult;
}
partial class HDShadowManager
@@ -22,6 +24,10 @@ internal static ShadowResult ReadShadowResult(in ShadowResult shadowResult, Rend
result.directionalShadowResult = builder.ReadTexture(shadowResult.directionalShadowResult);
if (shadowResult.areaShadowResult.IsValid())
result.areaShadowResult = builder.ReadTexture(shadowResult.areaShadowResult);
+ if (shadowResult.cachedPunctualShadowResult.IsValid())
+ result.cachedPunctualShadowResult = builder.ReadTexture(shadowResult.cachedPunctualShadowResult);
+ if (shadowResult.cachedAreaShadowResult.IsValid())
+ result.cachedAreaShadowResult = builder.ReadTexture(shadowResult.cachedAreaShadowResult);
return result;
}
@@ -35,6 +41,8 @@ internal ShadowResult RenderShadows(RenderGraph renderGraph, in ShaderVariablesG
result.punctualShadowResult = m_Atlas.RenderShadows(renderGraph, cullResults, globalCB, hdCamera.frameSettings, "Punctual Lights Shadows rendering");
result.directionalShadowResult = m_CascadeAtlas.RenderShadows(renderGraph, cullResults, globalCB, hdCamera.frameSettings, "Directional Light Shadows rendering");
result.areaShadowResult = m_AreaLightShadowAtlas.RenderShadows(renderGraph, cullResults, globalCB, hdCamera.frameSettings, "Area Light Shadows rendering");
+ result.cachedPunctualShadowResult = cachedShadowManager.punctualShadowAtlas.RenderShadows(renderGraph, cullResults, globalCB, hdCamera.frameSettings, "Cached Punctual Lights Shadows rendering");
+ result.cachedAreaShadowResult = cachedShadowManager.areaShadowAtlas.RenderShadows(renderGraph, cullResults, globalCB, hdCamera.frameSettings, "Cached Area Lights Shadows rendering");
}
// TODO RENDERGRAPH
diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDShadowManager.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDShadowManager.cs
index ef50ee78de8..1410120178f 100644
--- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDShadowManager.cs
+++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDShadowManager.cs
@@ -33,7 +33,7 @@ struct HDShadowData
public Vector4 shadowFilterParams0;
public Vector3 cacheTranslationDelta;
- public float _pad0;
+ public float isInCachedAtlas;
public Matrix4x4 shadowToWorld;
}
@@ -101,6 +101,8 @@ class HDShadowRequest
public bool shouldUseCachedShadow = false;
public HDShadowData cachedShadowData;
+
+ public bool isInCachedAtlas;
}
enum DirectionalShadowAlgorithm
@@ -158,6 +160,8 @@ internal static HDShadowAtlasInitParams GetDefault()
directionalShadowsDepthBits = k_DefaultShadowMapDepthBits,
punctualLightShadowAtlas = HDShadowAtlasInitParams.GetDefault(),
areaLightShadowAtlas = HDShadowAtlasInitParams.GetDefault(),
+ cachedPunctualLightShadowAtlas = 2048,
+ cachedAreaLightShadowAtlas = 1024,
shadowResolutionDirectional = new IntScalableSetting(new []{ 256, 512, 1024, 2048 }, ScalableSettingSchemaId.With4Levels),
shadowResolutionArea = new IntScalableSetting(new []{ 256, 512, 1024, 2048 }, ScalableSettingSchemaId.With4Levels),
shadowResolutionPunctual = new IntScalableSetting(new []{ 256, 512, 1024, 2048 }, ScalableSettingSchemaId.With4Levels),
@@ -189,6 +193,12 @@ internal static HDShadowAtlasInitParams GetDefault()
/// Initialization parameters for area shadows atlas.
public HDShadowAtlasInitParams areaLightShadowAtlas;
+ /// Resolution for the punctual lights cached shadow maps atlas.
+ public int cachedPunctualLightShadowAtlas;
+
+ /// Resolution for the area lights cached shadow maps atlas.
+ public int cachedAreaLightShadowAtlas;
+
/// Shadow scalable resolution for directional lights.
public IntScalableSetting shadowResolutionDirectional;
/// Shadow scalable resolution for point lights.
@@ -217,13 +227,6 @@ class HDShadowResolutionRequest
public Vector2 resolution;
public ShadowMapType shadowMapType;
- /* Data for cached shadows */
- public int lightID;
- public int indexInLight = 0;
- public int lastFrameActive = 0;
- public bool emptyRequest = false;
- public bool hasBeenStoredInCachedList = false;
-
public HDShadowResolutionRequest ShallowCopy()
{
return (HDShadowResolutionRequest)this.MemberwiseClone();
@@ -248,9 +251,9 @@ partial class HDShadowManager : IDisposable
ComputeBuffer m_DirectionalShadowDataBuffer;
// The two shadowmaps atlases we uses, one for directional cascade (without resize) and the second for the rest of the shadows
- HDShadowAtlas m_CascadeAtlas;
- HDShadowAtlas m_Atlas;
- HDShadowAtlas m_AreaLightShadowAtlas;
+ HDDynamicShadowAtlas m_CascadeAtlas;
+ HDDynamicShadowAtlas m_Atlas;
+ HDDynamicShadowAtlas m_AreaLightShadowAtlas;
int m_MaxShadowRequests;
int m_ShadowRequestCount;
@@ -262,40 +265,48 @@ partial class HDShadowManager : IDisposable
private static HDShadowManager s_Instance = new HDShadowManager();
public static HDShadowManager instance { get { return s_Instance; } }
+ public static HDCachedShadowManager cachedShadowManager { get { return HDCachedShadowManager.instance; } }
private HDShadowManager()
{}
- public void InitShadowManager(RenderPipelineResources renderPipelineResources, DepthBits directionalShadowDepthBits,
- HDShadowInitParameters.HDShadowAtlasInitParams punctualLightAtlasInfo, HDShadowInitParameters.HDShadowAtlasInitParams areaLightAtlasInfo, int maxShadowRequests, Shader clearShader)
+ public void InitShadowManager(RenderPipelineResources renderPipelineResources, HDShadowInitParameters initParams, Shader clearShader)
{
m_ClearShadowMaterial = CoreUtils.CreateEngineMaterial(clearShader);
// Prevent the list from resizing their internal container when we add shadow requests
- m_ShadowDatas.Capacity = Math.Max(maxShadowRequests, m_ShadowDatas.Capacity);
- m_ShadowResolutionRequests = new HDShadowResolutionRequest[maxShadowRequests];
- m_ShadowRequests = new HDShadowRequest[maxShadowRequests];
+ m_ShadowDatas.Capacity = Math.Max(initParams.maxShadowRequests, m_ShadowDatas.Capacity);
+ m_ShadowResolutionRequests = new HDShadowResolutionRequest[initParams.maxShadowRequests];
+ m_ShadowRequests = new HDShadowRequest[initParams.maxShadowRequests];
m_CachedDirectionalShadowData = new HDDirectionalShadowData[1]; // we only support directional light shadow
- for (int i = 0; i < maxShadowRequests; i++)
+ for (int i = 0; i < initParams.maxShadowRequests; i++)
{
m_ShadowResolutionRequests[i] = new HDShadowResolutionRequest();
}
// The cascade atlas will be allocated only if there is a directional light
- m_Atlas = new HDShadowAtlas(renderPipelineResources, punctualLightAtlasInfo.shadowAtlasResolution, punctualLightAtlasInfo.shadowAtlasResolution, HDShaderIDs._ShadowmapAtlas, m_ClearShadowMaterial, maxShadowRequests, depthBufferBits: punctualLightAtlasInfo.shadowAtlasDepthBits, name: "Shadow Map Atlas");
+ m_Atlas = new HDDynamicShadowAtlas(renderPipelineResources, initParams.punctualLightShadowAtlas.shadowAtlasResolution, initParams.punctualLightShadowAtlas.shadowAtlasResolution,
+ HDShaderIDs._ShadowmapAtlas, m_ClearShadowMaterial, initParams.maxShadowRequests, initParams, depthBufferBits: initParams.punctualLightShadowAtlas.shadowAtlasDepthBits, name: "Shadow Map Atlas");
// Cascade atlas render texture will only be allocated if there is a shadow casting directional light
HDShadowAtlas.BlurAlgorithm cascadeBlur = GetDirectionalShadowAlgorithm() == DirectionalShadowAlgorithm.IMS ? HDShadowAtlas.BlurAlgorithm.IM : HDShadowAtlas.BlurAlgorithm.None;
- m_CascadeAtlas = new HDShadowAtlas(renderPipelineResources, 1, 1, HDShaderIDs._ShadowmapCascadeAtlas, m_ClearShadowMaterial, maxShadowRequests, cascadeBlur, depthBufferBits: directionalShadowDepthBits, name: "Cascade Shadow Map Atlas");
+ m_CascadeAtlas = new HDDynamicShadowAtlas(renderPipelineResources, 1, 1, HDShaderIDs._ShadowmapCascadeAtlas, m_ClearShadowMaterial, initParams.maxShadowRequests, initParams, cascadeBlur, depthBufferBits: initParams.directionalShadowsDepthBits, name: "Cascade Shadow Map Atlas");
if (ShaderConfig.s_AreaLights == 1)
- m_AreaLightShadowAtlas = new HDShadowAtlas(renderPipelineResources, areaLightAtlasInfo.shadowAtlasResolution, areaLightAtlasInfo.shadowAtlasResolution, HDShaderIDs._ShadowmapAreaAtlas, m_ClearShadowMaterial, maxShadowRequests, HDShadowAtlas.BlurAlgorithm.EVSM, depthBufferBits: areaLightAtlasInfo.shadowAtlasDepthBits, name: "Area Light Shadow Map Atlas");
+ m_AreaLightShadowAtlas = new HDDynamicShadowAtlas(renderPipelineResources, initParams.areaLightShadowAtlas.shadowAtlasResolution, initParams.areaLightShadowAtlas.shadowAtlasResolution,
+ HDShaderIDs._ShadowmapAreaAtlas, m_ClearShadowMaterial, initParams.maxShadowRequests, initParams, HDShadowAtlas.BlurAlgorithm.EVSM, depthBufferBits: initParams.areaLightShadowAtlas.shadowAtlasDepthBits, name: "Area Light Shadow Map Atlas");
- m_ShadowDataBuffer = new ComputeBuffer(maxShadowRequests, System.Runtime.InteropServices.Marshal.SizeOf(typeof(HDShadowData)));
+ m_ShadowDataBuffer = new ComputeBuffer(initParams.maxShadowRequests, System.Runtime.InteropServices.Marshal.SizeOf(typeof(HDShadowData)));
m_DirectionalShadowDataBuffer = new ComputeBuffer(1, System.Runtime.InteropServices.Marshal.SizeOf(typeof(HDDirectionalShadowData)));
- m_MaxShadowRequests = maxShadowRequests;
+ m_MaxShadowRequests = initParams.maxShadowRequests;
+
+ cachedShadowManager.InitPunctualShadowAtlas(renderPipelineResources, initParams.cachedPunctualLightShadowAtlas, initParams.cachedPunctualLightShadowAtlas,
+ HDShaderIDs._CachedShadowmapAtlas, m_ClearShadowMaterial, initParams.maxShadowRequests, initParams: initParams, depthBufferBits: initParams.punctualLightShadowAtlas.shadowAtlasDepthBits, name: "Cached Shadow Map Atlas");
+ if (ShaderConfig.s_AreaLights == 1)
+ cachedShadowManager.InitAreaLightShadowAtlas(renderPipelineResources, initParams.cachedAreaLightShadowAtlas, initParams.cachedAreaLightShadowAtlas,
+ HDShaderIDs._CachedAreaLightShadowmapAtlas, m_ClearShadowMaterial, initParams.maxShadowRequests, initParams: initParams, HDShadowAtlas.BlurAlgorithm.EVSM, depthBufferBits: initParams.areaLightShadowAtlas.shadowAtlasDepthBits, name: "Cached Area Light Shadow Map Atlas");
}
public void InitializeNonRenderGraphResources()
@@ -340,8 +351,12 @@ public void UpdateShaderVariablesGlobalCB(ref ShaderVariablesGlobal cb)
cb._CascadeShadowCount = (uint)(m_CascadeCount + 1);
cb._ShadowAtlasSize = new Vector4(m_Atlas.width, m_Atlas.height, 1.0f / m_Atlas.width, 1.0f / m_Atlas.height);
cb._CascadeShadowAtlasSize = new Vector4(m_CascadeAtlas.width, m_CascadeAtlas.height, 1.0f / m_CascadeAtlas.width, 1.0f / m_CascadeAtlas.height);
+ cb._CachedShadowAtlasSize = new Vector4(cachedShadowManager.punctualShadowAtlas.width, cachedShadowManager.punctualShadowAtlas.height, 1.0f / cachedShadowManager.punctualShadowAtlas.width, 1.0f / cachedShadowManager.punctualShadowAtlas.height);
if (ShaderConfig.s_AreaLights == 1)
+ {
cb._AreaShadowAtlasSize = new Vector4(m_AreaLightShadowAtlas.width, m_AreaLightShadowAtlas.height, 1.0f / m_AreaLightShadowAtlas.width, 1.0f / m_AreaLightShadowAtlas.height);
+ cb._CachedAreaShadowAtlasSize = new Vector4(cachedShadowManager.areaShadowAtlas.width, cachedShadowManager.areaShadowAtlas.height, 1.0f / cachedShadowManager.areaShadowAtlas.width, 1.0f / cachedShadowManager.areaShadowAtlas.height);
+ }
}
public void UpdateDirectionalShadowResolution(int resolution, int cascadeCount)
@@ -356,161 +371,53 @@ public void UpdateDirectionalShadowResolution(int resolution, int cascadeCount)
m_CascadeAtlas.UpdateSize(atlasResolution);
}
- internal int ReserveShadowResolutions(Vector2 resolution, ShadowMapType shadowMapType, int lightID, int index, bool canBeCached, out int cachedRequestIdx)
+ internal int ReserveShadowResolutions(Vector2 resolution, ShadowMapType shadowMapType, int lightID, int index, bool isDynamicShadow)
{
- cachedRequestIdx = -1;
if (m_ShadowRequestCount >= m_MaxShadowRequests)
{
Debug.LogWarning("Max shadow requests count reached, dropping all exceeding requests. You can increase this limit by changing the max requests in the HDRP asset");
return -1;
}
- int cachedIndex = -1;
-
- m_ShadowResolutionRequests[m_ShadowResolutionRequestCounter].resolution = resolution;
m_ShadowResolutionRequests[m_ShadowResolutionRequestCounter].shadowMapType = shadowMapType;
- m_ShadowResolutionRequests[m_ShadowResolutionRequestCounter].lightID = lightID;
- m_ShadowResolutionRequests[m_ShadowResolutionRequestCounter].emptyRequest = false;
- m_ShadowResolutionRequests[m_ShadowResolutionRequestCounter].indexInLight = index;
- m_ShadowResolutionRequests[m_ShadowResolutionRequestCounter].atlasViewport.width = resolution.x;
- m_ShadowResolutionRequests[m_ShadowResolutionRequestCounter].atlasViewport.height = resolution.y;
- if (canBeCached)
- {
- m_ShadowResolutionRequests[m_ShadowResolutionRequestCounter].hasBeenStoredInCachedList = true;
- }
- else
- {
- m_ShadowResolutionRequests[m_ShadowResolutionRequestCounter].hasBeenStoredInCachedList = false;
- }
- switch (shadowMapType)
+ // Note: for cached shadows we manage the resolution requests directly on the CachedShadowAtlas as they need special handling. We however keep incrementing the counter for two reasons:
+ // - Maintain the limit of m_MaxShadowRequests
+ // - Avoid to refactor other parts that the shadow manager that get requests indices from here.
+
+ if (isDynamicShadow)
{
- case ShadowMapType.PunctualAtlas:
- if(canBeCached)
- {
- cachedIndex = m_Atlas.RegisterCachedLight(m_ShadowResolutionRequests[m_ShadowResolutionRequestCounter]);
- }
- m_Atlas.ReserveResolution(m_ShadowResolutionRequests[m_ShadowResolutionRequestCounter]);
- break;
- case ShadowMapType.AreaLightAtlas:
- if(canBeCached)
- {
- cachedIndex = m_AreaLightShadowAtlas.RegisterCachedLight(m_ShadowResolutionRequests[m_ShadowResolutionRequestCounter]);
- }
+ m_ShadowResolutionRequests[m_ShadowResolutionRequestCounter].resolution = resolution;
+ m_ShadowResolutionRequests[m_ShadowResolutionRequestCounter].atlasViewport.width = resolution.x;
+ m_ShadowResolutionRequests[m_ShadowResolutionRequestCounter].atlasViewport.height = resolution.y;
- m_AreaLightShadowAtlas.ReserveResolution(m_ShadowResolutionRequests[m_ShadowResolutionRequestCounter]);
- break;
- case ShadowMapType.CascadedDirectional:
- m_CascadeAtlas.ReserveResolution(m_ShadowResolutionRequests[m_ShadowResolutionRequestCounter]);
- break;
+ switch (shadowMapType)
+ {
+ case ShadowMapType.PunctualAtlas:
+ m_Atlas.ReserveResolution(m_ShadowResolutionRequests[m_ShadowResolutionRequestCounter]);
+ break;
+ case ShadowMapType.AreaLightAtlas:
+ m_AreaLightShadowAtlas.ReserveResolution(m_ShadowResolutionRequests[m_ShadowResolutionRequestCounter]);
+ break;
+ case ShadowMapType.CascadedDirectional:
+ m_CascadeAtlas.ReserveResolution(m_ShadowResolutionRequests[m_ShadowResolutionRequestCounter]);
+ break;
+ }
}
+
m_ShadowResolutionRequestCounter++;
m_ShadowRequestCount = m_ShadowResolutionRequestCounter;
- cachedRequestIdx = cachedIndex;
return m_ShadowResolutionRequestCounter - 1;
}
- internal void MarkCachedShadowSlotsAsEmpty(ShadowMapType shadowMapType, int lightID)
- {
- switch (shadowMapType)
- {
- case ShadowMapType.PunctualAtlas:
- if(m_Atlas != null)
- m_Atlas.MarkCachedShadowSlotAsEmpty(lightID);
- break;
- case ShadowMapType.AreaLightAtlas:
- if (m_AreaLightShadowAtlas != null)
- m_AreaLightShadowAtlas.MarkCachedShadowSlotAsEmpty(lightID);
- break;
- }
- }
-
- internal void CheckForCulledCachedShadows()
- {
- m_Atlas.MarkCulledShadowMapAsEmptySlots();
- if (ShaderConfig.s_AreaLights == 1)
- m_AreaLightShadowAtlas.MarkCulledShadowMapAsEmptySlots();
- }
- internal bool CachedDataIsValid(ShadowMapType type)
- {
- const int thresholdOfValidFrames = 30;
- switch (type)
- {
- case ShadowMapType.PunctualAtlas:
- return m_Atlas.frameOfCacheValidity > thresholdOfValidFrames;
- case ShadowMapType.AreaLightAtlas:
- return m_AreaLightShadowAtlas.frameOfCacheValidity > thresholdOfValidFrames;
- }
-
- return false;
- }
-
- internal void PruneEmptyCachedSlots(ShadowMapType type)
+ internal HDShadowResolutionRequest GetResolutionRequest(int index)
{
- switch (type)
- {
- case ShadowMapType.PunctualAtlas:
- if(m_Atlas != null)
- m_Atlas.PruneDeadCachedLightSlots();
- break;
- case ShadowMapType.AreaLightAtlas:
- if (m_AreaLightShadowAtlas != null)
- m_AreaLightShadowAtlas.PruneDeadCachedLightSlots();
- break;
- default:
- break;
- }
-
- }
-
- internal int GetAtlasShapeID(ShadowMapType type)
- {
- switch (type)
- {
- case ShadowMapType.PunctualAtlas:
- return m_Atlas.atlasShapeID;
- case ShadowMapType.AreaLightAtlas:
- return m_AreaLightShadowAtlas.atlasShapeID;
- }
- return -1;
- }
-
- internal bool AtlasHasResized(ShadowMapType type)
- {
- switch (type)
- {
- case ShadowMapType.PunctualAtlas:
- return m_Atlas.HasResizedThisFrame();
- case ShadowMapType.AreaLightAtlas:
- return m_AreaLightShadowAtlas.HasResizedThisFrame();
- }
-
- return false;
- }
-
- internal HDShadowResolutionRequest GetResolutionRequest(ShadowMapType type, bool cachedShadow, int index)
- {
- if(cachedShadow)
- {
- switch (type)
- {
- case ShadowMapType.PunctualAtlas:
- return m_Atlas.GetCachedRequest(index);
- case ShadowMapType.AreaLightAtlas:
- return m_AreaLightShadowAtlas.GetCachedRequest(index);
- }
- }
- else
- {
- if (index < 0 || index >= m_ShadowRequestCount)
- return null;
-
- return m_ShadowResolutionRequests[index];
- }
+ if (index < 0 || index >= m_ShadowRequestCount)
+ return null;
- return null;
+ return m_ShadowResolutionRequests[index];
}
public Vector2 GetReservedResolution(int index)
@@ -521,7 +428,7 @@ public Vector2 GetReservedResolution(int index)
return m_ShadowResolutionRequests[index].resolution;
}
- internal void UpdateShadowRequest(int index, HDShadowRequest shadowRequest)
+ internal void UpdateShadowRequest(int index, HDShadowRequest shadowRequest, bool isCached = false)
{
if (index >= m_ShadowRequestCount)
return;
@@ -532,7 +439,11 @@ internal void UpdateShadowRequest(int index, HDShadowRequest shadowRequest)
{
case ShadowMapType.PunctualAtlas:
{
- m_Atlas.AddShadowRequest(shadowRequest);
+ if (isCached)
+ cachedShadowManager.punctualShadowAtlas.AddShadowRequest(shadowRequest);
+ else
+ m_Atlas.AddShadowRequest(shadowRequest);
+
break;
}
case ShadowMapType.CascadedDirectional:
@@ -542,7 +453,11 @@ internal void UpdateShadowRequest(int index, HDShadowRequest shadowRequest)
}
case ShadowMapType.AreaLightAtlas:
{
- m_AreaLightShadowAtlas.AddShadowRequest(shadowRequest);
+ if (isCached)
+ cachedShadowManager.areaShadowAtlas.AddShadowRequest(shadowRequest);
+ else
+ m_AreaLightShadowAtlas.AddShadowRequest(shadowRequest);
+
break;
}
};
@@ -602,6 +517,8 @@ HDShadowData CreateShadowData(HDShadowRequest shadowRequest, HDShadowAtlas atlas
data.shadowFilterParams0 = shadowRequest.evsmParams;
}
+ data.isInCachedAtlas = shadowRequest.isInCachedAtlas ? 1.0f : 0.0f;
+
return data;
}
@@ -620,6 +537,8 @@ public void UpdateCullingParameters(ref ScriptableCullingParameters cullingParam
public void LayoutShadowMaps(LightingDebugSettings lightingDebugSettings)
{
+ cachedShadowManager.UpdateDebugSettings(lightingDebugSettings);
+
m_Atlas.UpdateDebugSettings(lightingDebugSettings);
if (m_CascadeAtlas != null)
@@ -659,6 +578,11 @@ unsafe public void PrepareGPUShadowDatas(CullingResults cullResults, HDCamera ca
Debug.Assert(m_ShadowRequests[i] != null);
HDShadowAtlas atlas = m_Atlas;
+ if(m_ShadowRequests[i].isInCachedAtlas)
+ {
+ atlas = cachedShadowManager.punctualShadowAtlas;
+ }
+
if (m_ShadowRequests[i].shadowMapType == ShadowMapType.CascadedDirectional)
{
atlas = m_CascadeAtlas;
@@ -666,6 +590,10 @@ unsafe public void PrepareGPUShadowDatas(CullingResults cullResults, HDCamera ca
else if (m_ShadowRequests[i].shadowMapType == ShadowMapType.AreaLightAtlas)
{
atlas = m_AreaLightShadowAtlas;
+ if(m_ShadowRequests[i].isInCachedAtlas)
+ {
+ atlas = cachedShadowManager.areaShadowAtlas;
+ }
}
HDShadowData shadowData;
@@ -722,6 +650,7 @@ public void RenderShadows(ScriptableRenderContext renderContext, CommandBuffer c
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.RenderPunctualShadowMaps)))
{
m_Atlas.RenderShadows(cullResults, globalCB, hdCamera.frameSettings, renderContext, cmd);
+ cachedShadowManager.punctualShadowAtlas.RenderShadows(cullResults, globalCB, hdCamera.frameSettings, renderContext, cmd);
}
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.RenderDirectionalShadowMaps)))
@@ -732,7 +661,10 @@ public void RenderShadows(ScriptableRenderContext renderContext, CommandBuffer c
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.RenderAreaShadowMaps)))
{
if (ShaderConfig.s_AreaLights == 1)
+ {
m_AreaLightShadowAtlas.RenderShadows(cullResults, globalCB, hdCamera.frameSettings, renderContext, cmd);
+ cachedShadowManager.areaShadowAtlas.RenderShadows(cullResults, globalCB, hdCamera.frameSettings, renderContext, cmd);
+ }
}
}
@@ -747,8 +679,12 @@ public void BindResources(CommandBuffer cmd)
{
m_Atlas.BindResources(cmd);
m_CascadeAtlas.BindResources(cmd);
+ cachedShadowManager.punctualShadowAtlas.BindResources(cmd);
if (ShaderConfig.s_AreaLights == 1)
+ {
m_AreaLightShadowAtlas.BindResources(cmd);
+ cachedShadowManager.areaShadowAtlas.BindResources(cmd);
+ }
}
public int GetShadowRequestCount()
@@ -764,6 +700,8 @@ public void Clear()
if (ShaderConfig.s_AreaLights == 1)
m_AreaLightShadowAtlas.Clear();
+ cachedShadowManager.ClearShadowRequests();
+
m_ShadowResolutionRequestCounter = 0;
m_ShadowRequestCount = 0;
@@ -775,15 +713,22 @@ public struct ShadowDebugAtlasTextures
public RTHandle punctualShadowAtlas;
public RTHandle cascadeShadowAtlas;
public RTHandle areaShadowAtlas;
+
+ public RTHandle cachedPunctualShadowAtlas;
+ public RTHandle cachedAreaShadowAtlas;
}
public ShadowDebugAtlasTextures GetDebugAtlasTextures()
{
var result = new ShadowDebugAtlasTextures();
if (ShaderConfig.s_AreaLights == 1)
+ {
result.areaShadowAtlas = m_AreaLightShadowAtlas.renderTarget;
+ result.cachedAreaShadowAtlas = cachedShadowManager.areaShadowAtlas.renderTarget;
+ }
result.punctualShadowAtlas = m_Atlas.renderTarget;
result.cascadeShadowAtlas = m_CascadeAtlas.renderTarget;
+ result.cachedPunctualShadowAtlas = cachedShadowManager.punctualShadowAtlas.renderTarget;
return result;
}
@@ -806,6 +751,17 @@ public void DisplayAreaLightShadowAtlas(RTHandle atlasTexture, CommandBuffer cmd
m_AreaLightShadowAtlas.DisplayAtlas(atlasTexture, cmd, debugMaterial, new Rect(0, 0, m_AreaLightShadowAtlas.width, m_AreaLightShadowAtlas.height), screenX, screenY, screenSizeX, screenSizeY, minValue, maxValue, mpb);
}
+ public void DisplayCachedPunctualShadowAtlas(RTHandle atlasTexture, CommandBuffer cmd, Material debugMaterial, float screenX, float screenY, float screenSizeX, float screenSizeY, float minValue, float maxValue, MaterialPropertyBlock mpb)
+ {
+ cachedShadowManager.punctualShadowAtlas.DisplayAtlas(atlasTexture, cmd, debugMaterial, new Rect(0, 0, cachedShadowManager.punctualShadowAtlas.width, cachedShadowManager.punctualShadowAtlas.height), screenX, screenY, screenSizeX, screenSizeY, minValue, maxValue, mpb);
+ }
+
+ public void DisplayCachedAreaShadowAtlas(RTHandle atlasTexture, CommandBuffer cmd, Material debugMaterial, float screenX, float screenY, float screenSizeX, float screenSizeY, float minValue, float maxValue, MaterialPropertyBlock mpb)
+ {
+ if (ShaderConfig.s_AreaLights == 1)
+ cachedShadowManager.areaShadowAtlas.DisplayAtlas(atlasTexture, cmd, debugMaterial, new Rect(0, 0, cachedShadowManager.areaShadowAtlas.width, cachedShadowManager.areaShadowAtlas.height), screenX, screenY, screenSizeX, screenSizeY, minValue, maxValue, mpb);
+ }
+
// Warning: must be called after ProcessShadowRequests and RenderShadows to have valid informations
public void DisplayShadowMap(in ShadowDebugAtlasTextures atlasTextures, int shadowIndex, CommandBuffer cmd, Material debugMaterial, float screenX, float screenY, float screenSizeX, float screenSizeY, float minValue, float maxValue, MaterialPropertyBlock mpb)
{
@@ -818,7 +774,10 @@ public void DisplayShadowMap(in ShadowDebugAtlasTextures atlasTextures, int shad
{
case ShadowMapType.PunctualAtlas:
{
- m_Atlas.DisplayAtlas(atlasTextures.punctualShadowAtlas, cmd, debugMaterial, shadowRequest.atlasViewport, screenX, screenY, screenSizeX, screenSizeY, minValue, maxValue, mpb);
+ if (shadowRequest.isInCachedAtlas)
+ cachedShadowManager.punctualShadowAtlas.DisplayAtlas(atlasTextures.cachedPunctualShadowAtlas, cmd, debugMaterial, shadowRequest.atlasViewport, screenX, screenY, screenSizeX, screenSizeY, minValue, maxValue, mpb);
+ else
+ m_Atlas.DisplayAtlas(atlasTextures.punctualShadowAtlas, cmd, debugMaterial, shadowRequest.atlasViewport, screenX, screenY, screenSizeX, screenSizeY, minValue, maxValue, mpb);
break;
}
case ShadowMapType.CascadedDirectional:
@@ -829,7 +788,12 @@ public void DisplayShadowMap(in ShadowDebugAtlasTextures atlasTextures, int shad
case ShadowMapType.AreaLightAtlas:
{
if (ShaderConfig.s_AreaLights == 1)
- m_AreaLightShadowAtlas.DisplayAtlas(atlasTextures.areaShadowAtlas, cmd, debugMaterial, shadowRequest.atlasViewport, screenX, screenY, screenSizeX, screenSizeY, minValue, maxValue, mpb);
+ {
+ if (shadowRequest.isInCachedAtlas)
+ cachedShadowManager.areaShadowAtlas.DisplayAtlas(atlasTextures.cachedAreaShadowAtlas, cmd, debugMaterial, shadowRequest.atlasViewport, screenX, screenY, screenSizeX, screenSizeY, minValue, maxValue, mpb);
+ else
+ m_AreaLightShadowAtlas.DisplayAtlas(atlasTextures.areaShadowAtlas, cmd, debugMaterial, shadowRequest.atlasViewport, screenX, screenY, screenSizeX, screenSizeY, minValue, maxValue, mpb);
+ }
break;
}
};
@@ -845,6 +809,7 @@ public void Dispose()
m_CascadeAtlas.Release();
CoreUtils.Destroy(m_ClearShadowMaterial);
+ cachedShadowManager.Dispose();
}
}
}
diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDShadowManager.cs.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDShadowManager.cs.hlsl
index 42dac6b10b9..43c6844777e 100644
--- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDShadowManager.cs.hlsl
+++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDShadowManager.cs.hlsl
@@ -20,7 +20,7 @@ struct HDShadowData
float4 shadowMapSize;
real4 shadowFilterParams0;
float3 cacheTranslationDelta;
- float _pad0;
+ float isInCachedAtlas;
float4x4 shadowToWorld;
};
diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDShadowSampling.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDShadowSampling.hlsl
index aac441d7899..f360bcde710 100644
--- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDShadowSampling.hlsl
+++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDShadowSampling.hlsl
@@ -266,7 +266,8 @@ float SampleShadow_MSM_1tap(float3 tcs, float lightLeakBias, float momentBias, f
//
// PCSS sampling
//
-float SampleShadow_PCSS(float3 tcs, float2 posSS, float2 scale, float2 offset, float shadowSoftness, float minFilterRadius, int blockerSampleCount, int filterSampleCount, Texture2D tex, SamplerComparisonState compSamp, SamplerState samp, float depthBias, float4 zParams, bool isPerspective)
+// Note shadowAtlasInfo contains: x: resolution, y: the inverse of atlas resolution
+float SampleShadow_PCSS(float3 tcs, float2 posSS, float2 scale, float2 offset, float shadowSoftness, float minFilterRadius, int blockerSampleCount, int filterSampleCount, Texture2D tex, SamplerComparisonState compSamp, SamplerState samp, float depthBias, float4 zParams, bool isPerspective, float2 shadowAtlasInfo)
{
#if SHADOW_USE_DEPTH_BIAS == 1
// add the depth bias
@@ -277,8 +278,6 @@ float SampleShadow_PCSS(float3 tcs, float2 posSS, float2 scale, float2 offset, f
float sampleJitterAngle = InterleavedGradientNoise(posSS.xy, taaFrameIndex) * 2.0 * PI;
float2 sampleJitter = float2(sin(sampleJitterAngle), cos(sampleJitterAngle));
- // x contains resolution and y the inverse of atlas resolution
- float2 shadowAtlasInfo = isPerspective ? _ShadowAtlasSize.xz : _CascadeShadowAtlasSize.xz;
// Note: this is a hack, but the original implementation was faulty as it didn't scale offset based on the resolution of the atlas (*not* the shadow map).
// All the softness fitting has been done using a reference 4096x4096, hence the following scale.
diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/VolumetricLighting/VolumetricLighting.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/VolumetricLighting/VolumetricLighting.cs
index eaafd5edd74..7c3280d4d4f 100644
--- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/VolumetricLighting/VolumetricLighting.cs
+++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/VolumetricLighting/VolumetricLighting.cs
@@ -467,7 +467,7 @@ internal void DestroyVolumetricLightingBuffers()
CoreUtils.SafeRelease(m_VisibleVolumeDataBuffer);
CoreUtils.SafeRelease(m_VisibleVolumeBoundsBuffer);
- m_VisibleVolumeData = null; // free()
+ m_VisibleVolumeData = null; // free()
m_VisibleVolumeBounds = null; // free()
}
@@ -493,7 +493,7 @@ internal void ResizeVolumetricLightingBuffers(HDCamera hdCamera, int frameIndex)
var currentParams = hdCamera.vBufferParams[currIdx];
- ResizeVolumetricBuffer(ref m_DensityBuffer, "VBufferDensity", currentParams.viewportSize.x,
+ ResizeVolumetricBuffer(ref m_DensityBuffer, "VBufferDensity", currentParams.viewportSize.x,
currentParams.viewportSize.y,
currentParams.viewportSize.z);
ResizeVolumetricBuffer(ref m_LightingBuffer, "VBufferLighting", currentParams.viewportSize.x,
diff --git a/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Components/Exposure.cs b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Components/Exposure.cs
index 0007c9c6076..7c942954e1e 100644
--- a/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Components/Exposure.cs
+++ b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Components/Exposure.cs
@@ -86,11 +86,25 @@ public sealed class Exposure : VolumeComponent, IPostProcessComponent
public MinFloatParameter adaptationSpeedLightToDark = new MinFloatParameter(1f, 0.001f);
///
- /// Sets the texture mask used to weight the pixels in the buffer when computing exposure. Used only with .
+ /// Sets the texture mask used to weight the pixels in the buffer when computing exposure.
///
- [Tooltip("Sets the texture mask to be used to weight the pixels in the buffer for the sake of computing exposure..")]
+ [Tooltip("Sets the texture mask to be used to weight the pixels in the buffer for the sake of computing exposure.")]
public NoInterpTextureParameter weightTextureMask = new NoInterpTextureParameter(null);
+ ///
+ /// These values are the lower and upper percentages of the histogram that will be used to
+ /// find a stable average luminance. Values outside of this range will be discarded and won't
+ /// contribute to the average luminance.
+ ///
+ [Tooltip("Sets the range of values (in terms of percentages) of the histogram that are accepted while finding a stable average exposure. Anything outside the value is discarded.")]
+ public FloatRangeParameter histogramPercentages = new FloatRangeParameter(new Vector2(40.0f, 90.0f), 0.0f, 100.0f);
+
+ ///
+ /// Sets whether histogram exposure mode will remap the computed exposure with a curve remapping (akin to Curve Remapping mode)
+ ///
+ [Tooltip("Sets whether histogram exposure mode will remap the computed exposure with a curve remapping (akin to Curve Remapping mode).")]
+ public BoolParameter histogramUseCurveRemapping = new BoolParameter(false);
+
///
/// Tells if the effect needs to be rendered or not.
///
@@ -117,6 +131,11 @@ public enum ExposureMode
///
Automatic,
+ ///
+ /// Automatically sets the exposure depending on what is on screen and can filter out outliers based on provided settings.
+ ///
+ AutomaticHistogram,
+
///
/// Maps the current Scene exposure to a custom curve.
///
diff --git a/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Components/Tonemapping.cs b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Components/Tonemapping.cs
index b103590431d..ef60f9e25e3 100644
--- a/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Components/Tonemapping.cs
+++ b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Components/Tonemapping.cs
@@ -6,6 +6,7 @@ namespace UnityEngine.Rendering.HighDefinition
/// Available tonemapping modes.
///
///
+ [GenerateHLSL]
public enum TonemappingMode
{
///
diff --git a/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Components/Tonemapping.cs.hlsl b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Components/Tonemapping.cs.hlsl
new file mode 100644
index 00000000000..01a14dbe92d
--- /dev/null
+++ b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Components/Tonemapping.cs.hlsl
@@ -0,0 +1,17 @@
+//
+// This file was automatically generated. Please don't edit by hand.
+//
+
+#ifndef TONEMAPPING_CS_HLSL
+#define TONEMAPPING_CS_HLSL
+//
+// UnityEngine.Rendering.HighDefinition.TonemappingMode: static fields
+//
+#define TONEMAPPINGMODE_NONE (0)
+#define TONEMAPPINGMODE_NEUTRAL (1)
+#define TONEMAPPINGMODE_ACES (2)
+#define TONEMAPPINGMODE_CUSTOM (3)
+#define TONEMAPPINGMODE_EXTERNAL (4)
+
+
+#endif
diff --git a/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Components/Tonemapping.cs.hlsl.meta b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Components/Tonemapping.cs.hlsl.meta
new file mode 100644
index 00000000000..dbc46634e4a
--- /dev/null
+++ b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Components/Tonemapping.cs.hlsl.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 3716aaa9cab64e5419f00d48a69fab04
+ShaderImporter:
+ externalObjects: {}
+ defaultTextures: []
+ nonModifiableTextures: []
+ preprocessorOverride: 0
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/PostProcessSystem.RenderGraph.cs b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/PostProcessSystem.RenderGraph.cs
index 5b55edea50a..692f3918445 100644
--- a/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/PostProcessSystem.RenderGraph.cs
+++ b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/PostProcessSystem.RenderGraph.cs
@@ -5,9 +5,9 @@ namespace UnityEngine.Rendering.HighDefinition
{
partial class PostProcessSystem
{
- public void Render( RenderGraph renderGraph,
- HDCamera hdCamera,
- BlueNoise blueNoise,
+ public void Render( RenderGraph renderGraph,
+ HDCamera hdCamera,
+ BlueNoise blueNoise,
TextureHandle colorBuffer,
TextureHandle afterPostProcessTexture,
TextureHandle depthBuffer,
diff --git a/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/PostProcessSystem.cs b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/PostProcessSystem.cs
index af115aa692b..dd7ce91f082 100644
--- a/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/PostProcessSystem.cs
+++ b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/PostProcessSystem.cs
@@ -34,11 +34,15 @@ private enum SMAAStage
// Exposure data
const int k_ExposureCurvePrecision = 128;
+ const int k_HistogramBins = 128; // Important! If this changes, need to change HistogramExposure.compute
readonly Color[] m_ExposureCurveColorArray = new Color[k_ExposureCurvePrecision];
readonly int[] m_ExposureVariants = new int[4];
Texture2D m_ExposureCurveTexture;
RTHandle m_EmptyExposureTexture; // RGHalf
+ RTHandle m_DebugExposureData;
+ ComputeBuffer m_HistogramBuffer;
+ readonly int[] m_EmptyHistogram = new int[k_HistogramBins];
// Depth of field data
ComputeBuffer m_BokehNearKernel;
@@ -188,6 +192,20 @@ public PostProcessSystem(HDRenderPipelineAsset hdAsset, RenderPipelineResources
m_KeepAlpha = hdAsset.currentPlatformRenderPipelineSettings.supportsAlpha;
}
+ // Setup a default exposure textures and clear it to neutral values so that the exposure
+ // multiplier is 1 and thus has no effect
+ // Beware that 0 in EV100 maps to a multiplier of 0.833 so the EV100 value in this
+ // neutral exposure texture isn't 0
+ m_EmptyExposureTexture = RTHandles.Alloc(1, 1, colorFormat: k_ExposureFormat,
+ enableRandomWrite: true, name: "Empty EV100 Exposure");
+
+ m_DebugExposureData = RTHandles.Alloc(1, 1, colorFormat: k_ExposureFormat,
+ enableRandomWrite: true, name: "Debug Exposure Info"
+ );
+
+
+ FillEmptyExposureTexture();
+
// Call after initializing m_LutSize and m_KeepAlpha as it's needed for render target allocation.
InitializeNonRenderGraphResources(hdAsset);
}
@@ -196,6 +214,9 @@ public void Cleanup()
{
CleanupNonRenderGraphResources();
+ RTHandles.Release(m_EmptyExposureTexture);
+ m_EmptyExposureTexture = null;
+
CoreUtils.Destroy(m_ExposureCurveTexture);
CoreUtils.Destroy(m_InternalSpectralLut);
CoreUtils.Destroy(m_FinalPassMaterial);
@@ -208,6 +229,8 @@ public void Cleanup()
CoreUtils.SafeRelease(m_NearBokehTileList);
CoreUtils.SafeRelease(m_FarBokehTileList);
CoreUtils.SafeRelease(m_ContrastAdaptiveSharpen);
+ CoreUtils.SafeRelease(m_HistogramBuffer);
+ RTHandles.Release(m_DebugExposureData);
m_ExposureCurveTexture = null;
m_InternalSpectralLut = null;
@@ -220,6 +243,9 @@ public void Cleanup()
m_BokehIndirectCmd = null;
m_NearBokehTileList = null;
m_FarBokehTileList = null;
+ m_HistogramBuffer = null;
+ m_DebugExposureData = null;
+
}
public void InitializeNonRenderGraphResources(HDRenderPipelineAsset hdAsset)
@@ -247,16 +273,6 @@ public void InitializeNonRenderGraphResources(HDRenderPipelineAsset hdAsset)
enableRandomWrite: true
);
- // Setup a default exposure textures and clear it to neutral values so that the exposure
- // multiplier is 1 and thus has no effect
- // Beware that 0 in EV100 maps to a multiplier of 0.833 so the EV100 value in this
- // neutral exposure texture isn't 0
- m_EmptyExposureTexture = RTHandles.Alloc(1, 1, colorFormat: k_ExposureFormat,
- enableRandomWrite: true, name: "Empty EV100 Exposure"
- );
-
- FillEmptyExposureTexture();
-
// Misc targets
m_TempTexture1024 = RTHandles.Alloc(
1024, 1024, colorFormat: GraphicsFormat.R16G16_SFloat,
@@ -283,30 +299,30 @@ public void CleanupNonRenderGraphResources()
m_Pool.Cleanup();
- RTHandles.Release(m_EmptyExposureTexture);
RTHandles.Release(m_TempTexture1024);
RTHandles.Release(m_TempTexture32);
RTHandles.Release(m_AlphaTexture);
RTHandles.Release(m_InternalLogLut);
- m_EmptyExposureTexture = null;
m_TempTexture1024 = null;
m_TempTexture32 = null;
m_AlphaTexture = null;
m_InternalLogLut = null;
}
+
// In some cases, the internal buffer of render textures might be invalid.
// Usually when using these textures with API such as SetRenderTarget, they are recreated internally.
// This is not the case when these textures are used exclusively with Compute Shaders. So to make sure they work in this case, we recreate them here.
void CheckRenderTexturesValidity()
{
- if (!m_NonRenderGraphResourcesAvailable)
- return;
-
if (!m_EmptyExposureTexture.rt.IsCreated())
FillEmptyExposureTexture();
+ if (!m_NonRenderGraphResourcesAvailable)
+ return;
+
+ HDUtils.CheckRTCreated(m_DebugExposureData.rt);
HDUtils.CheckRTCreated(m_InternalLogLut.rt);
HDUtils.CheckRTCreated(m_TempTexture1024.rt);
HDUtils.CheckRTCreated(m_TempTexture32.rt);
@@ -479,7 +495,14 @@ void PoolSource(ref RTHandle src, RTHandle dst)
{
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.DynamicExposure)))
{
- DoDynamicExposure(cmd, camera, source);
+ if (m_Exposure.mode.value == ExposureMode.AutomaticHistogram)
+ {
+ DoHistogramBasedExposure(cmd, camera, source);
+ }
+ else
+ {
+ DoDynamicExposure(cmd, camera, source);
+ }
// On reset history we need to apply dynamic exposure immediately to avoid
// white or black screen flashes when the current exposure isn't anywhere
@@ -796,6 +819,26 @@ public RTHandle GetPreviousExposureTexture(HDCamera camera)
return rt ?? m_EmptyExposureTexture;
}
+ internal RTHandle GetExposureDebugData()
+ {
+ return m_DebugExposureData;
+ }
+
+ internal HableCurve GetCustomToneMapCurve()
+ {
+ return m_HableCurve;
+ }
+
+ internal int GetLutSize()
+ {
+ return m_LutSize;
+ }
+
+ internal ComputeBuffer GetHistogramBuffer()
+ {
+ return m_HistogramBuffer;
+ }
+
void DoFixedExposure(CommandBuffer cmd, HDCamera camera)
{
var cs = m_Resources.shaders.exposureCS;
@@ -874,12 +917,9 @@ void PrepareExposureCurveData(AnimationCurve curve, out float min, out float max
m_ExposureCurveTexture.Apply();
}
- void DoDynamicExposure(CommandBuffer cmd, HDCamera camera, RTHandle colorBuffer)
+ void DynamicExposureSetup(CommandBuffer cmd, HDCamera camera, out RTHandle prevExposure, out RTHandle nextExposure)
{
- var cs = m_Resources.shaders.exposureCS;
- int kernel;
-
- GrabExposureHistoryTextures(camera, out var prevExposure, out var nextExposure);
+ GrabExposureHistoryTextures(camera, out prevExposure, out nextExposure);
// Setup variants
var adaptationMode = m_Exposure.adaptationMode.value;
@@ -898,6 +938,14 @@ void DoDynamicExposure(CommandBuffer cmd, HDCamera camera, RTHandle colorBuffer)
m_ExposureVariants[1] = (int)m_Exposure.meteringMode.value;
m_ExposureVariants[2] = (int)adaptationMode;
m_ExposureVariants[3] = 0;
+ }
+
+ void DoDynamicExposure(CommandBuffer cmd, HDCamera camera, RTHandle colorBuffer)
+ {
+ var cs = m_Resources.shaders.exposureCS;
+ int kernel;
+
+ DynamicExposureSetup(cmd, camera, out var prevExposure, out var nextExposure);
var sourceTex = colorBuffer;
@@ -935,6 +983,8 @@ void DoDynamicExposure(CommandBuffer cmd, HDCamera camera, RTHandle colorBuffer)
PrepareExposureCurveData(m_Exposure.curveMap.value, out float min, out float max);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._ExposureCurveTexture, m_ExposureCurveTexture);
cmd.SetComputeVectorParam(cs, HDShaderIDs._ExposureParams, new Vector4(m_Exposure.compensation.value + m_DebugExposureCompensation, min, max, 0f));
+ cmd.SetComputeVectorParam(cs, HDShaderIDs._ExposureParams2, new Vector4(min, max, 0f, 0f));
+
m_ExposureVariants[3] = 2;
}
@@ -946,6 +996,75 @@ void DoDynamicExposure(CommandBuffer cmd, HDCamera camera, RTHandle colorBuffer)
cmd.DispatchCompute(cs, kernel, 1, 1, 1);
}
+ void DoHistogramBasedExposure(CommandBuffer cmd, HDCamera camera, RTHandle sourceTexture)
+ {
+ var cs = m_Resources.shaders.histogramExposureCS;
+ cs.shaderKeywords = null;
+ int kernel;
+
+ DynamicExposureSetup(cmd, camera, out var prevExposure, out var nextExposure);
+ // Parameters
+ Vector2 histogramFraction = m_Exposure.histogramPercentages.value / 100.0f;
+ float evRange = m_Exposure.limitMax.value - m_Exposure.limitMin.value;
+ float histScale = 1.0f / Mathf.Max(1e-5f, evRange);
+ float histBias = -m_Exposure.limitMin.value * histScale;
+ Vector4 histogramParams = new Vector4(histScale, histBias, histogramFraction.x, histogramFraction.y);
+
+ ValidateComputeBuffer(ref m_HistogramBuffer, k_HistogramBins, sizeof(uint));
+ m_HistogramBuffer.SetData(m_EmptyHistogram); // Clear the histogram
+ cmd.SetComputeVectorParam(cs, HDShaderIDs._HistogramExposureParams, histogramParams);
+
+ // Generate histogram.
+ kernel = cs.FindKernel("KHistogramGen");
+ cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._PreviousExposureTexture, prevExposure);
+ cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._SourceTexture, sourceTexture);
+ if (m_Exposure.meteringMode == MeteringMode.MaskWeighted && m_Exposure.weightTextureMask.value != null)
+ {
+ cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._ExposureWeightMask, m_Exposure.weightTextureMask.value);
+ }
+ else
+ {
+ cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._ExposureWeightMask, Texture2D.whiteTexture);
+ }
+
+ cmd.SetComputeIntParams(cs, HDShaderIDs._Variants, m_ExposureVariants);
+
+ cmd.SetComputeBufferParam(cs, kernel, HDShaderIDs._HistogramBuffer, m_HistogramBuffer);
+
+ int threadGroupSizeX = 16;
+ int threadGroupSizeY = 8;
+ int dispatchSizeX = HDUtils.DivRoundUp(camera.actualWidth / 2, threadGroupSizeX);
+ int dispatchSizeY = HDUtils.DivRoundUp(camera.actualHeight / 2, threadGroupSizeY);
+ int totalPixels = camera.actualWidth * camera.actualHeight;
+ cmd.DispatchCompute(cs, kernel, dispatchSizeX, dispatchSizeY, 1);
+
+ // Now read the histogram
+ kernel = cs.FindKernel("KHistogramReduce");
+ cmd.SetComputeVectorParam(cs, HDShaderIDs._ExposureParams, new Vector4(m_Exposure.compensation.value + m_DebugExposureCompensation, m_Exposure.limitMin.value, m_Exposure.limitMax.value, 0f));
+ cmd.SetComputeVectorParam(cs, HDShaderIDs._AdaptationParams, new Vector4(m_Exposure.adaptationSpeedLightToDark.value, m_Exposure.adaptationSpeedDarkToLight.value, 0f, 0f));
+ cmd.SetComputeBufferParam(cs, kernel, HDShaderIDs._HistogramBuffer, m_HistogramBuffer);
+ cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._PreviousExposureTexture, prevExposure);
+ cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputTexture, nextExposure);
+
+ cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._ExposureCurveTexture, m_ExposureCurveTexture);
+ m_ExposureVariants[3] = 0;
+ if (m_Exposure.histogramUseCurveRemapping.value)
+ {
+ PrepareExposureCurveData(m_Exposure.curveMap.value, out float min, out float max);
+ cmd.SetComputeVectorParam(cs, HDShaderIDs._ExposureParams2, new Vector4(min, max, 0f, 0f));
+ m_ExposureVariants[3] = 2;
+ }
+ cmd.SetComputeIntParams(cs, HDShaderIDs._Variants, m_ExposureVariants);
+
+ if (m_HDInstance.m_CurrentDebugDisplaySettings.data.lightingDebugSettings.exposureDebugMode == ExposureDebugMode.HistogramView)
+ {
+ cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._ExposureDebugTexture, m_DebugExposureData);
+ cs.EnableKeyword("OUTPUT_DEBUG_DATA");
+ }
+
+ cmd.DispatchCompute(cs, kernel, 1, 1, 1);
+ }
+
#endregion
#region Temporal Anti-aliasing
diff --git a/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/Exposure.compute b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/Exposure.compute
index 24bb51c8249..4e53910f142 100644
--- a/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/Exposure.compute
+++ b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/Exposure.compute
@@ -1,7 +1,4 @@
-#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
-#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"
-#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/PhysicalCamera.hlsl"
-#include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl"
+#include "Packages/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/ExposureCommon.hlsl"
#pragma only_renderers d3d11 playstation xboxone vulkan metal switch
@@ -11,99 +8,11 @@
#pragma kernel KReduction
#pragma kernel KReset
-TEXTURE2D(_ExposureCurveTexture);
-TEXTURE2D(_PreviousExposureTexture);
TEXTURE2D(_InputTexture);
-TEXTURE2D(_ExposureWeightMask);
-
-TEXTURE2D_X(_SourceTexture);
-
-RW_TEXTURE2D(float2, _OutputTexture);
-
-SAMPLER(sampler_LinearClamp);
-
-CBUFFER_START(cb)
- float4 _ExposureParams;
- float4 _AdaptationParams;
- uint4 _Variants;
-CBUFFER_END
-
-#define ParamEV100 _ExposureParams.y
-#define ParamExposureCompensation _ExposureParams.x
-#define ParamAperture _ExposureParams.y
-#define ParamShutterSpeed _ExposureParams.z
-#define ParamISO _ExposureParams.w
-#define ParamSpeedLightToDark _AdaptationParams.x
-#define ParamSpeedDarkToLight _AdaptationParams.y
-#define ParamExposureLimitMin _ExposureParams.y
-#define ParamExposureLimitMax _ExposureParams.z
-#define ParamCurveMin _ExposureParams.y
-#define ParamCurveMax _ExposureParams.z
-#define ParamSourceBuffer _Variants.x
-#define ParamMeteringMode _Variants.y
-#define ParamAdaptationMode _Variants.z
-#define ParamEvaluateMode _Variants.w
-
#define PREPASS_TEX_SIZE 1024.0
#define PREPASS_TEX_HALF_SIZE 512.0
-float WeightSample(uint2 pixel)
-{
- UNITY_BRANCH
- switch (ParamMeteringMode)
- {
- case 1u:
- {
- // Spot metering
- const float kRadius = 0.075 * PREPASS_TEX_SIZE;
- const float2 kCenter = (PREPASS_TEX_HALF_SIZE).xx;
- float d = length(kCenter - pixel) - kRadius;
- return 1.0 - saturate(d);
- }
- case 2u:
- {
- // Center-weighted
- const float2 kCenter = (PREPASS_TEX_HALF_SIZE).xx;
- return 1.0 - saturate(pow(length(kCenter - pixel) / PREPASS_TEX_HALF_SIZE, 1.0));
- }
- case 3u:
- {
- // Mask weigthing
- return SAMPLE_TEXTURE2D_LOD(_ExposureWeightMask, sampler_LinearClamp, pixel * rcp(PREPASS_TEX_SIZE), 0.0).x;
- }
-
- default:
- {
- // Global average
- return 1.0;
- }
- }
-}
-
-float GetPreviousExposureEV100()
-{
- return _PreviousExposureTexture[uint2(0u, 0u)].y;
-}
-
-float AdaptExposure(float exposure)
-{
- UNITY_BRANCH
- switch (ParamAdaptationMode)
- {
- case 1u:
- {
- // Progressive
- return ComputeLuminanceAdaptation(GetPreviousExposureEV100(), exposure, ParamSpeedDarkToLight, ParamSpeedLightToDark, unity_DeltaTime.x);
- }
- default:
- {
- // Fixed
- return exposure;
- }
- }
-}
-
//
// Fixed exposure
// Doesn't do anything fancy, simply copies the exposure & clamp values set in the volume system
@@ -140,28 +49,10 @@ void KPrePass(uint2 dispatchThreadId : SV_DispatchThreadID)
PositionInputs posInputs = GetPositionInput(float2(dispatchThreadId), rcp(PREPASS_TEX_SIZE), uint2(8u, 8u));
float2 uv = ClampAndScaleUVForBilinear(posInputs.positionNDC);
- float luma;
+ float luma = SampleLuminance(uv);
- UNITY_BRANCH
- switch (ParamSourceBuffer)
- {
- case 1u:
- {
- // Color buffer
- float prevExposure = ConvertEV100ToExposure(GetPreviousExposureEV100());
- float3 color = SAMPLE_TEXTURE2D_X_LOD(_SourceTexture, sampler_LinearClamp, uv, 0.0).xyz;
- luma = Luminance(color / prevExposure);
- break;
- }
- default:
- {
- // Lighting buffer
- luma = 1.0;
- break;
- }
- }
+ float weight = WeightSample(dispatchThreadId, PREPASS_TEX_SIZE.xx);
- float weight = WeightSample(dispatchThreadId);
float logLuma = ComputeEV100FromAvgLuminance(max(luma, 1e-4));
_OutputTexture[posInputs.positionSS] = float2(logLuma, weight);
}
@@ -235,8 +126,7 @@ void KReduction(uint2 groupId : SV_GroupID, uint2 groupThreadId : SV_GroupThread
case 2u:
{
// Curve remapping
- float remap = saturate((avgLuminance - ParamCurveMin) / (ParamCurveMax - ParamCurveMin));
- float exposure = SAMPLE_TEXTURE2D_LOD(_ExposureCurveTexture, sampler_LinearClamp, float2(remap, 0.0), 0.0).x;
+ float exposure = CurveRemap(avgLuminance);
exposure = AdaptExposure(exposure - ParamExposureCompensation);
_OutputTexture[groupId.xy] = float2(ConvertEV100ToExposure(exposure), exposure);
break;
diff --git a/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/ExposureCommon.hlsl b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/ExposureCommon.hlsl
new file mode 100644
index 00000000000..f12efaea4f9
--- /dev/null
+++ b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/ExposureCommon.hlsl
@@ -0,0 +1,108 @@
+#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
+#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"
+#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/PhysicalCamera.hlsl"
+#include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl"
+
+
+TEXTURE2D(_ExposureWeightMask);
+TEXTURE2D_X(_SourceTexture);
+TEXTURE2D(_PreviousExposureTexture);
+RW_TEXTURE2D(float2, _OutputTexture);
+TEXTURE2D(_ExposureCurveTexture);
+
+CBUFFER_START(cb)
+float4 _ExposureParams;
+float4 _ExposureParams2;
+float4 _HistogramExposureParams;
+float4 _AdaptationParams;
+uint4 _Variants;
+CBUFFER_END
+
+#define ParamEV100 _ExposureParams.y
+#define ParamExposureCompensation _ExposureParams.x
+#define ParamAperture _ExposureParams.y
+#define ParamShutterSpeed _ExposureParams.z
+#define ParamISO _ExposureParams.w
+#define ParamSpeedLightToDark _AdaptationParams.x
+#define ParamSpeedDarkToLight _AdaptationParams.y
+#define ParamExposureLimitMin _ExposureParams.y
+#define ParamExposureLimitMax _ExposureParams.z
+#define ParamCurveMin _ExposureParams2.x
+#define ParamCurveMax _ExposureParams2.y
+#define ParamSourceBuffer _Variants.x
+#define ParamMeteringMode _Variants.y
+#define ParamAdaptationMode _Variants.z
+#define ParamEvaluateMode _Variants.w
+
+float GetPreviousExposureEV100()
+{
+ return _PreviousExposureTexture[uint2(0u, 0u)].y;
+}
+
+float WeightSample(uint2 pixel, float2 sourceSize)
+{
+ UNITY_BRANCH
+ switch (ParamMeteringMode)
+ {
+ case 1u:
+ {
+ // Spot metering
+ float screenDiagonal = 0.5f * (sourceSize.x + sourceSize.y);
+ const float kRadius = 0.075 * screenDiagonal;
+ const float2 kCenter = sourceSize * 0.5f;
+ float d = length(kCenter - pixel) - kRadius;
+ return 1.0 - saturate(d);
+ }
+ case 2u:
+ {
+ // Center-weighted
+ float screenDiagonal = 0.5f * (sourceSize.x + sourceSize.y);
+ const float2 kCenter = sourceSize * 0.5f;
+ return 1.0 - saturate(pow(length(kCenter - pixel) / screenDiagonal, 1.0));
+ }
+ case 3u:
+ {
+ // Mask weigthing
+ return SAMPLE_TEXTURE2D_LOD(_ExposureWeightMask, s_linear_clamp_sampler, pixel * rcp(sourceSize), 0.0).x;
+ }
+
+ default:
+ {
+ // Global average
+ return 1.0;
+ }
+ }
+}
+
+float SampleLuminance(float2 uv)
+{
+ if (ParamSourceBuffer == 1)
+ {
+ // Color buffer
+ float prevExposure = ConvertEV100ToExposure(GetPreviousExposureEV100());
+ float3 color = SAMPLE_TEXTURE2D_X_LOD(_SourceTexture, s_linear_clamp_sampler, uv, 0.0).xyz;
+ return Luminance(color / prevExposure);
+ }
+ else
+ {
+ return 1.0f;
+ }
+}
+
+float AdaptExposure(float exposure)
+{
+ if (ParamAdaptationMode == 1)
+ {
+ return ComputeLuminanceAdaptation(GetPreviousExposureEV100(), exposure, ParamSpeedDarkToLight, ParamSpeedLightToDark, unity_DeltaTime.x);
+ }
+ else
+ {
+ return exposure;
+ }
+}
+
+float CurveRemap(float inEV)
+{
+ float remap = saturate((inEV - ParamCurveMin) / (ParamCurveMax - ParamCurveMin));
+ return SAMPLE_TEXTURE2D_LOD(_ExposureCurveTexture, s_linear_clamp_sampler, float2(remap, 0.0), 0.0).x;
+}
diff --git a/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/ExposureCommon.hlsl.meta b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/ExposureCommon.hlsl.meta
new file mode 100644
index 00000000000..fd95688bcc3
--- /dev/null
+++ b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/ExposureCommon.hlsl.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: cb6a39236cce6824cbf70aa0d69f39f6
+ShaderImporter:
+ externalObjects: {}
+ defaultTextures: []
+ nonModifiableTextures: []
+ preprocessorOverride: 0
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/HistogramExposure.compute b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/HistogramExposure.compute
new file mode 100644
index 00000000000..43e5ccbdc54
--- /dev/null
+++ b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/HistogramExposure.compute
@@ -0,0 +1,183 @@
+#include "Packages/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/ExposureCommon.hlsl"
+#include "Packages/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/HistogramExposureCommon.hlsl"
+
+
+// TODO List to investigate
+// - Worth considering multiple histograms per lane in the thread. (i.e. sharedHisto[BINS][NUMB_HIST] )
+// - At the moment the dispatch is at half res, but the buffer sampled is full res,
+// causing fairly bad cache behaviour. Can we use the mip chain realistically without issues? [The one we have is blurred and might be incomplete?]
+
+#pragma kernel KHistogramGen GEN_PASS
+#pragma kernel KHistogramReduce REDUCE_PASS
+#define GROUP_SIZE_X 16
+#define GROUP_SIZE_Y 8
+
+#pragma multi_compile _ OUTPUT_DEBUG_DATA
+
+// Because atomics are only on uint and we need a weighted value, we need to convert.
+// If we multiply the weight by 2048, we get somewhat ok precision and we support up to
+// the equivalent of 1920x1080 image in one bin. (Note, we run this at half res, so equivalent of 4k image)
+uint PackWeight(float weight)
+{
+ return uint(weight * 2048);
+}
+
+groupshared uint gs_localHistogram[HISTOGRAM_BINS];
+
+
+[numthreads(GROUP_SIZE_X, GROUP_SIZE_Y, 1)]
+void KHistogramGen(uint groupIndex : SV_GroupIndex,
+ uint3 dispatchThreadId : SV_DispatchThreadID)
+{
+ // Groupshared memory is not guaranteed to be 0 initialized.
+ // Note that currently the branch is always true (GROUP_SIZE_X * GROUP_SIZE_Y == HISTOGRAM_BINS). Here as safeguard if changing group size or bins.
+ if (groupIndex < HISTOGRAM_BINS)
+ {
+ gs_localHistogram[groupIndex] = 0u;
+ }
+
+ GroupMemoryBarrierWithGroupSync();
+
+ // TODO: This leads to poor cache behaviour, verify if we can use lower mip of the color pyramid.
+ uint2 fullResCoords = dispatchThreadId.xy << 1u;
+
+ if (all(fullResCoords < uint2(_ScreenSize.xy)))
+ {
+ float2 uv = ClampAndScaleUVForBilinear((fullResCoords + 0.5) * _ScreenSize.zw);
+ float luminance = SampleLuminance(uv);
+ float weight = WeightSample(fullResCoords, _ScreenSize.xy);
+
+ uint bin = GetHistogramBinLocation(luminance);
+ InterlockedAdd(gs_localHistogram[bin], PackWeight(weight));
+ }
+
+ GroupMemoryBarrierWithGroupSync();
+
+ // Note that currently the branch is always true (GROUP_SIZE_X * GROUP_SIZE_Y == HISTOGRAM_BINS). Here as safeguard if changing group size or bins.
+ if (groupIndex < HISTOGRAM_BINS)
+ {
+ InterlockedAdd(_HistogramBuffer[groupIndex], gs_localHistogram[groupIndex]);
+ }
+}
+
+#define USE_WAVE_INTRINSICS defined(PLATFORM_LANE_COUNT) && defined(PLATFORM_SUPPORTS_WAVE_INTRINSICS)
+
+
+#if USE_WAVE_INTRINSICS
+
+#define WAVE_SIZE PLATFORM_LANE_COUNT
+#define SUM_SCRATCH_SIZE HISTOGRAM_BINS / WAVE_SIZE
+
+#else
+
+#define SUM_SCRATCH_SIZE HISTOGRAM_BINS
+
+#endif
+
+groupshared float gs_partialSums[SUM_SCRATCH_SIZE];
+groupshared float gs_values[HISTOGRAM_BINS];
+
+float ComputeTotalSum(uint threadID, float threadVal)
+{
+ float sum = 0;
+
+#if USE_WAVE_INTRINSICS
+
+ uint waveCount = (HISTOGRAM_BINS / WAVE_SIZE);
+ float waveSum = WaveActiveSum(threadVal);
+
+ uint waveIDInGroup = threadID / WAVE_SIZE;
+ if (WaveIsFirstLane())
+ {
+ gs_partialSums[waveIDInGroup] = waveSum;
+ }
+
+ // We have values for all the waves, let's sync.
+ GroupMemoryBarrierWithGroupSync();
+
+ sum = gs_partialSums[0];
+ for (uint i = 1u; i < waveCount; ++i)
+ {
+ sum += gs_partialSums[i];
+ }
+
+#else // !USE_WAVE_INTRINSICS
+
+ gs_partialSums[threadID] = threadVal;
+
+ GroupMemoryBarrierWithGroupSync();
+
+ // Sum all values
+ for (uint i = HISTOGRAM_BINS >> 1u; i > 0u; i >>= 1u)
+ {
+ if (threadID < i)
+ gs_partialSums[threadID] = (gs_partialSums[threadID] + gs_partialSums[threadID + i]);
+
+ GroupMemoryBarrierWithGroupSync();
+ }
+
+ sum = gs_partialSums[0];
+
+#endif
+
+ return sum;
+}
+
+void ProcessBin(uint binIndex, inout float2 extremesSums, inout float evSum, inout float totalWeight)
+{
+ float histVal = gs_values[binIndex];
+ float binEV = BinLocationToEV(binIndex);
+
+ // Shadows
+ float off = min(extremesSums.x, histVal);
+ extremesSums -= off;
+ histVal -= off;
+ // Highlights
+ histVal = min(extremesSums.y, histVal);
+ extremesSums.y -= histVal;
+
+ evSum += histVal * binEV;
+ totalWeight += histVal;
+}
+
+[numthreads(HISTOGRAM_BINS, 1, 1)]
+void KHistogramReduce(uint3 dispatchThreadId : SV_DispatchThreadID)
+{
+ uint threadID = dispatchThreadId.x;
+ float histogramVal = UnpackWeight(_HistogramBuffer[threadID]);
+
+ gs_values[threadID] = histogramVal;
+
+ float sum = ComputeTotalSum(threadID, histogramVal);
+
+ float2 extremesSums = float2(_HistogramMinPercentile, _HistogramMaxPercentile) * sum;
+
+ // TODO: Can we be a bit more parallel here?
+ if (threadID == 0)
+ {
+ float evProcessedSum = 0;
+ float w = 0;
+
+ for (int i = 0; i < HISTOGRAM_BINS; ++i)
+ {
+ ProcessBin(i, extremesSums, evProcessedSum, w);
+ }
+
+ w = max(w, 1e-4f);
+ float avgEV = evProcessedSum * rcp(w);
+
+ if (ParamEvaluateMode == 2)
+ {
+ avgEV = CurveRemap(avgEV);
+ }
+
+ float exposure = AdaptExposure(avgEV - ParamExposureCompensation);
+ exposure = clamp(exposure, ParamExposureLimitMin, ParamExposureLimitMax);
+ _OutputTexture[uint2(0, 0)] = float2(ConvertEV100ToExposure(exposure), exposure);
+#ifdef OUTPUT_DEBUG_DATA
+ _ExposureDebugTexture[uint2(0, 0)] = float2(avgEV - ParamExposureCompensation, 0.0f);
+#endif
+ }
+
+
+}
diff --git a/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/HistogramExposure.compute.meta b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/HistogramExposure.compute.meta
new file mode 100644
index 00000000000..ec4511dfb0c
--- /dev/null
+++ b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/HistogramExposure.compute.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 222da48299136f34b8e3fb75ae9f8ac7
+ComputeShaderImporter:
+ externalObjects: {}
+ currentAPIMask: 4
+ preprocessorOverride: 0
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/HistogramExposureCommon.hlsl b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/HistogramExposureCommon.hlsl
new file mode 100644
index 00000000000..7620df3ec91
--- /dev/null
+++ b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/HistogramExposureCommon.hlsl
@@ -0,0 +1,39 @@
+
+#define HISTOGRAM_BINS 128
+
+#define _HistogramRangeScale _HistogramExposureParams.x
+#define _HistogramRangeBias _HistogramExposureParams.y
+#define _HistogramMinPercentile _HistogramExposureParams.z
+#define _HistogramMaxPercentile _HistogramExposureParams.w
+
+#ifdef GEN_PASS
+RWStructuredBuffer _HistogramBuffer;
+#else
+StructuredBuffer _HistogramBuffer;
+#endif
+
+#ifdef OUTPUT_DEBUG_DATA
+RW_TEXTURE2D(float2, _ExposureDebugTexture);
+#else
+TEXTURE2D(_ExposureDebugTexture);
+#endif
+
+float UnpackWeight(uint val)
+{
+ return val * rcp(2048.0f);
+}
+
+float GetFractionWithinHistogram(float value)
+{
+ return ComputeEV100FromAvgLuminance(value) * _HistogramRangeScale + _HistogramRangeBias;
+}
+
+uint GetHistogramBinLocation(float value)
+{
+ return uint(saturate(GetFractionWithinHistogram(value)) * (HISTOGRAM_BINS - 1));
+}
+
+float BinLocationToEV(uint binIdx)
+{
+ return (binIdx * rcp(float(HISTOGRAM_BINS - 1)) - _HistogramRangeBias) / _HistogramRangeScale;
+}
diff --git a/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/HistogramExposureCommon.hlsl.meta b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/HistogramExposureCommon.hlsl.meta
new file mode 100644
index 00000000000..11a0b6748c6
--- /dev/null
+++ b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/HistogramExposureCommon.hlsl.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 8b66a6c34796d04498345e5530eb228c
+ShaderImporter:
+ externalObjects: {}
+ defaultTextures: []
+ nonModifiableTextures: []
+ preprocessorOverride: 0
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/TemporalAntiAliasing.shader b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/TemporalAntiAliasing.shader
index f495059c61a..dddee9f8308 100644
--- a/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/TemporalAntiAliasing.shader
+++ b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/TemporalAntiAliasing.shader
@@ -50,6 +50,7 @@ Shader "Hidden/HDRP/TemporalAA"
#define CENTRAL_FILTERING NO_FILTERING
#define HISTORY_CLIP DIRECT_CLIP
#define ANTI_FLICKER 1
+ #define ANTI_FLICKER_MV_DEPENDENT 0
#define VELOCITY_REJECTION (defined(ENABLE_MV_REJECTION) && 0)
#define PERCEPTUAL_SPACE 1
#define PERCEPTUAL_SPACE_ONLY_END 0 && (PERCEPTUAL_SPACE == 0)
@@ -63,6 +64,7 @@ Shader "Hidden/HDRP/TemporalAA"
#define CENTRAL_FILTERING BLACKMAN_HARRIS
#define HISTORY_CLIP DIRECT_CLIP
#define ANTI_FLICKER 1
+ #define ANTI_FLICKER_MV_DEPENDENT 1
#define VELOCITY_REJECTION defined(ENABLE_MV_REJECTION)
#define PERCEPTUAL_SPACE 1
#define PERCEPTUAL_SPACE_ONLY_END 0 && (PERCEPTUAL_SPACE == 0)
@@ -163,7 +165,13 @@ Shader "Hidden/HDRP/TemporalAA"
// --------------- Get neighbourhood information and clamp history ---------------
float colorLuma = GetLuma(filteredColor);
float historyLuma = GetLuma(history);
- GetNeighbourhoodCorners(samples, historyLuma, colorLuma, float2(_AntiFlickerIntensity, _ContrastForMaxAntiFlicker));
+
+#if ANTI_FLICKER_MV_DEPENDENT || VELOCITY_REJECTION
+ float motionVectorLength = length(motionVector);
+#else
+ float motionVectorLength = 0.0f;
+#endif
+ GetNeighbourhoodCorners(samples, historyLuma, colorLuma, float2(_AntiFlickerIntensity, _ContrastForMaxAntiFlicker), motionVectorLength);
history = GetClippedHistory(filteredColor, history, samples.minNeighbour, samples.maxNeighbour);
filteredColor = SharpenColor(samples, filteredColor, sharpenStrength);
@@ -189,7 +197,7 @@ Shader "Hidden/HDRP/TemporalAA"
// --------------- Blend to final value and output ---------------
#if VELOCITY_REJECTION
- float lengthMV = length(motionVector) * 10;
+ float lengthMV = motionVectorLength * 10;
blendFactor = ModifyBlendWithMotionVectorRejection(_InputVelocityMagnitudeHistory, lengthMV, prevUV, blendFactor, _SpeedRejectionIntensity);
#endif
diff --git a/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/TemporalAntialiasing.hlsl b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/TemporalAntialiasing.hlsl
index 0eb2906c180..a55a9af46c5 100644
--- a/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/TemporalAntialiasing.hlsl
+++ b/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/TemporalAntialiasing.hlsl
@@ -257,7 +257,7 @@ float3 ConvertToOutputSpace(float3 color)
// Front most neighbourhood velocity ([Karis 2014])
float2 GetClosestFragment(TEXTURE2D_X(DepthTexture), int2 positionSS)
{
- float center = LoadCameraDepth(positionSS);
+ float center = LOAD_TEXTURE2D_X_LOD(DepthTexture, positionSS, 0).r;
int2 quadOffset = GetQuadOffset(positionSS);
@@ -526,7 +526,7 @@ void MinMaxNeighbourhood(inout NeighbourhoodSamples samples)
samples.avgNeighbour *= rcp(NEIGHBOUR_COUNT);
}
-void VarianceNeighbourhood(inout NeighbourhoodSamples samples, float historyLuma, float colorLuma, float2 antiFlickerParams)
+void VarianceNeighbourhood(inout NeighbourhoodSamples samples, float historyLuma, float colorLuma, float2 antiFlickerParams, float motionVectorLen)
{
CTYPE moment1 = 0;
CTYPE moment2 = 0;
@@ -552,20 +552,29 @@ void VarianceNeighbourhood(inout NeighbourhoodSamples samples, float historyLuma
// and high temporal contrast, we let the history to be closer to be unclipped. To achieve, the min/max bounds
// are extended artificially more.
#if ANTI_FLICKER
- stDevMultiplier = 1.4;
+ stDevMultiplier = 1.5;
float temporalContrast = saturate(abs(colorLuma - historyLuma) / Max3(0.2, colorLuma, historyLuma));
- stDevMultiplier += lerp(0.0, antiFlickerParams.x, smoothstep(0.05, antiFlickerParams.y, temporalContrast));
+#if ANTI_FLICKER_MV_DEPENDENT
+ const float screenDiag = length(_ScreenSize.xy);
+ const float maxFactorScale = 2.25f; // when stationary
+ const float minFactorScale = 0.8f; // when moving more than slightly
+ float localizedAntiFlicker = lerp(antiFlickerParams.x * minFactorScale, antiFlickerParams.x * maxFactorScale, saturate(1.0f - 2.0f * (motionVectorLen * screenDiag)));
+#else
+ float localizedAntiFlicker = antiFlickerParams.x;
+#endif
+ stDevMultiplier += lerp(0.0, localizedAntiFlicker, smoothstep(0.05, antiFlickerParams.y, temporalContrast));
+
#endif
samples.minNeighbour = moment1 - stdDev * stDevMultiplier;
samples.maxNeighbour = moment1 + stdDev * stDevMultiplier;
}
-void GetNeighbourhoodCorners(inout NeighbourhoodSamples samples, float historyLuma, float colorLuma, float2 antiFlickerParams)
+void GetNeighbourhoodCorners(inout NeighbourhoodSamples samples, float historyLuma, float colorLuma, float2 antiFlickerParams, float motionVecLen)
{
#if NEIGHBOUROOD_CORNER_METHOD == MINMAX
MinMaxNeighbourhood(samples);
#else
- VarianceNeighbourhood(samples, historyLuma, colorLuma, antiFlickerParams);
+ VarianceNeighbourhood(samples, historyLuma, colorLuma, antiFlickerParams, motionVecLen);
#endif
}
diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Camera/HDCamera.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Camera/HDCamera.cs
index 81881ecbf12..e77c0825e69 100644
--- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Camera/HDCamera.cs
+++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Camera/HDCamera.cs
@@ -746,8 +746,8 @@ internal void ExecuteCaptureActions(RenderGraph renderGraph, TextureHandle input
passData.input = builder.ReadTexture(input);
// We need to blit to an intermediate texture because input resolution can be bigger than the camera resolution
// Since recorder does not know about this, we need to send a texture of the right size.
- passData.tempTexture = builder.WriteTexture(renderGraph.CreateTexture(new TextureDesc(actualWidth, actualHeight)
- { colorFormat = inputDesc.colorFormat, name = "TempCaptureActions" }));
+ passData.tempTexture = builder.CreateTransientTexture(new TextureDesc(actualWidth, actualHeight)
+ { colorFormat = inputDesc.colorFormat, name = "TempCaptureActions" });
builder.SetRenderFunc(
(ExecuteCaptureActionsPassData data, RenderGraphContext ctx) =>
diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.Debug.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.Debug.cs
index 8eda7f371ca..125c2c7d095 100644
--- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.Debug.cs
+++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.Debug.cs
@@ -108,6 +108,8 @@ void RenderDebugOverlays( RenderGraph renderGraph,
shadowAtlases.punctualShadowAtlas = data.shadowTextures.punctualShadowResult.IsValid() ? ctx.resources.GetTexture(data.shadowTextures.punctualShadowResult) : null;
shadowAtlases.cascadeShadowAtlas = data.shadowTextures.directionalShadowResult.IsValid() ? ctx.resources.GetTexture(data.shadowTextures.directionalShadowResult) : null;
shadowAtlases.areaShadowAtlas = data.shadowTextures.areaShadowResult.IsValid() ? ctx.resources.GetTexture(data.shadowTextures.areaShadowResult) : null;
+ shadowAtlases.cachedPunctualShadowAtlas = data.shadowTextures.cachedPunctualShadowResult.IsValid() ? ctx.resources.GetTexture(data.shadowTextures.cachedPunctualShadowResult) : null;
+ shadowAtlases.cachedAreaShadowAtlas = data.shadowTextures.cachedAreaShadowResult.IsValid() ? ctx.resources.GetTexture(data.shadowTextures.cachedAreaShadowResult) : null;
RenderSkyReflectionOverlay(debugParams, ctx.cmd, ctx.renderGraphPool.GetTempMaterialPropertyBlock(), ref x, ref y, overlaySize);
RenderRayCountOverlay(debugParams, ctx.cmd, ref x, ref y, overlaySize);
@@ -137,12 +139,12 @@ static void RenderLightVolumes(RenderGraph renderGraph, in DebugParameters debug
using (var builder = renderGraph.AddRenderPass("LightVolumes", out var passData))
{
passData.parameters = s_lightVolumes.PrepareLightVolumeParameters(debugParameters.hdCamera, debugParameters.debugDisplaySettings.data.lightingDebugSettings, cullResults);
- passData.lightCountBuffer = builder.WriteTexture(renderGraph.CreateTexture(new TextureDesc(Vector2.one, true, true)
- { colorFormat= GraphicsFormat.R32_SFloat, clearBuffer = true, clearColor = Color.black, name = "LightVolumeCount" }));
- passData.colorAccumulationBuffer = builder.WriteTexture(renderGraph.CreateTexture(new TextureDesc(Vector2.one, true, true)
- { colorFormat = GraphicsFormat.R16G16B16A16_SFloat, clearBuffer = true, clearColor = Color.black, name = "LightVolumeColorAccumulation" }));
- passData.debugLightVolumesTexture = builder.WriteTexture(renderGraph.CreateTexture(new TextureDesc(Vector2.one, true, true)
- { colorFormat = GraphicsFormat.R16G16B16A16_SFloat, clearBuffer = true, clearColor = Color.black, enableRandomWrite = true, name = "LightVolumeDebugLightVolumesTexture" }));
+ passData.lightCountBuffer = builder.CreateTransientTexture(new TextureDesc(Vector2.one, true, true)
+ { colorFormat= GraphicsFormat.R32_SFloat, clearBuffer = true, clearColor = Color.black, name = "LightVolumeCount" });
+ passData.colorAccumulationBuffer = builder.CreateTransientTexture(new TextureDesc(Vector2.one, true, true)
+ { colorFormat = GraphicsFormat.R16G16B16A16_SFloat, clearBuffer = true, clearColor = Color.black, name = "LightVolumeColorAccumulation" });
+ passData.debugLightVolumesTexture = builder.CreateTransientTexture(new TextureDesc(Vector2.one, true, true)
+ { colorFormat = GraphicsFormat.R16G16B16A16_SFloat, clearBuffer = true, clearColor = Color.black, enableRandomWrite = true, name = "LightVolumeDebugLightVolumesTexture" });
passData.depthBuffer = builder.UseDepthBuffer(depthBuffer, DepthAccess.ReadWrite);
passData.destination = builder.WriteTexture(destination);
@@ -194,6 +196,14 @@ TextureHandle RenderDebug( RenderGraph renderGraph,
m_FullScreenDebugPushed = false;
}
+ // TODO RENDERGRAPH (Needs post processing in Rendergraph to properly be implemented)
+ if(debugParameters.exposureDebugEnabled)
+ {
+ // For reference the following is what is called in the non-render-graph version.
+ // RenderExposureDebug(debugParams, m_CameraColorBuffer, m_DebugFullScreenTempBuffer,m_PostProcessSystem.GetPreviousExposureTexture(hdCamera), m_PostProcessSystem.GetExposureTexture(hdCamera),
+ // m_PostProcessSystem.GetExposureDebugData(),m_IntermediateAfterPostProcessBuffer, m_PostProcessSystem.GetCustomToneMapCurve(), m_PostProcessSystem.GetLutSize(), m_PostProcessSystem.GetHistogramBuffer(), cmd);
+ }
+
if (debugParameters.colorPickerEnabled)
output = ResolveColorPickerDebug(renderGraph, debugParameters, colorPickerDebugTexture);
diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.LightLoop.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.LightLoop.cs
index caa18d08333..b17034843db 100644
--- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.LightLoop.cs
+++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.LightLoop.cs
@@ -235,7 +235,7 @@ LightingOutput RenderDeferredLighting( RenderGraph renderGraph,
// TODO RENDERGRAPH: Check how to avoid this kind of pattern.
// Unfortunately, the low level needs this texture to always be bound with UAV enabled, so in order to avoid effectively creating the full resolution texture here,
// we need to create a small dummy texture.
- passData.sssDiffuseLightingBuffer = builder.WriteTexture(renderGraph.CreateTexture(new TextureDesc(1, 1, true, true) { colorFormat = GraphicsFormat.B10G11R11_UFloatPack32, enableRandomWrite = true } ));
+ passData.sssDiffuseLightingBuffer = builder.CreateTransientTexture(new TextureDesc(1, 1, true, true) { colorFormat = GraphicsFormat.B10G11R11_UFloatPack32, enableRandomWrite = true } );
}
passData.depthBuffer = builder.ReadTexture(depthStencilBuffer);
passData.depthTexture = builder.ReadTexture(depthPyramidTexture);
@@ -274,7 +274,7 @@ LightingOutput RenderDeferredLighting( RenderGraph renderGraph,
resources.tileListBuffer = context.resources.GetComputeBuffer(data.tileListBuffer);
resources.dispatchIndirectBuffer = context.resources.GetComputeBuffer(data.dispatchIndirectBuffer);
- // RENDERGRAPH TODO: try to find a better way to bind this.
+ // TODO RENDERGRAPH: try to find a better way to bind this.
// Issue is that some GBuffers have several names (for example normal buffer is both NormalBuffer and GBuffer1)
// So it's not possible to use auto binding via dependency to shaderTagID
// Should probably get rid of auto binding and go explicit all the way (might need to wait for us to remove non rendergraph code path).
@@ -362,8 +362,8 @@ TextureHandle RenderSSR( RenderGraph renderGraph,
// In practice, these textures are sparse (mostly black). Therefore, clearing them is fast (due to CMASK),
// and much faster than fully overwriting them from within SSR shaders.
- passData.hitPointsTexture = builder.WriteTexture(renderGraph.CreateTexture(new TextureDesc(Vector2.one, true, true)
- { colorFormat = GraphicsFormat.R16G16_UNorm, clearBuffer = true, clearColor = Color.clear, enableRandomWrite = true, name = "SSR_Hit_Point_Texture" }));
+ passData.hitPointsTexture = builder.CreateTransientTexture(new TextureDesc(Vector2.one, true, true)
+ { colorFormat = GraphicsFormat.R16G16_UNorm, clearBuffer = true, clearColor = Color.clear, enableRandomWrite = true, name = "SSR_Hit_Point_Texture" });
passData.lightingTexture = builder.WriteTexture(renderGraph.CreateTexture(new TextureDesc(Vector2.one, true, true)
{ colorFormat = GraphicsFormat.R16G16B16A16_SFloat, clearBuffer = true, clearColor = Color.clear, enableRandomWrite = true, name = "SSR_Lighting_Texture" }, HDShaderIDs._SsrLightingTexture));
//passData.hitPointsTexture = builder.WriteTexture(renderGraph.CreateTexture(new TextureDesc(Vector2.one, true, true)
diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.Prepass.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.Prepass.cs
index ffb4c42da01..c6bafbc3155 100644
--- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.Prepass.cs
+++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.Prepass.cs
@@ -492,7 +492,7 @@ class ResolveStencilPassData
public BuildCoarseStencilAndResolveParameters parameters;
public TextureHandle inputDepth;
public TextureHandle resolvedStencil;
- public ComputeBuffer coarseStencilBuffer;
+ public ComputeBufferHandle coarseStencilBuffer;
}
void BuildCoarseStencilAndResolveIfNeeded(RenderGraph renderGraph, HDCamera hdCamera, ref PrepassOutput output)
@@ -501,8 +501,9 @@ void BuildCoarseStencilAndResolveIfNeeded(RenderGraph renderGraph, HDCamera hdCa
{
passData.parameters = PrepareBuildCoarseStencilParameters(hdCamera);
passData.inputDepth = output.depthBuffer;
- passData.coarseStencilBuffer = m_SharedRTManager.GetCoarseStencilBuffer();
- passData.resolvedStencil = builder.WriteTexture(renderGraph.CreateTexture(new TextureDesc(Vector2.one, true, true) { colorFormat = GraphicsFormat.R8G8_UInt, enableRandomWrite = true, name = "StencilBufferResolved" }));
+ passData.coarseStencilBuffer = builder.WriteComputeBuffer(renderGraph.ImportComputeBuffer(m_SharedRTManager.GetCoarseStencilBuffer()));
+ if (passData.parameters.resolveIsNecessary)
+ passData.resolvedStencil = builder.WriteTexture(renderGraph.CreateTexture(new TextureDesc(Vector2.one, true, true) { colorFormat = GraphicsFormat.R8G8_UInt, enableRandomWrite = true, name = "StencilBufferResolved" }));
builder.SetRenderFunc(
(ResolveStencilPassData data, RenderGraphContext context) =>
{
@@ -510,7 +511,7 @@ void BuildCoarseStencilAndResolveIfNeeded(RenderGraph renderGraph, HDCamera hdCa
BuildCoarseStencilAndResolveIfNeeded(data.parameters,
res.GetTexture(data.inputDepth),
res.GetTexture(data.resolvedStencil),
- data.coarseStencilBuffer,
+ res.GetComputeBuffer(data.coarseStencilBuffer),
context.cmd);
}
);
diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.RenderGraph.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.RenderGraph.cs
index 74384efdf78..2c0c9a1b3fd 100644
--- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.RenderGraph.cs
+++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.RenderGraph.cs
@@ -211,14 +211,14 @@ void ExecuteWithRenderGraph( RenderRequest renderRequest,
hdCamera.ExecuteCaptureActions(m_RenderGraph, colorBuffer);
postProcessDest = RenderDebug( m_RenderGraph,
- hdCamera,
+ hdCamera,
postProcessDest,
- prepassOutput.depthBuffer,
- prepassOutput.depthPyramidTexture,
- m_DebugFullScreenTexture,
- colorPickerTexture,
- shadowResult,
- cullingResults);
+ prepassOutput.depthBuffer,
+ prepassOutput.depthPyramidTexture,
+ m_DebugFullScreenTexture,
+ colorPickerTexture,
+ shadowResult,
+ cullingResults);
BlitFinalCameraTexture(m_RenderGraph, hdCamera, postProcessDest, backBuffer, prepassOutput.resolvedMotionVectorsBuffer, prepassOutput.resolvedNormalBuffer);
@@ -459,10 +459,12 @@ void RenderForwardTransparent( RenderGraph renderGraph,
bool renderMotionVecForTransparent = NeedMotionVectorForTransparent(hdCamera.frameSettings);
- TextureHandle mrt1;
+ passData.renderTargetCount = 2;
+ passData.renderTarget[0] = builder.WriteTexture(colorBuffer);
+
if (renderMotionVecForTransparent)
{
- mrt1 = motionVectorBuffer;
+ passData.renderTarget[1] = builder.WriteTexture(motionVectorBuffer);
// TODO RENDERGRAPH
// WORKAROUND VELOCITY-MSAA
// This is a workaround for velocity with MSAA. Currently motion vector resolve is not implemented with MSAA
@@ -475,13 +477,9 @@ void RenderForwardTransparent( RenderGraph renderGraph,
// It doesn't really matter what gets bound here since the color mask state set will prevent this from ever being written to. However, we still need to bind something
// to avoid warnings about unbound render targets. The following rendertarget could really be anything if renderVelocitiesForTransparent
// Create a new target here should reuse existing already released one
- mrt1 = renderGraph.CreateTexture(new TextureDesc(Vector2.one, true, true) { colorFormat = GraphicsFormat.R8G8B8A8_SRGB, name = "Transparency Velocity Dummy" });
+ passData.renderTarget[1] = builder.CreateTransientTexture(new TextureDesc(Vector2.one, true, true) { colorFormat = GraphicsFormat.R8G8B8A8_SRGB, name = "Transparency Velocity Dummy" });
}
- passData.renderTargetCount = 2;
- passData.renderTarget[0] = builder.WriteTexture(colorBuffer);
- passData.renderTarget[1] = builder.WriteTexture(mrt1);
-
if (colorPyramid != null && hdCamera.frameSettings.IsEnabled(FrameSettingsField.Refraction) && !preRefractionPass)
{
builder.ReadTexture(colorPyramid.Value);
@@ -783,16 +781,16 @@ void RenderForwardError(RenderGraph renderGraph,
class RenderSkyPassData
{
- public VisualEnvironment visualEnvironment;
- public Light sunLight;
- public HDCamera hdCamera;
+ public VisualEnvironment visualEnvironment;
+ public Light sunLight;
+ public HDCamera hdCamera;
public TextureHandle volumetricLighting;
public TextureHandle colorBuffer;
public TextureHandle depthStencilBuffer;
public TextureHandle intermediateBuffer;
- public DebugDisplaySettings debugDisplaySettings;
- public SkyManager skyManager;
- public int frameCount;
+ public DebugDisplaySettings debugDisplaySettings;
+ public SkyManager skyManager;
+ public int frameCount;
}
void RenderSky(RenderGraph renderGraph, HDCamera hdCamera, TextureHandle colorBuffer, TextureHandle volumetricLighting, TextureHandle depthStencilBuffer, TextureHandle depthTexture)
@@ -810,7 +808,7 @@ void RenderSky(RenderGraph renderGraph, HDCamera hdCamera, TextureHandle colorBu
passData.volumetricLighting = builder.ReadTexture(volumetricLighting);
passData.colorBuffer = builder.WriteTexture(colorBuffer);
passData.depthStencilBuffer = builder.WriteTexture(depthStencilBuffer);
- passData.intermediateBuffer = builder.WriteTexture(renderGraph.CreateTexture(colorBuffer));
+ passData.intermediateBuffer = builder.CreateTransientTexture(colorBuffer);
passData.debugDisplaySettings = m_CurrentDebugDisplaySettings;
passData.skyManager = m_SkyManager;
passData.frameCount = m_FrameCount;
@@ -928,8 +926,8 @@ class RenderDistortionPassData
public Vector4 size;
}
- void RenderDistortion( RenderGraph renderGraph,
- HDCamera hdCamera,
+ void RenderDistortion( RenderGraph renderGraph,
+ HDCamera hdCamera,
TextureHandle colorBuffer,
TextureHandle depthStencilBuffer,
TextureHandle colorPyramidBuffer,
diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.SubsurfaceScattering.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.SubsurfaceScattering.cs
index 1830c6829c9..1204288ec02 100644
--- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.SubsurfaceScattering.cs
+++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.SubsurfaceScattering.cs
@@ -53,9 +53,9 @@ void RenderSubsurfaceScattering(RenderGraph renderGraph, HDCamera hdCamera, Text
passData.sssBuffer = builder.ReadTexture(lightingBuffers.sssBuffer);
if (passData.parameters.needTemporaryBuffer)
{
- passData.cameraFilteringBuffer = builder.WriteTexture(renderGraph.CreateTexture(
+ passData.cameraFilteringBuffer = builder.CreateTransientTexture(
new TextureDesc(Vector2.one, true, true)
- { colorFormat = GraphicsFormat.B10G11R11_UFloatPack32, enableRandomWrite = true, clearBuffer = true, clearColor = Color.clear, name = "SSSCameraFiltering" }));
+ { colorFormat = GraphicsFormat.B10G11R11_UFloatPack32, enableRandomWrite = true, clearBuffer = true, clearColor = Color.clear, name = "SSSCameraFiltering" });
}
builder.SetRenderFunc(
diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.cs
index f9e29a6c68e..5aaa678d6c8 100644
--- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.cs
+++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.cs
@@ -154,6 +154,7 @@ internal static Volume GetOrCreateDefaultVolume()
Material m_DebugFullScreen;
MaterialPropertyBlock m_DebugFullScreenPropertyBlock = new MaterialPropertyBlock();
Material m_DebugColorPicker;
+ Material m_DebugExposure;
Material m_ErrorMaterial;
Material m_Blit;
@@ -770,13 +771,8 @@ void SetRenderingFeatures()
#if UNITY_EDITOR
// HDRP always enable baking of cookie by default
- #if UNITY_2020_2_OR_NEWER
m_PreviousEnableCookiesInLightmapper = UnityEditor.EditorSettings.enableCookiesInLightmapper;
UnityEditor.EditorSettings.enableCookiesInLightmapper = true;
- #else
- m_PreviousEnableCookiesInLightmapper = UnityEditor.EditorSettings.disableCookiesInLightmapper;
- UnityEditor.EditorSettings.disableCookiesInLightmapper = false;
- #endif
SceneViewDrawMode.SetupDrawMode();
@@ -872,11 +868,7 @@ void UnsetRenderingFeatures()
Lightmapping.ResetDelegate();
#if UNITY_EDITOR
- #if UNITY_2020_2_OR_NEWER
UnityEditor.EditorSettings.enableCookiesInLightmapper = m_PreviousEnableCookiesInLightmapper;
- #else
- UnityEditor.EditorSettings.disableCookiesInLightmapper = m_PreviousEnableCookiesInLightmapper;
- #endif
#endif
}
@@ -947,6 +939,7 @@ void InitializeDebugMaterials()
m_DebugDisplayLatlong = CoreUtils.CreateEngineMaterial(defaultResources.shaders.debugDisplayLatlongPS);
m_DebugFullScreen = CoreUtils.CreateEngineMaterial(defaultResources.shaders.debugFullScreenPS);
m_DebugColorPicker = CoreUtils.CreateEngineMaterial(defaultResources.shaders.debugColorPickerPS);
+ m_DebugExposure = CoreUtils.CreateEngineMaterial(defaultResources.shaders.debugExposurePS);
m_Blit = CoreUtils.CreateEngineMaterial(defaultResources.shaders.blitPS);
m_ErrorMaterial = CoreUtils.CreateEngineMaterial("Hidden/InternalErrorShader");
@@ -1032,6 +1025,7 @@ protected override void Dispose(bool disposing)
CoreUtils.Destroy(m_DebugDisplayLatlong);
CoreUtils.Destroy(m_DebugFullScreen);
CoreUtils.Destroy(m_DebugColorPicker);
+ CoreUtils.Destroy(m_DebugExposure);
CoreUtils.Destroy(m_Blit);
CoreUtils.Destroy(m_BlitTexArray);
CoreUtils.Destroy(m_BlitTexArraySingleSlice);
@@ -1266,8 +1260,10 @@ void CopyDepthBufferIfNeeded(HDCamera hdCamera, CommandBuffer cmd)
struct BuildCoarseStencilAndResolveParameters
{
- public HDCamera hdCamera;
- public ComputeShader resolveStencilCS;
+ public HDCamera hdCamera;
+ public ComputeShader resolveStencilCS;
+ public int resolveKernel;
+ public bool resolveIsNecessary;
}
BuildCoarseStencilAndResolveParameters PrepareBuildCoarseStencilParameters(HDCamera hdCamera)
@@ -1275,6 +1271,19 @@ BuildCoarseStencilAndResolveParameters PrepareBuildCoarseStencilParameters(HDCam
var parameters = new BuildCoarseStencilAndResolveParameters();
parameters.hdCamera = hdCamera;
parameters.resolveStencilCS = defaultResources.shaders.resolveStencilCS;
+
+ bool MSAAEnabled = hdCamera.frameSettings.IsEnabled(FrameSettingsField.MSAA);
+
+ // The following features require a copy of the stencil, if none are active, no need to do the resolve.
+ bool resolveIsNecessary = GetFeatureVariantsEnabled(hdCamera.frameSettings);
+ resolveIsNecessary = resolveIsNecessary || hdCamera.IsSSREnabled()
+ || hdCamera.IsTransparentSSREnabled();
+ // We need the resolve only with msaa
+ parameters.resolveIsNecessary = resolveIsNecessary && MSAAEnabled;
+
+ int kernel = SampleCountToPassIndex(MSAAEnabled ? hdCamera.msaaSamples : MSAASamples.None);
+ parameters.resolveKernel = parameters.resolveIsNecessary ? kernel + 3 : kernel; // We have a different variant if we need to resolve to non-MSAA stencil
+
return parameters;
}
@@ -1292,31 +1301,18 @@ static void BuildCoarseStencilAndResolveIfNeeded(BuildCoarseStencilAndResolvePar
{
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.CoarseStencilGeneration)))
{
- var hdCamera = parameters.hdCamera;
- bool MSAAEnabled = hdCamera.frameSettings.IsEnabled(FrameSettingsField.MSAA);
-
- // The following features require a copy of the stencil, if none are active, no need to do the resolve.
- bool resolveIsNecessary = GetFeatureVariantsEnabled(hdCamera.frameSettings);
- resolveIsNecessary = resolveIsNecessary || hdCamera.IsSSREnabled()
- || hdCamera.IsTransparentSSREnabled();
-
- // We need the resolve only with msaa
- resolveIsNecessary = resolveIsNecessary && MSAAEnabled;
-
ComputeShader cs = parameters.resolveStencilCS;
- int kernel = SampleCountToPassIndex(MSAAEnabled ? hdCamera.msaaSamples : MSAASamples.None);
- kernel = resolveIsNecessary ? kernel + 3 : kernel; // We have a different variant if we need to resolve to non-MSAA stencil
- cmd.SetComputeBufferParam(cs, kernel, HDShaderIDs._CoarseStencilBuffer, coarseStencilBuffer);
- cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._StencilTexture, depthStencilBuffer, 0, RenderTextureSubElement.Stencil);
+ cmd.SetComputeBufferParam(cs, parameters.resolveKernel, HDShaderIDs._CoarseStencilBuffer, coarseStencilBuffer);
+ cmd.SetComputeTextureParam(cs, parameters.resolveKernel, HDShaderIDs._StencilTexture, depthStencilBuffer, 0, RenderTextureSubElement.Stencil);
- if (resolveIsNecessary)
+ if (parameters.resolveIsNecessary)
{
- cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputStencilBuffer, resolvedStencilBuffer);
+ cmd.SetComputeTextureParam(cs, parameters.resolveKernel, HDShaderIDs._OutputStencilBuffer, resolvedStencilBuffer);
}
- int coarseStencilWidth = HDUtils.DivRoundUp(hdCamera.actualWidth, 8);
- int coarseStencilHeight = HDUtils.DivRoundUp(hdCamera.actualHeight, 8);
- cmd.DispatchCompute(cs, kernel, coarseStencilWidth, coarseStencilHeight, hdCamera.viewCount);
+ int coarseStencilWidth = HDUtils.DivRoundUp(parameters.hdCamera.actualWidth, 8);
+ int coarseStencilHeight = HDUtils.DivRoundUp(parameters.hdCamera.actualHeight, 8);
+ cmd.DispatchCompute(cs, parameters.resolveKernel, coarseStencilWidth, coarseStencilHeight, parameters.hdCamera.viewCount);
}
}
@@ -2709,6 +2705,8 @@ void Callback(CommandBuffer c, HDCamera cam)
RenderTargetIdentifier postProcessDest = HDUtils.PostProcessIsFinalPass(hdCamera) ? target.id : m_IntermediateAfterPostProcessBuffer;
RenderPostProcess(cullingResults, hdCamera, postProcessDest, renderContext, cmd);
+ PushFullScreenExposureDebugTexture(cmd, m_IntermediateAfterPostProcessBuffer);
+
RenderCustomPass(renderContext, cmd, hdCamera, customPassCullingResults, CustomPassInjectionPoint.AfterPostProcess, aovRequest, aovCustomPassBuffers);
// Copy and rescale depth buffer for XR devices
@@ -4439,7 +4437,8 @@ unsafe void ApplyDebugDisplaySettings(HDCamera hdCamera, CommandBuffer cmd)
cmd.SetGlobalTexture(HDShaderIDs._DebugMatCapTexture, defaultResources.textures.matcapTex);
if (debugDisplayEnabledOrSceneLightingDisabled ||
- m_CurrentDebugDisplaySettings.data.colorPickerDebugSettings.colorPickerMode != ColorPickerDebugMode.None)
+ m_CurrentDebugDisplaySettings.data.colorPickerDebugSettings.colorPickerMode != ColorPickerDebugMode.None ||
+ m_CurrentDebugDisplaySettings.IsDebugExposureModeEnabled())
{
// This is for texture streaming
m_CurrentDebugDisplaySettings.UpdateMaterials();
@@ -4528,6 +4527,11 @@ void PushColorPickerDebugTexture(CommandBuffer cmd, HDCamera hdCamera, RTHandle
}
}
+ bool NeedExposureDebugMode(DebugDisplaySettings debugSettings)
+ {
+ return debugSettings.data.lightingDebugSettings.exposureDebugMode != ExposureDebugMode.None;
+ }
+
bool NeedsFullScreenDebugMode()
{
bool fullScreenDebugEnabled = m_CurrentDebugDisplaySettings.data.fullScreenDebugMode != FullScreenDebugMode.None;
@@ -4547,6 +4551,14 @@ void PushFullScreenLightingDebugTexture(HDCamera hdCamera, CommandBuffer cmd, RT
}
}
+ void PushFullScreenExposureDebugTexture(CommandBuffer cmd, RTHandle textureID)
+ {
+ if (m_CurrentDebugDisplaySettings.data.lightingDebugSettings.exposureDebugMode != ExposureDebugMode.None)
+ {
+ HDUtils.BlitCameraTexture(cmd, textureID, m_DebugFullScreenTempBuffer);
+ }
+ }
+
internal void PushFullScreenDebugTexture(HDCamera hdCamera, CommandBuffer cmd, RTHandle textureID, FullScreenDebugMode debugMode)
{
if (debugMode == m_CurrentDebugDisplaySettings.data.fullScreenDebugMode)
@@ -4591,6 +4603,10 @@ struct DebugParameters
// Color picker
public bool colorPickerEnabled;
public Material colorPickerMaterial;
+
+ // Exposure
+ public bool exposureDebugEnabled;
+ public Material debugExposureMaterial;
}
DebugParameters PrepareDebugParameters(HDCamera hdCamera, HDUtils.PackedMipChainInfo depthMipInfo)
@@ -4615,6 +4631,9 @@ DebugParameters PrepareDebugParameters(HDCamera hdCamera, HDUtils.PackedMipChain
parameters.colorPickerEnabled = NeedColorPickerDebug(parameters.debugDisplaySettings);
parameters.colorPickerMaterial = m_DebugColorPicker;
+ parameters.exposureDebugEnabled = NeedExposureDebugMode(parameters.debugDisplaySettings);
+ parameters.debugExposureMaterial = m_DebugExposure;
+
return parameters;
}
@@ -4662,6 +4681,82 @@ static void ResolveColorPickerDebug(in DebugParameters parameters,
HDUtils.DrawFullScreen(cmd, parameters.colorPickerMaterial, output);
}
+ static void RenderExposureDebug(in DebugParameters parameters,
+ RTHandle inputColorBuffer,
+ RTHandle postprocessedColorBuffer,
+ RTHandle currentExposure,
+ RTHandle prevExposure,
+ RTHandle debugExposureData,
+ RTHandle output,
+ HableCurve hableCurve,
+ int lutSize,
+ ComputeBuffer histogramBuffer,
+ CommandBuffer cmd)
+ {
+ // Grab exposure parameters
+ var exposureSettings = parameters.hdCamera.volumeStack.GetComponent();
+
+ Vector4 exposureParams = new Vector4(exposureSettings.compensation.value + parameters.debugDisplaySettings.data.lightingDebugSettings.debugExposure, exposureSettings.limitMin.value,
+ exposureSettings.limitMax.value, 0f);
+
+ Vector4 exposureVariants = new Vector4(1.0f, (int)exposureSettings.meteringMode.value, (int)exposureSettings.adaptationMode.value, 0.0f);
+ Vector2 histogramFraction = exposureSettings.histogramPercentages.value / 100.0f;
+ float evRange = exposureSettings.limitMax.value - exposureSettings.limitMin.value;
+ float histScale = 1.0f / Mathf.Max(1e-5f, evRange);
+ float histBias = -exposureSettings.limitMin.value * histScale;
+ Vector4 histogramParams = new Vector4(histScale, histBias, histogramFraction.x, histogramFraction.y);
+
+ parameters.debugExposureMaterial.SetVector(HDShaderIDs._HistogramExposureParams, histogramParams);
+ parameters.debugExposureMaterial.SetVector(HDShaderIDs._Variants, exposureVariants);
+ parameters.debugExposureMaterial.SetVector(HDShaderIDs._ExposureParams, exposureParams);
+ parameters.debugExposureMaterial.SetVector(HDShaderIDs._MousePixelCoord, HDUtils.GetMouseCoordinates(parameters.hdCamera));
+ parameters.debugExposureMaterial.SetTexture(HDShaderIDs._SourceTexture, inputColorBuffer);
+ parameters.debugExposureMaterial.SetTexture(HDShaderIDs._DebugFullScreenTexture, postprocessedColorBuffer);
+ parameters.debugExposureMaterial.SetTexture(HDShaderIDs._PreviousExposureTexture, prevExposure);
+ parameters.debugExposureMaterial.SetTexture(HDShaderIDs._ExposureTexture, currentExposure);
+ parameters.debugExposureMaterial.SetTexture(HDShaderIDs._ExposureWeightMask, exposureSettings.weightTextureMask.value);
+ parameters.debugExposureMaterial.SetBuffer(HDShaderIDs._HistogramBuffer, histogramBuffer);
+
+
+ int passIndex = 0;
+ if (parameters.debugDisplaySettings.data.lightingDebugSettings.exposureDebugMode == ExposureDebugMode.MeteringWeighted)
+ passIndex = 1;
+ if (parameters.debugDisplaySettings.data.lightingDebugSettings.exposureDebugMode == ExposureDebugMode.HistogramView)
+ {
+ parameters.debugExposureMaterial.SetTexture(HDShaderIDs._ExposureDebugTexture, debugExposureData);
+ var tonemappingSettings = parameters.hdCamera.volumeStack.GetComponent();
+
+ bool toneMapIsEnabled = parameters.hdCamera.frameSettings.IsEnabled(FrameSettingsField.Tonemapping);
+ var tonemappingMode = toneMapIsEnabled ? tonemappingSettings.mode.value : TonemappingMode.None;
+
+ bool drawTonemapCurve = tonemappingMode != TonemappingMode.None &&
+ parameters.debugDisplaySettings.data.lightingDebugSettings.showTonemapCurveAlongHistogramView;
+
+ parameters.debugExposureMaterial.SetVector(HDShaderIDs._ExposureDebugParams, new Vector4(drawTonemapCurve ? 1.0f : 0.0f, (int)tonemappingMode, 0, 0));
+ if (drawTonemapCurve)
+ {
+ if (tonemappingMode == TonemappingMode.Custom)
+ {
+ parameters.debugExposureMaterial.SetVector(HDShaderIDs._CustomToneCurve, hableCurve.uniforms.curve);
+ parameters.debugExposureMaterial.SetVector(HDShaderIDs._ToeSegmentA, hableCurve.uniforms.toeSegmentA);
+ parameters.debugExposureMaterial.SetVector(HDShaderIDs._ToeSegmentB, hableCurve.uniforms.toeSegmentB);
+ parameters.debugExposureMaterial.SetVector(HDShaderIDs._MidSegmentA, hableCurve.uniforms.midSegmentA);
+ parameters.debugExposureMaterial.SetVector(HDShaderIDs._MidSegmentB, hableCurve.uniforms.midSegmentB);
+ parameters.debugExposureMaterial.SetVector(HDShaderIDs._ShoSegmentA, hableCurve.uniforms.shoSegmentA);
+ parameters.debugExposureMaterial.SetVector(HDShaderIDs._ShoSegmentB, hableCurve.uniforms.shoSegmentB);
+ }
+ }
+ else if (tonemappingMode == TonemappingMode.External)
+ {
+ parameters.debugExposureMaterial.SetTexture(HDShaderIDs._LogLut3D, tonemappingSettings.lutTexture.value);
+ parameters.debugExposureMaterial.SetVector(HDShaderIDs._LogLut3D_Params, new Vector4(1f / lutSize, lutSize - 1f, tonemappingSettings.lutContribution.value, 0f));
+ }
+ passIndex = 2;
+ }
+
+ HDUtils.DrawFullScreen(cmd, parameters.debugExposureMaterial, output, null, passIndex);
+ }
+
static void RenderSkyReflectionOverlay(in DebugParameters debugParameters, CommandBuffer cmd, MaterialPropertyBlock mpb, ref float x, ref float y, float overlaySize)
{
var lightingDebug = debugParameters.debugDisplaySettings.data.lightingDebugSettings;
@@ -4704,6 +4799,12 @@ void RenderDebug(HDCamera hdCamera, CommandBuffer cmd, CullingResults cullResult
PushColorPickerDebugTexture(cmd, hdCamera, m_IntermediateAfterPostProcessBuffer);
}
+ if (debugParams.exposureDebugEnabled)
+ {
+ RenderExposureDebug(debugParams, m_CameraColorBuffer, m_DebugFullScreenTempBuffer,m_PostProcessSystem.GetPreviousExposureTexture(hdCamera), m_PostProcessSystem.GetExposureTexture(hdCamera),
+ m_PostProcessSystem.GetExposureDebugData(),m_IntermediateAfterPostProcessBuffer, m_PostProcessSystem.GetCustomToneMapCurve(), m_PostProcessSystem.GetLutSize(), m_PostProcessSystem.GetHistogramBuffer(), cmd);
+ }
+
// First resolve color picker
if (debugParams.colorPickerEnabled)
ResolveColorPickerDebug(debugParams, m_DebugColorPickerBuffer, m_IntermediateAfterPostProcessBuffer, cmd);
diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDStringConstants.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDStringConstants.cs
index 0f491337b7a..6db4c395dec 100644
--- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDStringConstants.cs
+++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDStringConstants.cs
@@ -83,6 +83,11 @@ static class HDShaderIDs
public static readonly int _ShadowmapAreaAtlas = Shader.PropertyToID("_ShadowmapAreaAtlas");
public static readonly int _ShadowmapCascadeAtlas = Shader.PropertyToID("_ShadowmapCascadeAtlas");
+ public static readonly int _CachedShadowmapAtlas = Shader.PropertyToID("_CachedShadowmapAtlas");
+ public static readonly int _CachedAreaLightShadowmapAtlas = Shader.PropertyToID("_CachedAreaLightShadowmapAtlas");
+ public static readonly int _CachedShadowAtlasSize = Shader.PropertyToID("_CachedShadowAtlasSize");
+ public static readonly int _CachedAreaShadowAtlasSize = Shader.PropertyToID("_CachedAreaShadowAtlasSize");
+
// Moment shadow map data
public static readonly int _MomentShadowAtlas = Shader.PropertyToID("_MomentShadowAtlas");
public static readonly int _MomentShadowmapSlotST = Shader.PropertyToID("_MomentShadowmapSlotST");
@@ -597,7 +602,12 @@ static class HDShaderIDs
public static readonly int _ExposureTexture = Shader.PropertyToID("_ExposureTexture");
public static readonly int _PrevExposureTexture = Shader.PropertyToID("_PrevExposureTexture");
public static readonly int _PreviousExposureTexture = Shader.PropertyToID("_PreviousExposureTexture");
+ public static readonly int _ExposureDebugTexture = Shader.PropertyToID("_ExposureDebugTexture");
public static readonly int _ExposureParams = Shader.PropertyToID("_ExposureParams");
+ public static readonly int _ExposureParams2 = Shader.PropertyToID("_ExposureParams2");
+ public static readonly int _ExposureDebugParams = Shader.PropertyToID("_ExposureDebugParams");
+ public static readonly int _HistogramExposureParams = Shader.PropertyToID("_HistogramExposureParams");
+ public static readonly int _HistogramBuffer = Shader.PropertyToID("_HistogramBuffer");
public static readonly int _AdaptationParams = Shader.PropertyToID("_AdaptationParams");
public static readonly int _ExposureCurveTexture = Shader.PropertyToID("_ExposureCurveTexture");
public static readonly int _ExposureWeightMask = Shader.PropertyToID("_ExposureWeightMask");
diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPipelineResources.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPipelineResources.cs
index 391ee0d57e1..3a98956a933 100644
--- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPipelineResources.cs
+++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPipelineResources.cs
@@ -25,6 +25,8 @@ public sealed class ShaderResources
public Shader debugFullScreenPS;
[Reload("Runtime/Debug/DebugColorPicker.Shader")]
public Shader debugColorPickerPS;
+ [Reload("Runtime/Debug/DebugExposure.Shader")]
+ public Shader debugExposurePS;
[Reload("Runtime/Debug/DebugLightVolumes.Shader")]
public Shader debugLightVolumePS;
[Reload("Runtime/Debug/DebugLightVolumes.compute")]
@@ -220,6 +222,8 @@ public sealed class ShaderResources
public ComputeShader nanKillerCS;
[Reload("Runtime/PostProcessing/Shaders/Exposure.compute")]
public ComputeShader exposureCS;
+ [Reload("Runtime/PostProcessing/Shaders/HistogramExposure.compute")]
+ public ComputeShader histogramExposureCS;
[Reload("Runtime/PostProcessing/Shaders/ApplyExposure.compute")]
public ComputeShader applyExposureCS;
[Reload("Runtime/PostProcessing/Shaders/UberPost.compute")]
diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Settings/FrameSettings.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Settings/FrameSettings.cs
index 1a6d9f69243..df665026da4 100644
--- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Settings/FrameSettings.cs
+++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Settings/FrameSettings.cs
@@ -315,7 +315,7 @@ public enum FrameSettingsField
[FrameSettingsField(2, displayedName: "SS Ambient Occlusion", positiveDependencies: new[] { AsyncCompute }, tooltip: "When enabled, HDRP calculates screen space ambient occlusion asynchronously.")]
SSAOAsync = 43,
// TODO: Enable thing when the render graph will be the default renderer.
- // [FrameSettingsField(2, displayedName: "Contact Shadows", positiveDependencies: new[] { AsyncCompute }, tooltip: "When enabled, HDRP calculates Contact Shadows asynchronously.")]
+ //[FrameSettingsField(2, displayedName: "Contact Shadows", positiveDependencies: new[] { AsyncCompute }, tooltip: "When enabled, HDRP calculates Contact Shadows asynchronously.")]
/// When enabled, HDRP calculates Contact Shadows asynchronously.
ContactShadowsAsync = 44,
/// When enabled, HDRP calculates volumetric voxelization asynchronously.
@@ -433,6 +433,7 @@ partial struct FrameSettings
(uint)FrameSettingsField.RayTracing,
(uint)FrameSettingsField.AlphaToMask,
(uint)FrameSettingsField.ProbeVolume,
+ (uint)FrameSettingsField.MSAA
}),
lodBias = 1,
sssQualityMode = SssQualityMode.FromQualitySettings,
@@ -670,7 +671,7 @@ public int GetResolvedSssSampleBudget(HDRenderPipelineAsset hdrp)
internal bool SSRRunsAsync() => SystemInfo.supportsAsyncCompute && bitDatas[(int)FrameSettingsField.AsyncCompute] && bitDatas[(int)FrameSettingsField.SSRAsync];
internal bool SSAORunsAsync() => SystemInfo.supportsAsyncCompute && bitDatas[(int)FrameSettingsField.AsyncCompute] && bitDatas[(int)FrameSettingsField.SSAOAsync];
// TODO: Re-enable this when the render graph will be used by default.
- internal bool ContactShadowsRunAsync() => SystemInfo.supportsAsyncCompute && bitDatas[(int)FrameSettingsField.AsyncCompute] && /* bitDatas[(int)FrameSettingsField.ContactShadowsAsync] */ false;
+ internal bool ContactShadowsRunAsync() => SystemInfo.supportsAsyncCompute && bitDatas[(int)FrameSettingsField.AsyncCompute] && /*bitDatas[(int)FrameSettingsField.ContactShadowsAsync]*/ false;
internal bool VolumeVoxelizationRunsAsync() => SystemInfo.supportsAsyncCompute && bitDatas[(int)FrameSettingsField.AsyncCompute] && bitDatas[(int)FrameSettingsField.VolumeVoxelizationsAsync];
/// Override a frameSettings according to a mask.
diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipelineResources/HDRenderPipelineResources.asset b/com.unity.render-pipelines.high-definition/Runtime/RenderPipelineResources/HDRenderPipelineResources.asset
index dcafb599476..add31419125 100644
--- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipelineResources/HDRenderPipelineResources.asset
+++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipelineResources/HDRenderPipelineResources.asset
@@ -21,12 +21,10 @@ MonoBehaviour:
type: 3}
debugViewTilesPS: {fileID: 4800000, guid: c7c2bd17b06ceb4468e14081aaf1b96f, type: 3}
debugFullScreenPS: {fileID: 4800000, guid: e874aca2df8300a488258738c31f85cf, type: 3}
- debugColorPickerPS: {fileID: 4800000, guid: 8137b807709e178498f22ed710864bb0,
- type: 3}
- debugLightVolumePS: {fileID: 4800000, guid: 8e706c0e71fcec34a8f5c9713e5e2943,
- type: 3}
- debugLightVolumeCS: {fileID: 7200000, guid: f5d5d21faef5cf445ac2c5d8ff9c4184,
- type: 3}
+ debugColorPickerPS: {fileID: 4800000, guid: 8137b807709e178498f22ed710864bb0, type: 3}
+ debugExposurePS: {fileID: 4800000, guid: 0ef322534f047a34c96d29419d56d17a, type: 3}
+ debugLightVolumePS: {fileID: 4800000, guid: 8e706c0e71fcec34a8f5c9713e5e2943, type: 3}
+ debugLightVolumeCS: {fileID: 7200000, guid: f5d5d21faef5cf445ac2c5d8ff9c4184, type: 3}
debugBlitQuad: {fileID: 4800000, guid: cf5ca5b6ef18b3f429ed707ee9ceac9f, type: 3}
deferredPS: {fileID: 4800000, guid: 00dd221e34a6ab349a1196b0f2fab693, type: 3}
colorPyramidPS: {fileID: 4800000, guid: 2fcfb8d92f45e4549b3f0bad5d0654bf, type: 3}
@@ -152,6 +150,7 @@ MonoBehaviour:
copyAlphaCS: {fileID: 7200000, guid: c2c7eb6611725264187721ef9df0354b, type: 3}
nanKillerCS: {fileID: 7200000, guid: 83982f199acf927499576a99abc9bea9, type: 3}
exposureCS: {fileID: 7200000, guid: 976d7bce54fae534fb9ec67e9c18570c, type: 3}
+ histogramExposureCS: {fileID: 7200000, guid: 222da48299136f34b8e3fb75ae9f8ac7, type: 3}
applyExposureCS: {fileID: 7200000, guid: 1a6fea1dc099b984d8f2b27d504dc096, type: 3}
uberPostCS: {fileID: 7200000, guid: f1bf52f7c71bffd4f91e6cd90d12a4f7, type: 3}
lutBuilder3DCS: {fileID: 7200000, guid: 37f2b1b0ecd6f1c439e4c1b4f2fdb524, type: 3}
diff --git a/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariablesGlobal.cs b/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariablesGlobal.cs
index b17250fb174..ca44206beb9 100644
--- a/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariablesGlobal.cs
+++ b/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariablesGlobal.cs
@@ -146,6 +146,8 @@ unsafe struct ShaderVariablesGlobal
public Vector4 _ShadowAtlasSize;
public Vector4 _CascadeShadowAtlasSize;
public Vector4 _AreaShadowAtlasSize;
+ public Vector4 _CachedShadowAtlasSize;
+ public Vector4 _CachedAreaShadowAtlasSize;
[HLSLArray(s_MaxEnv2DLight, typeof(Matrix4x4))]
public fixed float _Env2DCaptureVP[s_MaxEnv2DLight * 4 * 4];
diff --git a/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariablesGlobal.cs.hlsl b/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariablesGlobal.cs.hlsl
index eaa41b94a68..ce07922e4f1 100644
--- a/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariablesGlobal.cs.hlsl
+++ b/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariablesGlobal.cs.hlsl
@@ -72,6 +72,8 @@ GLOBAL_CBUFFER_START(ShaderVariablesGlobal, b0)
float4 _ShadowAtlasSize;
float4 _CascadeShadowAtlasSize;
float4 _AreaShadowAtlasSize;
+ float4 _CachedShadowAtlasSize;
+ float4 _CachedAreaShadowAtlasSize;
float4x4 _Env2DCaptureVP[32];
float4 _Env2DCaptureForward[32];
float4 _Env2DAtlasScaleOffset[32];
diff --git a/com.unity.template-hd/Assets/Settings/HDRenderPipelineAsset.asset b/com.unity.template-hd/Assets/Settings/HDRenderPipelineAsset.asset
index c69fe0f3ba7..d317e4963cc 100644
--- a/com.unity.template-hd/Assets/Settings/HDRenderPipelineAsset.asset
+++ b/com.unity.template-hd/Assets/Settings/HDRenderPipelineAsset.asset
@@ -12,7 +12,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 0cf1dab834d4ec34195b920ea7bbf9ec, type: 3}
m_Name: HDRenderPipelineAsset
m_EditorClassIdentifier:
- m_Version: 11
+ m_Version: 15
m_ObsoleteFrameSettings:
overrides: 0
enableShadow: 0
@@ -153,21 +153,25 @@ MonoBehaviour:
m_RenderPipelineRayTracingResources: {fileID: 0}
m_DefaultVolumeProfile: {fileID: 11400000, guid: 582adbd84082fdb4faf7cd4beb1ccd14,
type: 2}
- m_DefaultLookDevProfile: {fileID: 0}
+ m_DefaultLookDevProfile: {fileID: 11400000, guid: 254c4fe87beb7be4fa72e1681edbed02,
+ type: 2}
m_RenderingPathDefaultCameraFrameSettings:
bitDatas:
- data1: 70297877217117
- data2: 4539628424926265344
+ data1: 140668768878429
+ data2: 4539628425463136256
lodBias: 1
lodBiasMode: 0
lodBiasQualityLevel: 0
maximumLODLevel: 0
maximumLODLevelMode: 0
maximumLODLevelQualityLevel: 0
+ sssQualityMode: 0
+ sssQualityLevel: 0
+ sssCustomSampleBudget: 20
materialQuality: 0
m_RenderingPathDefaultBakedOrCustomReflectionFrameSettings:
bitDatas:
- data1: 69373911135005
+ data1: 139742655312669
data2: 4539628424389459968
lodBias: 1
lodBiasMode: 0
@@ -175,10 +179,13 @@ MonoBehaviour:
maximumLODLevel: 0
maximumLODLevelMode: 0
maximumLODLevelQualityLevel: 0
+ sssQualityMode: 0
+ sssQualityLevel: 0
+ sssCustomSampleBudget: 20
materialQuality: 0
m_RenderingPathDefaultRealtimeReflectionFrameSettings:
bitDatas:
- data1: 69622750778125
+ data1: 139991494955789
data2: 4539628424389459968
lodBias: 1
lodBiasMode: 0
@@ -186,15 +193,21 @@ MonoBehaviour:
maximumLODLevel: 0
maximumLODLevelMode: 0
maximumLODLevelQualityLevel: 0
+ sssQualityMode: 0
+ sssQualityLevel: 0
+ sssCustomSampleBudget: 20
materialQuality: 0
m_RenderPipelineSettings:
supportShadowMask: 1
supportSSR: 0
+ supportSSRTransparent: 0
supportSSAO: 1
supportSubsurfaceScattering: 1
- increaseSssSampleCount: 0
+ sssSampleBudget:
+ m_Values: 140000002800000050000000
+ m_SchemaId:
+ m_Id: With3Levels
supportVolumetrics: 1
- increaseResolutionOfVolumetrics: 0
supportLightLayers: 0
lightLayerName0: Light Layer default
lightLayerName1: Light Layer 1
@@ -218,15 +231,23 @@ MonoBehaviour:
supportRuntimeDebugDisplay: 1
supportDitheringCrossFade: 1
supportTerrainHole: 0
+ supportProbeVolume: 0
supportRayTracing: 0
- supportedRaytracingTier: 2
+ supportedRayTracingMode: 3
+ probeVolumeSettings:
+ atlasWidth: 128
+ atlasHeight: 128
+ atlasDepth: 512
+ atlasOctahedralDepthWidth: 2048
+ atlasOctahedralDepthHeight: 2048
lightLoopSettings:
- cookieSize: 128
- cookieTexArraySize: 16
+ cookieAtlasSize: 512
+ cookieFormat: 74
pointCookieSize: 128
cubeCookieTexArraySize: 16
- planarReflectionProbeCacheSize: 2
- planarReflectionTextureSize: 1024
+ cookieAtlasLastValidMip: 0
+ cookieTexArraySize: 16
+ planarReflectionAtlasSize: 4096
reflectionProbeCacheSize: 64
reflectionCubemapSize: 256
reflectionCacheCompressed: 0
@@ -241,6 +262,7 @@ MonoBehaviour:
maxAreaLightsOnScreen: 64
maxEnvLightsOnScreen: 64
maxDecalsOnScreen: 512
+ maxPlanarReflectionOnScreen: 16
hdShadowInitParams:
maxShadowRequests: 128
directionalShadowsDepthBits: 16
@@ -269,7 +291,8 @@ MonoBehaviour:
maxPunctualShadowMapResolution: 2048
maxAreaShadowMapResolution: 2048
supportScreenSpaceShadows: 0
- maxScreenSpaceShadows: 2
+ maxScreenSpaceShadowSlots: 4
+ screenSpaceShadowBufferFormat: 48
decalSettings:
drawDistance: 1000
atlasWidth: 4096
@@ -278,6 +301,7 @@ MonoBehaviour:
postProcessSettings:
m_LutSize: 32
lutFormat: 48
+ bufferFormat: 74
dynamicResolutionSettings:
enabled: 0
maxPercentage: 100
@@ -293,6 +317,7 @@ MonoBehaviour:
xrSettings:
singlePass: 1
occlusionMesh: 1
+ cameraJitter: 0
postProcessQualitySettings:
NearBlurSampleCount: 030000000500000008000000
NearBlurMaxRadius:
diff --git a/com.unity.template-hd/ProjectSettings/EditorSettings.asset b/com.unity.template-hd/ProjectSettings/EditorSettings.asset
index 125f2ccef56..652e2f799ff 100644
--- a/com.unity.template-hd/ProjectSettings/EditorSettings.asset
+++ b/com.unity.template-hd/ProjectSettings/EditorSettings.asset
@@ -19,8 +19,9 @@ EditorSettings:
m_ProjectGenerationRootNamespace:
m_EnableTextureStreamingInEditMode: 1
m_EnableTextureStreamingInPlayMode: 1
+ m_EnableRoslynAnalyzers: 0
m_AsyncShaderCompilation: 1
- m_CachingShaderPreprocessor: 0
+ m_CachingShaderPreprocessor: 1
m_EnterPlayModeOptionsEnabled: 0
m_EnterPlayModeOptions: 3
m_UseLegacyProbeSampleCount: 0