347 lines
17 KiB
C#
347 lines
17 KiB
C#
using System.Collections.Generic;
|
|
using UnityEngine.Experimental.Rendering;
|
|
using UnityEngine.Experimental.Rendering.RenderGraphModule;
|
|
|
|
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
|
|
{
|
|
internal static ShadowResult ReadShadowResult(in ShadowResult shadowResult, RenderGraphBuilder builder)
|
|
{
|
|
var result = new ShadowResult();
|
|
|
|
if (shadowResult.punctualShadowResult.IsValid())
|
|
result.punctualShadowResult = builder.ReadTexture(shadowResult.punctualShadowResult);
|
|
if (shadowResult.directionalShadowResult.IsValid())
|
|
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;
|
|
}
|
|
|
|
internal void RenderShadows(RenderGraph renderGraph, in ShaderVariablesGlobal globalCB, HDCamera hdCamera, CullingResults cullResults, ref ShadowResult result)
|
|
{
|
|
InvalidateAtlasOutputsIfNeeded();
|
|
|
|
// Avoid to do any commands if there is no shadow to draw
|
|
if (m_ShadowRequestCount != 0 &&
|
|
(hdCamera.frameSettings.IsEnabled(FrameSettingsField.OpaqueObjects) || hdCamera.frameSettings.IsEnabled(FrameSettingsField.TransparentObjects)))
|
|
{
|
|
result.cachedPunctualShadowResult = cachedShadowManager.punctualShadowAtlas.RenderShadows(renderGraph, cullResults, globalCB, hdCamera.frameSettings, "Cached Punctual Lights Shadows rendering");
|
|
cachedShadowManager.punctualShadowAtlas.AddBlitRequestsForUpdatedShadows(m_Atlas);
|
|
|
|
if (ShaderConfig.s_AreaLights == 1)
|
|
{
|
|
result.cachedAreaShadowResult = cachedShadowManager.areaShadowAtlas.RenderShadows(renderGraph, cullResults, globalCB, hdCamera.frameSettings, "Cached Area Lights Shadows rendering");
|
|
cachedShadowManager.areaShadowAtlas.AddBlitRequestsForUpdatedShadows(m_AreaLightShadowAtlas);
|
|
}
|
|
|
|
BlitCachedShadows(renderGraph);
|
|
|
|
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");
|
|
if (ShaderConfig.s_AreaLights == 1)
|
|
result.areaShadowResult = m_AreaLightShadowAtlas.RenderShadows(renderGraph, cullResults, globalCB, hdCamera.frameSettings, "Area Light Shadows rendering");
|
|
}
|
|
|
|
// TODO RENDERGRAPH
|
|
// Not really good to bind things globally here (makes lifecycle of the textures fuzzy)
|
|
// Probably better to bind it explicitly where needed (deferred lighting and forward/debug passes)
|
|
// We can probably remove this when we have only one code path and can clean things up a bit.
|
|
BindShadowGlobalResources(renderGraph, result);
|
|
}
|
|
|
|
internal void ReleaseSharedShadowAtlases(RenderGraph renderGraph)
|
|
{
|
|
cachedShadowManager.punctualShadowAtlas.CleanupRenderGraphOutput(renderGraph);
|
|
if (ShaderConfig.s_AreaLights == 1)
|
|
cachedShadowManager.areaShadowAtlas.CleanupRenderGraphOutput(renderGraph);
|
|
|
|
cachedShadowManager.DefragAtlas(HDLightType.Point);
|
|
cachedShadowManager.DefragAtlas(HDLightType.Spot);
|
|
if (ShaderConfig.s_AreaLights == 1)
|
|
cachedShadowManager.DefragAtlas(HDLightType.Area);
|
|
}
|
|
|
|
void InvalidateAtlasOutputsIfNeeded()
|
|
{
|
|
cachedShadowManager.punctualShadowAtlas.InvalidateOutputIfNeeded();
|
|
m_Atlas.InvalidateOutputIfNeeded();
|
|
m_CascadeAtlas.InvalidateOutputIfNeeded();
|
|
if (ShaderConfig.s_AreaLights == 1)
|
|
{
|
|
cachedShadowManager.areaShadowAtlas.InvalidateOutputIfNeeded();
|
|
m_AreaLightShadowAtlas.InvalidateOutputIfNeeded();
|
|
}
|
|
}
|
|
|
|
class BindShadowGlobalResourcesPassData
|
|
{
|
|
public ShadowResult shadowResult;
|
|
}
|
|
|
|
|
|
static void BindAtlasTexture(RenderGraphContext ctx, TextureHandle texture, int shaderId)
|
|
{
|
|
if (texture.IsValid())
|
|
ctx.cmd.SetGlobalTexture(shaderId, texture);
|
|
else
|
|
ctx.cmd.SetGlobalTexture(shaderId, ctx.defaultResources.blackTexture);
|
|
}
|
|
|
|
void BindShadowGlobalResources(RenderGraph renderGraph, in ShadowResult shadowResult)
|
|
{
|
|
using (var builder = renderGraph.AddRenderPass<BindShadowGlobalResourcesPassData>("BindShadowGlobalResources", out var passData))
|
|
{
|
|
passData.shadowResult = ReadShadowResult(shadowResult, builder);
|
|
builder.AllowPassCulling(false);
|
|
builder.SetRenderFunc(
|
|
(BindShadowGlobalResourcesPassData data, RenderGraphContext ctx) =>
|
|
{
|
|
BindAtlasTexture(ctx, data.shadowResult.punctualShadowResult, HDShaderIDs._ShadowmapAtlas);
|
|
BindAtlasTexture(ctx, data.shadowResult.directionalShadowResult, HDShaderIDs._ShadowmapCascadeAtlas);
|
|
BindAtlasTexture(ctx, data.shadowResult.areaShadowResult, HDShaderIDs._ShadowmapAreaAtlas);
|
|
BindAtlasTexture(ctx, data.shadowResult.cachedPunctualShadowResult, HDShaderIDs._CachedShadowmapAtlas);
|
|
BindAtlasTexture(ctx, data.shadowResult.cachedAreaShadowResult, HDShaderIDs._CachedAreaLightShadowmapAtlas);
|
|
});
|
|
}
|
|
}
|
|
|
|
class BlitCachedShadowPassData
|
|
{
|
|
public TextureHandle sourceCachedAtlas;
|
|
public TextureHandle atlasTexture;
|
|
|
|
public HDDynamicShadowAtlas.ShadowBlitParameters shadowBlitParameters;
|
|
}
|
|
|
|
internal void BlitCachedShadows(RenderGraph renderGraph)
|
|
{
|
|
if (m_Atlas.HasPendingBlitsRequests())
|
|
{
|
|
using (var builder = renderGraph.AddRenderPass<BlitCachedShadowPassData>("Blit Punctual Mixed Cached Shadows", out var passData, ProfilingSampler.Get(HDProfileId.BlitPunctualMixedCachedShadowMaps)))
|
|
{
|
|
passData.shadowBlitParameters = m_Atlas.PrepareShadowBlitParameters(cachedShadowManager.punctualShadowAtlas, m_BlitShadowMaterial, m_BlitShadowPropertyBlock);
|
|
|
|
passData.sourceCachedAtlas = builder.ReadTexture(cachedShadowManager.punctualShadowAtlas.GetOutputTexture(renderGraph));
|
|
passData.atlasTexture = builder.WriteTexture(m_Atlas.GetOutputTexture(renderGraph));
|
|
|
|
builder.SetRenderFunc(
|
|
(BlitCachedShadowPassData data, RenderGraphContext ctx) =>
|
|
{
|
|
HDDynamicShadowAtlas.BlitCachedIntoAtlas(data.shadowBlitParameters, data.atlasTexture, data.sourceCachedAtlas, ctx.cmd);
|
|
});
|
|
}
|
|
}
|
|
|
|
if (ShaderConfig.s_AreaLights == 1 && m_AreaLightShadowAtlas.HasPendingBlitsRequests())
|
|
{
|
|
using (var builder = renderGraph.AddRenderPass<BlitCachedShadowPassData>("Blit Area Mixed Cached Shadows", out var passData, ProfilingSampler.Get(HDProfileId.BlitAreaMixedCachedShadowMaps)))
|
|
{
|
|
passData.shadowBlitParameters = m_AreaLightShadowAtlas.PrepareShadowBlitParameters(cachedShadowManager.areaShadowAtlas, m_BlitShadowMaterial, m_BlitShadowPropertyBlock);
|
|
|
|
passData.sourceCachedAtlas = builder.ReadTexture(cachedShadowManager.areaShadowAtlas.GetOutputTexture(renderGraph));
|
|
passData.atlasTexture = builder.WriteTexture(m_AreaLightShadowAtlas.GetOutputTexture(renderGraph));
|
|
|
|
builder.SetRenderFunc(
|
|
(BlitCachedShadowPassData data, RenderGraphContext ctx) =>
|
|
{
|
|
HDDynamicShadowAtlas.BlitCachedIntoAtlas(data.shadowBlitParameters, data.atlasTexture, data.sourceCachedAtlas, ctx.cmd);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
partial class HDShadowAtlas
|
|
{
|
|
bool m_UseSharedTexture;
|
|
|
|
protected TextureHandle m_Output;
|
|
|
|
|
|
public TextureDesc GetShadowMapTextureDesc()
|
|
{
|
|
return new TextureDesc(width, height)
|
|
{ filterMode = m_FilterMode, depthBufferBits = m_DepthBufferBits, isShadowMap = true, name = m_Name };
|
|
}
|
|
|
|
TextureDesc GetMomentAtlasDesc(string name)
|
|
{
|
|
return new TextureDesc(width / 2, height / 2)
|
|
{ colorFormat = GraphicsFormat.R32G32_SFloat, useMipMap = true, autoGenerateMips = false, name = name, enableRandomWrite = true };
|
|
}
|
|
|
|
TextureDesc GetImprovedMomentAtlasDesc()
|
|
{
|
|
return new TextureDesc(width, height)
|
|
{ colorFormat = GraphicsFormat.R32G32B32A32_SFloat, name = m_MomentName, enableRandomWrite = true };
|
|
}
|
|
|
|
TextureDesc GetAtlasDesc()
|
|
{
|
|
switch (m_BlurAlgorithm)
|
|
{
|
|
case (BlurAlgorithm.None):
|
|
return GetShadowMapTextureDesc();
|
|
case BlurAlgorithm.EVSM:
|
|
return GetMomentAtlasDesc(m_MomentName);
|
|
case BlurAlgorithm.IM:
|
|
return GetImprovedMomentAtlasDesc();
|
|
}
|
|
|
|
return default;
|
|
}
|
|
|
|
public void InvalidateOutputIfNeeded()
|
|
{
|
|
// Since we now store the output TextureHandle (because we only want to create the texture once depending on the control flow and because of shared textures),
|
|
// we need to be careful not to keep a "valid" handle when it's not a shared resource.
|
|
// Indeed, if for example we don't render with the atlas for a few frames, this handle will "look" valid (with a valid index internally) but its index will not match any valid resource.
|
|
// To avoid that, we invalidate it explicitly at the start of every frame if it's not a shared resource.
|
|
if (!m_UseSharedTexture)
|
|
{
|
|
m_Output = TextureHandle.nullHandle;
|
|
}
|
|
}
|
|
|
|
public TextureHandle GetOutputTexture(RenderGraph renderGraph)
|
|
{
|
|
if (m_UseSharedTexture)
|
|
{
|
|
Debug.Assert(m_Output.IsValid());
|
|
return m_Output; // Should always be valid.
|
|
}
|
|
else
|
|
{
|
|
renderGraph.CreateTextureIfInvalid(GetAtlasDesc(), ref m_Output);
|
|
return m_Output;
|
|
}
|
|
}
|
|
|
|
protected void InitializeRenderGraphOutput(RenderGraph renderGraph, bool useSharedTexture)
|
|
{
|
|
// TODO RENDERGRAPH remove null tests when we have only one path. RenderGraph should always be present.
|
|
if (renderGraph != null)
|
|
{
|
|
// First release if not needed anymore.
|
|
if (m_UseSharedTexture)
|
|
{
|
|
Debug.Assert(useSharedTexture, "Shadow atlas can't go from shared to non-shared texture");
|
|
}
|
|
|
|
m_UseSharedTexture = useSharedTexture;
|
|
// Else it's created on the fly like a regular render graph texture.
|
|
// Also when using shared texture (for static shadows) we want to manage lifetime manually. Otherwise this would break static shadow caching.
|
|
if (m_UseSharedTexture)
|
|
m_Output = renderGraph.CreateSharedTexture(GetAtlasDesc(), explicitRelease: true);
|
|
}
|
|
}
|
|
|
|
internal void CleanupRenderGraphOutput(RenderGraph renderGraph)
|
|
{
|
|
if (m_UseSharedTexture && renderGraph != null && m_Output.IsValid())
|
|
{
|
|
renderGraph.ReleaseSharedTexture(m_Output);
|
|
m_UseSharedTexture = false;
|
|
m_Output = TextureHandle.nullHandle;
|
|
}
|
|
}
|
|
|
|
class RenderShadowsPassData
|
|
{
|
|
public TextureHandle atlasTexture;
|
|
public TextureHandle momentAtlasTexture1;
|
|
public TextureHandle momentAtlasTexture2;
|
|
public TextureHandle intermediateSummedAreaTexture;
|
|
public TextureHandle summedAreaTexture;
|
|
|
|
public ConstantBuffer<ShaderVariablesGlobal> constantBuffer;
|
|
|
|
public RenderShadowsParameters parameters;
|
|
public ShadowDrawingSettings shadowDrawSettings;
|
|
|
|
public bool isRenderingOnACache;
|
|
}
|
|
|
|
internal TextureHandle RenderShadows(RenderGraph renderGraph, CullingResults cullResults, in ShaderVariablesGlobal globalCB, FrameSettings frameSettings, string shadowPassName)
|
|
{
|
|
if (m_ShadowRequests.Count == 0)
|
|
{
|
|
return renderGraph.defaultResources.blackTexture;
|
|
}
|
|
|
|
using (var builder = renderGraph.AddRenderPass<RenderShadowsPassData>(shadowPassName, out var passData, ProfilingSampler.Get(HDProfileId.RenderShadowMaps)))
|
|
{
|
|
passData.parameters = PrepareRenderShadowsParameters(globalCB);
|
|
// TODO: Get rid of this and refactor to use the same kind of API than RendererList
|
|
passData.shadowDrawSettings = new ShadowDrawingSettings(cullResults, 0);
|
|
passData.shadowDrawSettings.useRenderingLayerMaskTest = frameSettings.IsEnabled(FrameSettingsField.LightLayers);
|
|
passData.isRenderingOnACache = m_IsACacheForShadows;
|
|
passData.constantBuffer = m_GlobalConstantBuffer;
|
|
|
|
if (passData.parameters.blurAlgorithm == BlurAlgorithm.EVSM)
|
|
{
|
|
passData.atlasTexture = builder.WriteTexture(renderGraph.CreateTexture(GetShadowMapTextureDesc()));
|
|
passData.momentAtlasTexture1 = builder.WriteTexture(GetOutputTexture(renderGraph));
|
|
passData.momentAtlasTexture2 = builder.WriteTexture(renderGraph.CreateTexture(GetMomentAtlasDesc(m_MomentCopyName)));
|
|
}
|
|
else if (passData.parameters.blurAlgorithm == BlurAlgorithm.IM)
|
|
{
|
|
passData.atlasTexture = builder.WriteTexture(renderGraph.CreateTexture(GetShadowMapTextureDesc()));
|
|
passData.momentAtlasTexture1 = builder.WriteTexture(GetOutputTexture(renderGraph));
|
|
passData.intermediateSummedAreaTexture = builder.WriteTexture(renderGraph.CreateTexture(new TextureDesc(width, height)
|
|
{ colorFormat = GraphicsFormat.R32G32B32A32_SInt, name = m_IntermediateSummedAreaName, enableRandomWrite = true }));
|
|
passData.summedAreaTexture = builder.WriteTexture(renderGraph.CreateTexture(new TextureDesc(width, height)
|
|
{ colorFormat = GraphicsFormat.R32G32B32A32_SInt, name = m_SummedAreaName, enableRandomWrite = true }));
|
|
}
|
|
else
|
|
{
|
|
passData.atlasTexture = builder.WriteTexture(GetOutputTexture(renderGraph));
|
|
}
|
|
|
|
builder.SetRenderFunc(
|
|
(RenderShadowsPassData data, RenderGraphContext context) =>
|
|
{
|
|
RenderShadows(data.parameters,
|
|
data.atlasTexture,
|
|
data.shadowDrawSettings,
|
|
context.renderContext,
|
|
data.isRenderingOnACache,
|
|
data.constantBuffer,
|
|
context.cmd);
|
|
|
|
if (data.parameters.blurAlgorithm == BlurAlgorithm.EVSM)
|
|
{
|
|
RTHandle[] momentTextures = context.renderGraphPool.GetTempArray<RTHandle>(2);
|
|
momentTextures[0] = data.momentAtlasTexture1;
|
|
momentTextures[1] = data.momentAtlasTexture2;
|
|
|
|
EVSMBlurMoments(data.parameters, data.atlasTexture, momentTextures, data.isRenderingOnACache, context.cmd);
|
|
}
|
|
else if (data.parameters.blurAlgorithm == BlurAlgorithm.IM)
|
|
{
|
|
IMBlurMoment(data.parameters, data.atlasTexture, data.momentAtlasTexture1, data.intermediateSummedAreaTexture, data.summedAreaTexture, context.cmd);
|
|
}
|
|
});
|
|
|
|
return GetOutputTexture(renderGraph);
|
|
}
|
|
}
|
|
}
|
|
}
|