You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#jira UE-142732 #rb andrew.lauritzen ola.olsson #preflight 628d06a45c3ef99a7b2fffa3 [CL 20351116 by jason hoerner in ue5-main branch]
2304 lines
91 KiB
C++
2304 lines
91 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
/*=============================================================================
|
|
ShadowDepthRendering.cpp: Shadow depth rendering implementation
|
|
=============================================================================*/
|
|
|
|
#include "CoreMinimal.h"
|
|
#include "Stats/Stats.h"
|
|
#include "Misc/MemStack.h"
|
|
#include "RHIDefinitions.h"
|
|
#include "HAL/IConsoleManager.h"
|
|
#include "Async/TaskGraphInterfaces.h"
|
|
#include "RHI.h"
|
|
#include "HitProxies.h"
|
|
#include "ShaderParameters.h"
|
|
#include "RenderResource.h"
|
|
#include "RendererInterface.h"
|
|
#include "PrimitiveViewRelevance.h"
|
|
#include "UniformBuffer.h"
|
|
#include "Shader.h"
|
|
#include "StaticBoundShaderState.h"
|
|
#include "SceneUtils.h"
|
|
#include "Materials/Material.h"
|
|
#include "RHIStaticStates.h"
|
|
#include "PostProcess/SceneRenderTargets.h"
|
|
#include "GlobalShader.h"
|
|
#include "MaterialShaderType.h"
|
|
#include "MaterialShader.h"
|
|
#include "MeshMaterialShader.h"
|
|
#include "ShaderBaseClasses.h"
|
|
#include "ShadowRendering.h"
|
|
#include "SceneRendering.h"
|
|
#include "ScenePrivate.h"
|
|
#include "PostProcess/SceneFilterRendering.h"
|
|
#include "ScreenRendering.h"
|
|
#include "ClearQuad.h"
|
|
#include "PipelineStateCache.h"
|
|
#include "MeshPassProcessor.inl"
|
|
#include "VisualizeTexture.h"
|
|
#include "GPUScene.h"
|
|
#include "SceneTextureReductions.h"
|
|
#include "RendererModule.h"
|
|
#include "PixelShaderUtils.h"
|
|
#include "VirtualShadowMaps/VirtualShadowMapCacheManager.h"
|
|
#include "VirtualShadowMaps/VirtualShadowMapClipmap.h"
|
|
#include "ProfilingDebugging/CpuProfilerTrace.h"
|
|
#include "Rendering/NaniteResources.h"
|
|
#include "Rendering/NaniteStreamingManager.h"
|
|
|
|
DECLARE_GPU_DRAWCALL_STAT_NAMED(ShadowDepths, TEXT("Shadow Depths"));
|
|
|
|
IMPLEMENT_STATIC_UNIFORM_BUFFER_STRUCT(FShadowDepthPassUniformParameters, "ShadowDepthPass", SceneTextures);
|
|
IMPLEMENT_STATIC_UNIFORM_BUFFER_STRUCT(FMobileShadowDepthPassUniformParameters, "MobileShadowDepthPass", SceneTextures);
|
|
|
|
template<bool bUsingVertexLayers = false>
|
|
class TScreenVSForGS : public FScreenVS
|
|
{
|
|
public:
|
|
DECLARE_SHADER_TYPE(TScreenVSForGS, Global);
|
|
|
|
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
|
|
{
|
|
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5) && (!bUsingVertexLayers || (!RHISupportsGeometryShaders(Parameters.Platform) && RHISupportsVertexShaderLayer(Parameters.Platform)));
|
|
}
|
|
|
|
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
|
|
{
|
|
FScreenVS::ModifyCompilationEnvironment(Parameters, OutEnvironment);
|
|
OutEnvironment.SetDefine(TEXT("USING_LAYERS"), (uint32)(bUsingVertexLayers ? 1 : 0));
|
|
if (!bUsingVertexLayers)
|
|
{
|
|
OutEnvironment.CompilerFlags.Add(CFLAG_VertexToGeometryShader);
|
|
}
|
|
}
|
|
|
|
TScreenVSForGS() = default;
|
|
TScreenVSForGS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) :
|
|
FScreenVS(Initializer)
|
|
{}
|
|
};
|
|
|
|
IMPLEMENT_SHADER_TYPE(template<>, TScreenVSForGS<false>, TEXT("/Engine/Private/ScreenVertexShader.usf"), TEXT("MainForGS"), SF_Vertex);
|
|
IMPLEMENT_SHADER_TYPE(template<>, TScreenVSForGS<true>, TEXT("/Engine/Private/ScreenVertexShader.usf"), TEXT("MainForGS"), SF_Vertex);
|
|
|
|
|
|
static TAutoConsoleVariable<int32> CVarShadowForceSerialSingleRenderPass(
|
|
TEXT("r.Shadow.ForceSerialSingleRenderPass"),
|
|
0,
|
|
TEXT("Force Serial shadow passes to render in 1 pass."),
|
|
ECVF_RenderThreadSafe);
|
|
|
|
TAutoConsoleVariable<int32> CVarNaniteShadows(
|
|
TEXT("r.Shadow.Nanite"),
|
|
1,
|
|
TEXT("Enables shadows from Nanite meshes."),
|
|
ECVF_RenderThreadSafe);
|
|
|
|
static TAutoConsoleVariable<int32> CVarNaniteShadowsUseHZB(
|
|
TEXT("r.Shadow.NaniteUseHZB"),
|
|
1,
|
|
TEXT("Enables HZB for Nanite shadows."),
|
|
ECVF_RenderThreadSafe);
|
|
|
|
static TAutoConsoleVariable<float> CVarNaniteShadowsLODBias(
|
|
TEXT("r.Shadow.NaniteLODBias"),
|
|
1.0f,
|
|
TEXT("LOD bias for nanite geometry in shadows. 0 = full detail. >0 = reduced detail."),
|
|
ECVF_RenderThreadSafe);
|
|
|
|
static TAutoConsoleVariable<int32> CVarNaniteShadowsUpdateStreaming(
|
|
TEXT("r.Shadow.NaniteUpdateStreaming"),
|
|
1,
|
|
TEXT("Produce Nanite geometry streaming requests from shadow map rendering."),
|
|
ECVF_RenderThreadSafe);
|
|
|
|
static int32 GNaniteProgrammableRasterShadows = 1;
|
|
static FAutoConsoleVariableRef CNaniteProgrammableRasterShadows(
|
|
TEXT("r.Nanite.ProgrammableRaster.Shadows"),
|
|
GNaniteProgrammableRasterShadows,
|
|
TEXT("A toggle that allows Nanite programmable raster in shadow passes.\n")
|
|
TEXT(" 0: Programmable raster is disabled\n")
|
|
TEXT(" 1: Programmable raster is enabled (default)"),
|
|
ECVF_RenderThreadSafe);
|
|
|
|
extern int32 GNaniteShowStats;
|
|
extern int32 GEnableNonNaniteVSM;
|
|
|
|
namespace Nanite
|
|
{
|
|
extern bool IsStatFilterActive(const FString& FilterName);
|
|
extern bool IsStatFilterActiveForLight(const FLightSceneProxy* LightProxy);
|
|
extern FString GetFilterNameForLight(const FLightSceneProxy* LightProxy);
|
|
}
|
|
|
|
// Multiply PackedView.LODScale by return value when rendering Nanite shadows
|
|
static float ComputeNaniteShadowsLODScaleFactor()
|
|
{
|
|
return FMath::Pow(2.0f, -CVarNaniteShadowsLODBias.GetValueOnRenderThread());
|
|
}
|
|
|
|
void SetupShadowDepthPassUniformBuffer(
|
|
const FProjectedShadowInfo* ShadowInfo,
|
|
FRDGBuilder& GraphBuilder,
|
|
const FViewInfo& View,
|
|
FShadowDepthPassUniformParameters& ShadowDepthPassParameters)
|
|
{
|
|
static const auto CSMCachingCVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.Shadow.CSMCaching"));
|
|
const bool bCSMCachingEnabled = CSMCachingCVar && CSMCachingCVar->GetValueOnAnyThread() != 0;
|
|
|
|
SetupSceneTextureUniformParameters(GraphBuilder, View.GetSceneTexturesChecked(), View.FeatureLevel, ESceneTextureSetupMode::None, ShadowDepthPassParameters.SceneTextures);
|
|
|
|
ShadowDepthPassParameters.ProjectionMatrix = FTranslationMatrix44f(FVector3f(ShadowInfo->PreShadowTranslation - View.ViewMatrices.GetPreViewTranslation())) * ShadowInfo->TranslatedWorldToClipOuterMatrix; // LWC_TDOO: Precision loss?
|
|
ShadowDepthPassParameters.ViewMatrix = FMatrix44f(ShadowInfo->TranslatedWorldToView); // LWC_TODO: Precision loss
|
|
|
|
// Disable the SlopeDepthBias because we couldn't reconstruct the depth offset if it is not 0.0f when scrolling the cached shadow map.
|
|
ShadowDepthPassParameters.ShadowParams = FVector4f(ShadowInfo->GetShaderDepthBias(), bCSMCachingEnabled ? 0.0f : ShadowInfo->GetShaderSlopeDepthBias(), ShadowInfo->GetShaderMaxSlopeDepthBias(), ShadowInfo->bOnePassPointLightShadow ? 1 : ShadowInfo->InvMaxSubjectDepth);
|
|
ShadowDepthPassParameters.bClampToNearPlane = ShadowInfo->ShouldClampToNearPlane() ? 1.0f : 0.0f;
|
|
|
|
if (ShadowInfo->bOnePassPointLightShadow)
|
|
{
|
|
check(ShadowInfo->BorderSize == 0);
|
|
|
|
// offset from translated world space to (pre translated) shadow space
|
|
const FMatrix Translation = FTranslationMatrix(ShadowInfo->PreShadowTranslation - View.ViewMatrices.GetPreViewTranslation());
|
|
|
|
for (int32 FaceIndex = 0; FaceIndex < 6; FaceIndex++)
|
|
{
|
|
ShadowDepthPassParameters.ShadowViewProjectionMatrices[FaceIndex] = FMatrix44f(Translation * ShadowInfo->OnePassShadowViewProjectionMatrices[FaceIndex]); // LWC_TODO: Precision loss?
|
|
ShadowDepthPassParameters.ShadowViewMatrices[FaceIndex] = FMatrix44f(Translation * ShadowInfo->OnePassShadowViewMatrices[FaceIndex]);
|
|
}
|
|
}
|
|
|
|
ShadowDepthPassParameters.bRenderToVirtualShadowMap = false;
|
|
ShadowDepthPassParameters.VirtualSmPageTable = GraphBuilder.CreateSRV(GSystemTextures.GetDefaultStructuredBuffer(GraphBuilder, sizeof(uint32)));
|
|
ShadowDepthPassParameters.PackedNaniteViews = GraphBuilder.CreateSRV(GSystemTextures.GetDefaultStructuredBuffer(GraphBuilder, sizeof(Nanite::FPackedView)));
|
|
ShadowDepthPassParameters.PageRectBounds = GraphBuilder.CreateSRV(GSystemTextures.GetDefaultStructuredBuffer(GraphBuilder, sizeof(FIntVector4)));
|
|
|
|
FRDGTextureRef DepthBufferArray = GraphBuilder.CreateTexture( FRDGTextureDesc::Create2DArray( FIntPoint(4,4), PF_R32_UINT, FClearValueBinding::None, TexCreate_ShaderResource | TexCreate_UAV, 1 ), TEXT("Dummy-OutDepthBuffer") );
|
|
|
|
ShadowDepthPassParameters.OutDepthBufferArray = GraphBuilder.CreateUAV( DepthBufferArray );
|
|
}
|
|
|
|
void SetupShadowDepthPassUniformBuffer(
|
|
const FProjectedShadowInfo* ShadowInfo,
|
|
FRDGBuilder& GraphBuilder,
|
|
const FViewInfo& View,
|
|
FMobileShadowDepthPassUniformParameters& ShadowDepthPassParameters)
|
|
{
|
|
SetupMobileSceneTextureUniformParameters(GraphBuilder, View.GetSceneTexturesChecked(), EMobileSceneTextureSetupMode::None, ShadowDepthPassParameters.SceneTextures);
|
|
|
|
ShadowDepthPassParameters.ProjectionMatrix = FTranslationMatrix44f(FVector3f(ShadowInfo->PreShadowTranslation - View.ViewMatrices.GetPreViewTranslation())) * ShadowInfo->TranslatedWorldToClipOuterMatrix; // LWC_TODO: Precision loss
|
|
ShadowDepthPassParameters.ViewMatrix = FMatrix44f(ShadowInfo->TranslatedWorldToView);
|
|
|
|
ShadowDepthPassParameters.ShadowParams = FVector4f(ShadowInfo->GetShaderDepthBias(), ShadowInfo->GetShaderSlopeDepthBias(), ShadowInfo->GetShaderMaxSlopeDepthBias(), ShadowInfo->InvMaxSubjectDepth);
|
|
ShadowDepthPassParameters.bClampToNearPlane = ShadowInfo->ShouldClampToNearPlane() ? 1.0f : 0.0f;
|
|
}
|
|
|
|
void AddClearShadowDepthPass(FRDGBuilder& GraphBuilder, FRDGTextureRef Texture)
|
|
{
|
|
// Clear atlas depth, but ignore stencil.
|
|
auto* PassParameters = GraphBuilder.AllocParameters<FRenderTargetParameters>();
|
|
PassParameters->RenderTargets.DepthStencil = FDepthStencilBinding(Texture, ERenderTargetLoadAction::EClear, ERenderTargetLoadAction::ENoAction, FExclusiveDepthStencil::DepthWrite_StencilNop);
|
|
GraphBuilder.AddPass(RDG_EVENT_NAME("ClearShadowDepth"), PassParameters, ERDGPassFlags::Raster, [](FRHICommandList&) {});
|
|
}
|
|
|
|
void AddClearShadowDepthPass(FRDGBuilder& GraphBuilder, FRDGTextureRef Texture, const FProjectedShadowInfo* ProjectedShadowInfo)
|
|
{
|
|
// Clear atlas depth, but ignore stencil.
|
|
auto* PassParameters = GraphBuilder.AllocParameters<FRenderTargetParameters>();
|
|
PassParameters->RenderTargets.DepthStencil = FDepthStencilBinding(Texture, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ENoAction, FExclusiveDepthStencil::DepthWrite_StencilNop);
|
|
GraphBuilder.AddPass(RDG_EVENT_NAME("ClearShadowDepthTile"), PassParameters, ERDGPassFlags::Raster, [ProjectedShadowInfo](FRHICommandList& RHICmdList)
|
|
{
|
|
ProjectedShadowInfo->ClearDepth(RHICmdList);
|
|
});
|
|
}
|
|
|
|
class FShadowDepthShaderElementData : public FMeshMaterialShaderElementData
|
|
{
|
|
public:
|
|
int32 LayerId;
|
|
int32 bUseGpuSceneInstancing;
|
|
};
|
|
|
|
/**
|
|
* A vertex shader for rendering the depth of a mesh.
|
|
*/
|
|
class FShadowDepthVS : public FMeshMaterialShader
|
|
{
|
|
public:
|
|
DECLARE_INLINE_TYPE_LAYOUT(FShadowDepthVS, NonVirtual);
|
|
|
|
static bool ShouldCompilePermutation(const FMeshMaterialShaderPermutationParameters& Parameters)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void GetShaderBindings(
|
|
const FScene* Scene,
|
|
ERHIFeatureLevel::Type FeatureLevel,
|
|
const FPrimitiveSceneProxy* PrimitiveSceneProxy,
|
|
const FMaterialRenderProxy& MaterialRenderProxy,
|
|
const FMaterial& Material,
|
|
const FMeshPassProcessorRenderState& DrawRenderState,
|
|
const FShadowDepthShaderElementData& ShaderElementData,
|
|
FMeshDrawSingleShaderBindings& ShaderBindings) const
|
|
{
|
|
FMeshMaterialShader::GetShaderBindings(Scene, FeatureLevel, PrimitiveSceneProxy, MaterialRenderProxy, Material, DrawRenderState, ShaderElementData, ShaderBindings);
|
|
|
|
ShaderBindings.Add(LayerId, ShaderElementData.LayerId);
|
|
ShaderBindings.Add(bUseGpuSceneInstancing, ShaderElementData.bUseGpuSceneInstancing);
|
|
}
|
|
|
|
FShadowDepthVS() = default;
|
|
FShadowDepthVS(const ShaderMetaType::CompiledShaderInitializerType & Initializer) :
|
|
FMeshMaterialShader(Initializer)
|
|
{
|
|
LayerId.Bind(Initializer.ParameterMap, TEXT("LayerId"));
|
|
bUseGpuSceneInstancing.Bind(Initializer.ParameterMap, TEXT("bUseGpuSceneInstancing"));
|
|
}
|
|
|
|
private:
|
|
LAYOUT_FIELD(FShaderParameter, LayerId);
|
|
LAYOUT_FIELD(FShaderParameter, bUseGpuSceneInstancing);
|
|
};
|
|
|
|
enum EShadowDepthVertexShaderMode
|
|
{
|
|
VertexShadowDepth_PerspectiveCorrect,
|
|
VertexShadowDepth_OutputDepth,
|
|
VertexShadowDepth_OnePassPointLight,
|
|
VertexShadowDepth_VirtualShadowMap,
|
|
};
|
|
|
|
static TAutoConsoleVariable<int32> CVarSupportPointLightWholeSceneShadows(
|
|
TEXT("r.SupportPointLightWholeSceneShadows"),
|
|
1,
|
|
TEXT("Enables shadowcasting point lights."),
|
|
ECVF_ReadOnly | ECVF_RenderThreadSafe);
|
|
|
|
static bool MobileUsesPerspectiveCorrectShadowPermutation(EShaderPlatform ShaderPlatform)
|
|
{
|
|
// Required only for spotlight shadows on mobile
|
|
static FShaderPlatformCachedIniValue<bool> MobileEnableMovableSpotlightShadowsIniValue(TEXT("r.Mobile.EnableMovableSpotlightsShadow"));
|
|
const bool bMobileEnableMovableSpotlightShadows = (MobileEnableMovableSpotlightShadowsIniValue.Get(ShaderPlatform) != 0);
|
|
return bMobileEnableMovableSpotlightShadows;
|
|
}
|
|
|
|
|
|
static TAutoConsoleVariable<int32> CVarDetectVertexShaderLayerAtRuntime(
|
|
TEXT("r.Shadow.DetectVertexShaderLayerAtRuntime"),
|
|
0,
|
|
TEXT("Forces the compilation of the vslayer shader permutation even if the platform (RHI) does not declare compile-time support through RHISupportsVertexShaderLayer.")
|
|
TEXT("Enabled by default for windows/SM5 as DX11 almost universally supports this at runtime."),
|
|
ECVF_ReadOnly | ECVF_RenderThreadSafe);
|
|
|
|
|
|
/**
|
|
* A vertex shader for rendering the depth of a mesh.
|
|
*/
|
|
template <EShadowDepthVertexShaderMode ShaderMode, bool bUsePositionOnlyStream>
|
|
class TShadowDepthVS : public FShadowDepthVS
|
|
{
|
|
public:
|
|
DECLARE_SHADER_TYPE(TShadowDepthVS, MeshMaterial);
|
|
|
|
static bool ShouldCompilePermutation(const FMeshMaterialShaderPermutationParameters& Parameters)
|
|
{
|
|
const EShaderPlatform Platform = Parameters.Platform;
|
|
|
|
static const auto SupportAllShaderPermutationsVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.SupportAllShaderPermutations"));
|
|
const bool bForceAllPermutations = SupportAllShaderPermutationsVar && SupportAllShaderPermutationsVar->GetValueOnAnyThread() != 0;
|
|
const bool bSupportPointLightWholeSceneShadows = CVarSupportPointLightWholeSceneShadows.GetValueOnAnyThread() != 0 || bForceAllPermutations;
|
|
|
|
// Mobile only needs OutputDepth, and optionally PerspectiveCorrect
|
|
if (!IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM5) &&
|
|
!(ShaderMode == VertexShadowDepth_OutputDepth ||
|
|
(ShaderMode == VertexShadowDepth_PerspectiveCorrect && MobileUsesPerspectiveCorrectShadowPermutation(Platform))))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Compile VS layer permutation if RHI supports it unconditionally OR we have forced it on (default for DX11&12 at SM5)
|
|
static FShaderPlatformCachedIniValue<bool> DetectVertexShaderLayerRuntimeIniValue(TEXT("r.Shadow.DetectVertexShaderLayerAtRuntime"));
|
|
const bool bShouldCompileVSLayer = RHISupportsVertexShaderLayer(Platform) || DetectVertexShaderLayerRuntimeIniValue.Get(Platform) != 0;
|
|
if (ShaderMode == VertexShadowDepth_OnePassPointLight && !bShouldCompileVSLayer)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (ShaderMode == VertexShadowDepth_VirtualShadowMap && !DoesPlatformSupportNonNaniteVirtualShadowMaps(Platform))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
|
|
//Note: This logic needs to stay in sync with OverrideWithDefaultMaterialForShadowDepth!
|
|
return (Parameters.MaterialParameters.bIsSpecialEngineMaterial
|
|
// Masked and WPO materials need their shaders but cannot be used with a position only stream.
|
|
|| ((!Parameters.MaterialParameters.bWritesEveryPixelShadowPass || Parameters.MaterialParameters.bMaterialMayModifyMeshPosition) && !bUsePositionOnlyStream))
|
|
// Only compile one pass point light shaders for feature levels >= SM5
|
|
&& (ShaderMode != VertexShadowDepth_OnePassPointLight || IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM5))
|
|
// Only compile position-only shaders for vertex factories that support it. (Note: this assumes that a vertex factor which supports PositionOnly, supports also PositionAndNormalOnly)
|
|
&& (!bUsePositionOnlyStream || Parameters.VertexFactoryType->SupportsPositionOnly())
|
|
// Don't render ShadowDepth for translucent unlit materials
|
|
&& Parameters.MaterialParameters.bShouldCastDynamicShadows;
|
|
}
|
|
|
|
static void ModifyCompilationEnvironment(const FMaterialShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
|
|
{
|
|
FShadowDepthVS::ModifyCompilationEnvironment(Parameters, OutEnvironment);
|
|
OutEnvironment.SetDefine(TEXT("PERSPECTIVE_CORRECT_DEPTH"), (uint32)(ShaderMode == VertexShadowDepth_PerspectiveCorrect));
|
|
OutEnvironment.SetDefine(TEXT("ONEPASS_POINTLIGHT_SHADOW"), (uint32)(ShaderMode == VertexShadowDepth_OnePassPointLight));
|
|
OutEnvironment.SetDefine(TEXT("POSITION_ONLY"), (uint32)bUsePositionOnlyStream);
|
|
|
|
bool bEnableNonNaniteVSM = (ShaderMode == VertexShadowDepth_VirtualShadowMap);
|
|
OutEnvironment.SetDefine(TEXT("ENABLE_NON_NANITE_VSM"), bEnableNonNaniteVSM ? 1 : 0);
|
|
if (bEnableNonNaniteVSM)
|
|
{
|
|
FVirtualShadowMapArray::SetShaderDefines(OutEnvironment);
|
|
}
|
|
|
|
if (ShaderMode == VertexShadowDepth_OnePassPointLight)
|
|
{
|
|
OutEnvironment.CompilerFlags.Add(CFLAG_VertexUseAutoCulling);
|
|
}
|
|
}
|
|
|
|
TShadowDepthVS() = default;
|
|
TShadowDepthVS(const ShaderMetaType::CompiledShaderInitializerType & Initializer)
|
|
: FShadowDepthVS(Initializer)
|
|
{}
|
|
};
|
|
|
|
#define IMPLEMENT_SHADOW_DEPTH_SHADERMODE_SHADERS(ShaderMode) \
|
|
typedef TShadowDepthVS<ShaderMode, false> TShadowDepthVS##ShaderMode; \
|
|
IMPLEMENT_MATERIAL_SHADER_TYPE(template<>, TShadowDepthVS##ShaderMode, TEXT("/Engine/Private/ShadowDepthVertexShader.usf"), TEXT("Main"), SF_Vertex);
|
|
|
|
IMPLEMENT_SHADOW_DEPTH_SHADERMODE_SHADERS(VertexShadowDepth_PerspectiveCorrect);
|
|
IMPLEMENT_SHADOW_DEPTH_SHADERMODE_SHADERS(VertexShadowDepth_OutputDepth);
|
|
IMPLEMENT_SHADOW_DEPTH_SHADERMODE_SHADERS(VertexShadowDepth_OnePassPointLight);
|
|
IMPLEMENT_SHADOW_DEPTH_SHADERMODE_SHADERS(VertexShadowDepth_VirtualShadowMap);
|
|
|
|
// Position only vertex shaders.
|
|
typedef TShadowDepthVS<VertexShadowDepth_PerspectiveCorrect, true> TShadowDepthVSVertexShadowDepth_PerspectiveCorrectPositionOnly;
|
|
typedef TShadowDepthVS<VertexShadowDepth_OutputDepth, true> TShadowDepthVSVertexShadowDepth_OutputDepthPositionOnly;
|
|
typedef TShadowDepthVS<VertexShadowDepth_OnePassPointLight, true> TShadowDepthVSVertexShadowDepth_OnePassPointLightPositionOnly;
|
|
typedef TShadowDepthVS<VertexShadowDepth_VirtualShadowMap, true> TShadowDepthVSVertexShadowDepth_VirtualShadowMapPositionOnly;
|
|
IMPLEMENT_MATERIAL_SHADER_TYPE(template<>, TShadowDepthVSVertexShadowDepth_PerspectiveCorrectPositionOnly, TEXT("/Engine/Private/ShadowDepthVertexShader.usf"), TEXT("PositionOnlyMain"), SF_Vertex);
|
|
IMPLEMENT_MATERIAL_SHADER_TYPE(template<>, TShadowDepthVSVertexShadowDepth_OutputDepthPositionOnly, TEXT("/Engine/Private/ShadowDepthVertexShader.usf"), TEXT("PositionOnlyMain"), SF_Vertex);
|
|
IMPLEMENT_MATERIAL_SHADER_TYPE(template<>, TShadowDepthVSVertexShadowDepth_OnePassPointLightPositionOnly, TEXT("/Engine/Private/ShadowDepthVertexShader.usf"), TEXT("PositionOnlyMain"), SF_Vertex);
|
|
IMPLEMENT_MATERIAL_SHADER_TYPE(template<>, TShadowDepthVSVertexShadowDepth_VirtualShadowMapPositionOnly, TEXT("/Engine/Private/ShadowDepthVertexShader.usf"), TEXT("PositionOnlyMain"), SF_Vertex);
|
|
|
|
|
|
/**
|
|
* A pixel shader for rendering the depth of a mesh.
|
|
*/
|
|
class FShadowDepthBasePS : public FMeshMaterialShader
|
|
{
|
|
DECLARE_INLINE_TYPE_LAYOUT(FShadowDepthBasePS, NonVirtual);
|
|
public:
|
|
|
|
FShadowDepthBasePS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
|
|
: FMeshMaterialShader(Initializer)
|
|
{
|
|
const ERHIFeatureLevel::Type FeatureLevel = GetMaxSupportedFeatureLevel((EShaderPlatform)Initializer.Target.Platform);
|
|
|
|
if (FSceneInterface::GetShadingPath(FeatureLevel) == EShadingPath::Deferred)
|
|
{
|
|
PassUniformBuffer.Bind(Initializer.ParameterMap, FShadowDepthPassUniformParameters::StaticStructMetadata.GetShaderVariableName());
|
|
}
|
|
|
|
if (FSceneInterface::GetShadingPath(FeatureLevel) == EShadingPath::Mobile)
|
|
{
|
|
PassUniformBuffer.Bind(Initializer.ParameterMap, FMobileShadowDepthPassUniformParameters::StaticStructMetadata.GetShaderVariableName());
|
|
}
|
|
}
|
|
|
|
FShadowDepthBasePS() = default;
|
|
};
|
|
|
|
enum EShadowDepthPixelShaderMode
|
|
{
|
|
PixelShadowDepth_NonPerspectiveCorrect,
|
|
PixelShadowDepth_PerspectiveCorrect,
|
|
PixelShadowDepth_OnePassPointLight,
|
|
PixelShadowDepth_VirtualShadowMap
|
|
};
|
|
|
|
template <EShadowDepthPixelShaderMode ShaderMode>
|
|
class TShadowDepthPS : public FShadowDepthBasePS
|
|
{
|
|
DECLARE_SHADER_TYPE(TShadowDepthPS, MeshMaterial);
|
|
public:
|
|
|
|
static bool ShouldCompilePermutation(const FMeshMaterialShaderPermutationParameters& Parameters)
|
|
{
|
|
const EShaderPlatform Platform = Parameters.Platform;
|
|
|
|
// Mobile only needs NonPerspectiveCorrect, and optionally PerspectiveCorrect
|
|
if (!IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM5) &&
|
|
!(ShaderMode == PixelShadowDepth_NonPerspectiveCorrect ||
|
|
(ShaderMode == PixelShadowDepth_PerspectiveCorrect && MobileUsesPerspectiveCorrectShadowPermutation(Platform))))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (ShaderMode == PixelShadowDepth_VirtualShadowMap && !DoesPlatformSupportNonNaniteVirtualShadowMaps(Platform))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool bModeRequiresPS = ShaderMode == PixelShadowDepth_PerspectiveCorrect || ShaderMode == PixelShadowDepth_VirtualShadowMap;
|
|
|
|
//Note: This logic needs to stay in sync with OverrideWithDefaultMaterialForShadowDepth!
|
|
return (Parameters.MaterialParameters.bIsSpecialEngineMaterial
|
|
// Only compile for masked or lit translucent materials
|
|
|| !Parameters.MaterialParameters.bWritesEveryPixelShadowPass
|
|
|| (Parameters.MaterialParameters.bMaterialMayModifyMeshPosition && Parameters.MaterialParameters.bIsUsedWithInstancedStaticMeshes)
|
|
// This mode needs a pixel shader and WPO materials can't be overridden with default material.
|
|
|| (bModeRequiresPS && Parameters.MaterialParameters.bMaterialMayModifyMeshPosition))
|
|
// Don't render ShadowDepth for translucent unlit materials
|
|
&& Parameters.MaterialParameters.bShouldCastDynamicShadows;
|
|
}
|
|
|
|
static void ModifyCompilationEnvironment(const FMaterialShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
|
|
{
|
|
FShadowDepthBasePS::ModifyCompilationEnvironment(Parameters, OutEnvironment);
|
|
OutEnvironment.SetDefine(TEXT("PERSPECTIVE_CORRECT_DEPTH"), (uint32)(ShaderMode == PixelShadowDepth_PerspectiveCorrect));
|
|
OutEnvironment.SetDefine(TEXT("ONEPASS_POINTLIGHT_SHADOW"), (uint32)(ShaderMode == PixelShadowDepth_OnePassPointLight));
|
|
OutEnvironment.SetDefine(TEXT("VIRTUAL_TEXTURE_TARGET"), (uint32)(ShaderMode == PixelShadowDepth_VirtualShadowMap));
|
|
|
|
bool bEnableNonNaniteVSM = (ShaderMode == PixelShadowDepth_VirtualShadowMap);
|
|
OutEnvironment.SetDefine(TEXT("ENABLE_NON_NANITE_VSM"), bEnableNonNaniteVSM ? 1 : 0);
|
|
if (bEnableNonNaniteVSM != 0)
|
|
{
|
|
FVirtualShadowMapArray::SetShaderDefines(OutEnvironment);
|
|
}
|
|
}
|
|
|
|
TShadowDepthPS()
|
|
{
|
|
}
|
|
|
|
TShadowDepthPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
|
|
: FShadowDepthBasePS(Initializer)
|
|
{
|
|
}
|
|
};
|
|
|
|
// typedef required to get around macro expansion failure due to commas in template argument list for TShadowDepthPixelShader
|
|
#define IMPLEMENT_SHADOWDEPTHPASS_PIXELSHADER_TYPE(ShaderMode) \
|
|
typedef TShadowDepthPS<ShaderMode> TShadowDepthPS##ShaderMode; \
|
|
IMPLEMENT_MATERIAL_SHADER_TYPE(template<>,TShadowDepthPS##ShaderMode,TEXT("/Engine/Private/ShadowDepthPixelShader.usf"),TEXT("Main"),SF_Pixel);
|
|
|
|
IMPLEMENT_SHADOWDEPTHPASS_PIXELSHADER_TYPE(PixelShadowDepth_NonPerspectiveCorrect);
|
|
IMPLEMENT_SHADOWDEPTHPASS_PIXELSHADER_TYPE(PixelShadowDepth_PerspectiveCorrect);
|
|
IMPLEMENT_SHADOWDEPTHPASS_PIXELSHADER_TYPE(PixelShadowDepth_OnePassPointLight);
|
|
IMPLEMENT_SHADOWDEPTHPASS_PIXELSHADER_TYPE(PixelShadowDepth_VirtualShadowMap);
|
|
|
|
/**
|
|
* Overrides a material used for shadow depth rendering with the default material when appropriate.
|
|
* Overriding in this manner can reduce state switches and the number of shaders that have to be compiled.
|
|
* This logic needs to stay in sync with shadow depth shader ShouldCache logic.
|
|
*/
|
|
void OverrideWithDefaultMaterialForShadowDepth(
|
|
const FMaterialRenderProxy*& InOutMaterialRenderProxy,
|
|
const FMaterial*& InOutMaterialResource,
|
|
ERHIFeatureLevel::Type InFeatureLevel)
|
|
{
|
|
// Override with the default material when possible.
|
|
if (InOutMaterialResource->WritesEveryPixel(true) && // Don't override masked materials.
|
|
!InOutMaterialResource->MaterialModifiesMeshPosition_RenderThread()) // Don't override materials using world position offset.
|
|
{
|
|
const FMaterialRenderProxy* DefaultProxy = UMaterial::GetDefaultMaterial(MD_Surface)->GetRenderProxy();
|
|
const FMaterial* DefaultMaterialResource = DefaultProxy->GetMaterialNoFallback(InFeatureLevel);
|
|
check(DefaultMaterialResource);
|
|
|
|
// Override with the default material for opaque materials that don't modify mesh position.
|
|
InOutMaterialRenderProxy = DefaultProxy;
|
|
InOutMaterialResource = DefaultMaterialResource;
|
|
}
|
|
}
|
|
|
|
bool GetShadowDepthPassShaders(
|
|
const FMaterial& Material,
|
|
const FVertexFactory* VertexFactory,
|
|
ERHIFeatureLevel::Type FeatureLevel,
|
|
bool bDirectionalLight,
|
|
bool bOnePassPointLightShadow,
|
|
bool bPositionOnlyVS,
|
|
bool bUsePerspectiveCorrectShadowDepths,
|
|
bool bVirtualShadowMap,
|
|
TShaderRef<FShadowDepthVS>& VertexShader,
|
|
TShaderRef<FShadowDepthBasePS>& PixelShader)
|
|
{
|
|
const FVertexFactoryType* VFType = VertexFactory->GetType();
|
|
|
|
FMaterialShaderTypes ShaderTypes;
|
|
|
|
// Vertex related shaders
|
|
if (bOnePassPointLightShadow)
|
|
{
|
|
if (DoesRuntimeSupportOnePassPointLightShadows(GShaderPlatformForFeatureLevel[FeatureLevel]))
|
|
{
|
|
if (bPositionOnlyVS)
|
|
{
|
|
ShaderTypes.AddShaderType<TShadowDepthVS<VertexShadowDepth_OnePassPointLight, true>>();
|
|
}
|
|
else
|
|
{
|
|
ShaderTypes.AddShaderType<TShadowDepthVS<VertexShadowDepth_OnePassPointLight, false>>();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else if (bVirtualShadowMap)
|
|
{
|
|
if (bPositionOnlyVS)
|
|
{
|
|
ShaderTypes.AddShaderType<TShadowDepthVS<VertexShadowDepth_VirtualShadowMap, true>>();
|
|
}
|
|
else
|
|
{
|
|
ShaderTypes.AddShaderType<TShadowDepthVS<VertexShadowDepth_VirtualShadowMap, false>>();
|
|
}
|
|
}
|
|
else if (bUsePerspectiveCorrectShadowDepths)
|
|
{
|
|
if (bPositionOnlyVS)
|
|
{
|
|
ShaderTypes.AddShaderType<TShadowDepthVS<VertexShadowDepth_PerspectiveCorrect, true>>();
|
|
}
|
|
else
|
|
{
|
|
ShaderTypes.AddShaderType<TShadowDepthVS<VertexShadowDepth_PerspectiveCorrect, false>>();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (bPositionOnlyVS)
|
|
{
|
|
ShaderTypes.AddShaderType<TShadowDepthVS<VertexShadowDepth_OutputDepth, true>>();
|
|
}
|
|
else
|
|
{
|
|
ShaderTypes.AddShaderType<TShadowDepthVS<VertexShadowDepth_OutputDepth, false>>();
|
|
}
|
|
}
|
|
|
|
// Pixel shaders
|
|
const bool bNullPixelShader = Material.WritesEveryPixel(true) && !bUsePerspectiveCorrectShadowDepths && !bVirtualShadowMap && VertexFactory->SupportsNullPixelShader();
|
|
if (!bNullPixelShader)
|
|
{
|
|
if (bVirtualShadowMap)
|
|
{
|
|
ShaderTypes.AddShaderType<TShadowDepthPS<PixelShadowDepth_VirtualShadowMap>>();
|
|
}
|
|
else if (bUsePerspectiveCorrectShadowDepths)
|
|
{
|
|
ShaderTypes.AddShaderType<TShadowDepthPS<PixelShadowDepth_PerspectiveCorrect>>();
|
|
}
|
|
else if (bOnePassPointLightShadow)
|
|
{
|
|
ShaderTypes.AddShaderType<TShadowDepthPS<PixelShadowDepth_OnePassPointLight>>();
|
|
}
|
|
else
|
|
{
|
|
ShaderTypes.AddShaderType<TShadowDepthPS<PixelShadowDepth_NonPerspectiveCorrect>>();
|
|
}
|
|
}
|
|
|
|
FMaterialShaders Shaders;
|
|
if (!Material.TryGetShaders(ShaderTypes, VFType, Shaders))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Shaders.TryGetVertexShader(VertexShader);
|
|
Shaders.TryGetPixelShader(PixelShader);
|
|
return true;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
FProjectedShadowInfo
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
static void CheckShadowDepthMaterials(const FMaterialRenderProxy* InRenderProxy, const FMaterial* InMaterial, ERHIFeatureLevel::Type InFeatureLevel)
|
|
{
|
|
const FMaterialRenderProxy* RenderProxy = InRenderProxy;
|
|
const FMaterial* Material = InMaterial;
|
|
OverrideWithDefaultMaterialForShadowDepth(RenderProxy, Material, InFeatureLevel);
|
|
check(RenderProxy == InRenderProxy);
|
|
check(Material == InMaterial);
|
|
}
|
|
|
|
void FProjectedShadowInfo::ClearDepth(FRHICommandList& RHICmdList) const
|
|
{
|
|
check(RHICmdList.IsInsideRenderPass());
|
|
|
|
const uint32 ViewportMinX = X;
|
|
const uint32 ViewportMinY = Y;
|
|
const float ViewportMinZ = 0.0f;
|
|
const uint32 ViewportMaxX = X + BorderSize * 2 + ResolutionX;
|
|
const uint32 ViewportMaxY = Y + BorderSize * 2 + ResolutionY;
|
|
const float ViewportMaxZ = 1.0f;
|
|
|
|
// Clear depth only.
|
|
const int32 NumClearColors = 1;
|
|
const bool bClearColor = false;
|
|
const FLinearColor Colors[] = { FLinearColor::White };
|
|
|
|
// Translucent shadows use draw call clear
|
|
check(!bTranslucentShadow);
|
|
|
|
RHICmdList.SetViewport(
|
|
ViewportMinX,
|
|
ViewportMinY,
|
|
ViewportMinZ,
|
|
ViewportMaxX,
|
|
ViewportMaxY,
|
|
ViewportMaxZ
|
|
);
|
|
|
|
DrawClearQuadMRT(RHICmdList, bClearColor, NumClearColors, Colors, true, 1.0f, false, 0);
|
|
}
|
|
|
|
void FProjectedShadowInfo::SetStateForView(FRHICommandList& RHICmdList) const
|
|
{
|
|
check(bAllocated);
|
|
|
|
RHICmdList.SetViewport(
|
|
X,
|
|
Y,
|
|
0.0f,
|
|
X + ResolutionX + 2 * BorderSize,
|
|
Y + ResolutionY + 2 * BorderSize,
|
|
1.0f
|
|
);
|
|
}
|
|
|
|
void SetStateForShadowDepth(bool bOnePassPointLightShadow, bool bDirectionalLight, FMeshPassProcessorRenderState& DrawRenderState, EMeshPass::Type InMeshPassTargetType)
|
|
{
|
|
// Disable color writes
|
|
DrawRenderState.SetBlendState(TStaticBlendState<CW_NONE>::GetRHI());
|
|
|
|
if( InMeshPassTargetType == EMeshPass::VSMShadowDepth )
|
|
{
|
|
DrawRenderState.SetDepthStencilState(TStaticDepthStencilState<false, CF_Always>::GetRHI());
|
|
}
|
|
else if (bOnePassPointLightShadow || InMeshPassTargetType == EMeshPass::VSMShadowDepth)
|
|
{
|
|
// Point lights use reverse Z depth maps
|
|
DrawRenderState.SetDepthStencilState(TStaticDepthStencilState<true, CF_DepthNearOrEqual>::GetRHI());
|
|
}
|
|
else
|
|
{
|
|
DrawRenderState.SetDepthStencilState(TStaticDepthStencilState<true, CF_LessEqual>::GetRHI());
|
|
}
|
|
}
|
|
|
|
static TAutoConsoleVariable<int32> CVarParallelShadows(
|
|
TEXT("r.ParallelShadows"),
|
|
1,
|
|
TEXT("Toggles parallel shadow rendering. Parallel rendering must be enabled for this to have an effect."),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
static TAutoConsoleVariable<int32> CVarParallelShadowsNonWholeScene(
|
|
TEXT("r.ParallelShadowsNonWholeScene"),
|
|
0,
|
|
TEXT("Toggles parallel shadow rendering for non whole-scene shadows. r.ParallelShadows must be enabled for this to have an effect."),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarRHICmdFlushRenderThreadTasksShadowPass(
|
|
TEXT("r.RHICmdFlushRenderThreadTasksShadowPass"),
|
|
0,
|
|
TEXT("Wait for completion of parallel render thread tasks at the end of each shadow pass. A more granular version of r.RHICmdFlushRenderThreadTasks. If either r.RHICmdFlushRenderThreadTasks or r.RHICmdFlushRenderThreadTasksShadowPass is > 0 we will flush."));
|
|
|
|
DECLARE_CYCLE_STAT(TEXT("Shadow"), STAT_CLP_Shadow, STATGROUP_ParallelCommandListMarkers);
|
|
|
|
class FShadowParallelCommandListSet final : public FParallelCommandListSet
|
|
{
|
|
public:
|
|
FShadowParallelCommandListSet(
|
|
const FRDGPass* InPass,
|
|
FRHICommandListImmediate& InParentCmdList,
|
|
const FViewInfo& InView,
|
|
const FProjectedShadowInfo& InProjectedShadowInfo,
|
|
const FParallelCommandListBindings& InBindings)
|
|
: FParallelCommandListSet(InPass, GET_STATID(STAT_CLP_Shadow), InView, InParentCmdList)
|
|
, ProjectedShadowInfo(InProjectedShadowInfo)
|
|
, Bindings(InBindings)
|
|
{}
|
|
|
|
~FShadowParallelCommandListSet() override
|
|
{
|
|
Dispatch();
|
|
}
|
|
|
|
void SetStateOnCommandList(FRHICommandList& RHICmdList) override
|
|
{
|
|
FParallelCommandListSet::SetStateOnCommandList(RHICmdList);
|
|
Bindings.SetOnCommandList(RHICmdList);
|
|
ProjectedShadowInfo.SetStateForView(RHICmdList);
|
|
}
|
|
|
|
private:
|
|
const FProjectedShadowInfo& ProjectedShadowInfo;
|
|
FParallelCommandListBindings Bindings;
|
|
};
|
|
|
|
class FCopyShadowMapsCubeGS : public FGlobalShader
|
|
{
|
|
public:
|
|
DECLARE_GLOBAL_SHADER(FCopyShadowMapsCubeGS);
|
|
|
|
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
|
|
{
|
|
return RHISupportsGeometryShaders(Parameters.Platform) && IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5);
|
|
}
|
|
|
|
FCopyShadowMapsCubeGS() = default;
|
|
FCopyShadowMapsCubeGS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
|
|
: FGlobalShader(Initializer)
|
|
{}
|
|
};
|
|
|
|
IMPLEMENT_GLOBAL_SHADER(FCopyShadowMapsCubeGS, "/Engine/Private/CopyShadowMaps.usf", "CopyCubeDepthGS", SF_Geometry);
|
|
|
|
class FCopyShadowMapsCubePS : public FGlobalShader
|
|
{
|
|
public:
|
|
DECLARE_GLOBAL_SHADER(FCopyShadowMapsCubePS);
|
|
SHADER_USE_PARAMETER_STRUCT(FCopyShadowMapsCubePS, FGlobalShader);
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
|
|
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
|
|
SHADER_PARAMETER_RDG_TEXTURE(TextureCube, ShadowDepthCubeTexture)
|
|
SHADER_PARAMETER_SAMPLER(SamplerState, ShadowDepthSampler)
|
|
RENDER_TARGET_BINDING_SLOTS()
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
|
|
{
|
|
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5);
|
|
}
|
|
};
|
|
|
|
IMPLEMENT_GLOBAL_SHADER(FCopyShadowMapsCubePS, "/Engine/Private/CopyShadowMaps.usf", "CopyCubeDepthPS", SF_Pixel);
|
|
|
|
class FCopyShadowMaps2DPS : public FGlobalShader
|
|
{
|
|
public:
|
|
DECLARE_GLOBAL_SHADER(FCopyShadowMaps2DPS);
|
|
SHADER_USE_PARAMETER_STRUCT(FCopyShadowMaps2DPS, FGlobalShader);
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
|
|
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
|
|
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, ShadowDepthTexture)
|
|
SHADER_PARAMETER_SAMPLER(SamplerState, ShadowDepthSampler)
|
|
RENDER_TARGET_BINDING_SLOTS()
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
};
|
|
|
|
IMPLEMENT_GLOBAL_SHADER(FCopyShadowMaps2DPS, "/Engine/Private/CopyShadowMaps.usf", "Copy2DDepthPS", SF_Pixel);
|
|
|
|
/** */
|
|
class FScrollingShadowMaps2DPS : public FGlobalShader
|
|
{
|
|
public:
|
|
DECLARE_GLOBAL_SHADER(FScrollingShadowMaps2DPS);
|
|
SHADER_USE_PARAMETER_STRUCT(FScrollingShadowMaps2DPS, FGlobalShader);
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
|
|
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
|
|
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, ShadowDepthTexture)
|
|
SHADER_PARAMETER_SAMPLER(SamplerState, ShadowDepthSampler)
|
|
SHADER_PARAMETER(FVector4f, DepthOffsetScale)
|
|
RENDER_TARGET_BINDING_SLOTS()
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
|
|
{
|
|
return true;
|
|
}
|
|
};
|
|
|
|
IMPLEMENT_GLOBAL_SHADER(FScrollingShadowMaps2DPS, "/Engine/Private/CopyShadowMaps.usf", "Scrolling2DDepthPS", SF_Pixel);
|
|
|
|
void FProjectedShadowInfo::CopyCachedShadowMap(
|
|
FRDGBuilder& GraphBuilder,
|
|
const FViewInfo& View,
|
|
const FSceneRenderer* SceneRenderer,
|
|
const FRenderTargetBindingSlots& RenderTargetBindingSlots,
|
|
const FMeshPassProcessorRenderState& DrawRenderState)
|
|
{
|
|
check(CacheMode == SDCM_MovablePrimitivesOnly || CacheMode == SDCM_CSMScrolling);
|
|
const FCachedShadowMapData& CachedShadowMapData = SceneRenderer->Scene->GetCachedShadowMapDataRef(GetLightSceneInfo().Id, FMath::Max(CascadeSettings.ShadowSplitIndex, 0));
|
|
|
|
if (CachedShadowMapData.bCachedShadowMapHasPrimitives && CachedShadowMapData.ShadowMap.IsValid())
|
|
{
|
|
FRDGTextureRef ShadowDepthTexture = GraphBuilder.RegisterExternalTexture(CachedShadowMapData.ShadowMap.DepthTarget);
|
|
const FIntPoint ShadowDepthExtent = ShadowDepthTexture->Desc.Extent;
|
|
|
|
FGraphicsPipelineStateInitializer GraphicsPSOInit;
|
|
DrawRenderState.ApplyToPSO(GraphicsPSOInit);
|
|
const uint32 StencilRef = DrawRenderState.GetStencilRef();
|
|
|
|
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<FM_Solid, CM_None>::GetRHI();
|
|
// No depth tests, so we can replace the clear
|
|
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<true, CF_Always>::GetRHI();
|
|
GraphicsPSOInit.PrimitiveType = PT_TriangleList;
|
|
|
|
extern TGlobalResource<FFilterVertexDeclaration> GFilterVertexDeclaration;
|
|
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI;
|
|
|
|
if (CacheMode == SDCM_MovablePrimitivesOnly)
|
|
{
|
|
if (bOnePassPointLightShadow)
|
|
{
|
|
TShaderRef<FScreenVS> ScreenVertexShader;
|
|
TShaderMapRef<FCopyShadowMapsCubePS> PixelShader(View.ShaderMap);
|
|
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
|
|
|
|
int32 InstanceCount = 1;
|
|
|
|
#if PLATFORM_SUPPORTS_GEOMETRY_SHADERS
|
|
if (RHISupportsGeometryShaders(GShaderPlatformForFeatureLevel[SceneRenderer->FeatureLevel]))
|
|
{
|
|
TShaderMapRef<TScreenVSForGS<false>> VertexShader(View.ShaderMap);
|
|
TShaderMapRef<FCopyShadowMapsCubeGS> GeometryShader(View.ShaderMap);
|
|
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
|
|
GraphicsPSOInit.BoundShaderState.SetGeometryShader(GeometryShader.GetGeometryShader());
|
|
ScreenVertexShader = VertexShader;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
check(RHISupportsVertexShaderLayer(GShaderPlatformForFeatureLevel[SceneRenderer->FeatureLevel]));
|
|
TShaderMapRef<TScreenVSForGS<true>> VertexShader(View.ShaderMap);
|
|
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
|
|
ScreenVertexShader = VertexShader;
|
|
|
|
InstanceCount = 6;
|
|
}
|
|
|
|
auto* PassParameters = GraphBuilder.AllocParameters<FCopyShadowMapsCubePS::FParameters>();
|
|
PassParameters->RenderTargets = RenderTargetBindingSlots;
|
|
PassParameters->ShadowDepthCubeTexture = ShadowDepthTexture;
|
|
PassParameters->ShadowDepthSampler = TStaticSamplerState<SF_Point, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
|
|
|
|
GraphBuilder.AddPass(
|
|
RDG_EVENT_NAME("CopyCachedShadowMap"),
|
|
PassParameters,
|
|
ERDGPassFlags::Raster,
|
|
[this, ScreenVertexShader, PixelShader, GraphicsPSOInit, PassParameters, ShadowDepthExtent, InstanceCount, StencilRef](FRHICommandList& RHICmdList) mutable
|
|
{
|
|
SetStateForView(RHICmdList);
|
|
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
|
|
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, StencilRef);
|
|
SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), *PassParameters);
|
|
|
|
FIntPoint ResolutionWithBorder = FIntPoint(ResolutionX + 2 * BorderSize, ResolutionY + 2 * BorderSize);
|
|
|
|
DrawRectangle(
|
|
RHICmdList,
|
|
0, 0,
|
|
ResolutionWithBorder.X, ResolutionWithBorder.Y,
|
|
0, 0,
|
|
ResolutionWithBorder.X, ResolutionWithBorder.Y,
|
|
ResolutionWithBorder,
|
|
ShadowDepthExtent,
|
|
ScreenVertexShader,
|
|
EDRF_Default,
|
|
InstanceCount);
|
|
});
|
|
}
|
|
else
|
|
{
|
|
TShaderMapRef<FScreenVS> ScreenVertexShader(View.ShaderMap);
|
|
TShaderMapRef<FCopyShadowMaps2DPS> PixelShader(View.ShaderMap);
|
|
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = ScreenVertexShader.GetVertexShader();
|
|
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
|
|
|
|
auto* PassParameters = GraphBuilder.AllocParameters<FCopyShadowMaps2DPS::FParameters>();
|
|
PassParameters->RenderTargets = RenderTargetBindingSlots;
|
|
PassParameters->ShadowDepthTexture = ShadowDepthTexture;
|
|
PassParameters->ShadowDepthSampler = TStaticSamplerState<SF_Point, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
|
|
|
|
GraphBuilder.AddPass(
|
|
RDG_EVENT_NAME("CopyCachedShadowMap"),
|
|
PassParameters,
|
|
ERDGPassFlags::Raster,
|
|
[this, ScreenVertexShader, PixelShader, GraphicsPSOInit, PassParameters, ShadowDepthExtent, StencilRef](FRHICommandList& RHICmdList) mutable
|
|
{
|
|
SetStateForView(RHICmdList);
|
|
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
|
|
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, StencilRef);
|
|
SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), *PassParameters);
|
|
|
|
FIntPoint ResolutionWithBorder = FIntPoint(ResolutionX + 2 * BorderSize, ResolutionY + 2 * BorderSize);
|
|
|
|
DrawRectangle(
|
|
RHICmdList,
|
|
0, 0,
|
|
ResolutionWithBorder.X, ResolutionWithBorder.Y,
|
|
0, 0,
|
|
ResolutionWithBorder.X, ResolutionWithBorder.Y,
|
|
ResolutionWithBorder,
|
|
ShadowDepthExtent,
|
|
ScreenVertexShader,
|
|
EDRF_Default);
|
|
});
|
|
}
|
|
}
|
|
else // CacheMode == SDCM_CSMScrolling
|
|
{
|
|
TShaderMapRef<FScreenVS> ScreenVertexShader(View.ShaderMap);
|
|
TShaderMapRef<FScrollingShadowMaps2DPS> PixelShader(View.ShaderMap);
|
|
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = ScreenVertexShader.GetVertexShader();
|
|
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
|
|
|
|
auto* PassParameters = GraphBuilder.AllocParameters<FScrollingShadowMaps2DPS::FParameters>();
|
|
PassParameters->RenderTargets = RenderTargetBindingSlots;
|
|
PassParameters->ShadowDepthTexture = ShadowDepthTexture;
|
|
PassParameters->ShadowDepthSampler = TStaticSamplerState<SF_Point, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
|
|
/** According to the implementation in ShadowDepthVertexShader.usf, the formula is DeviceZ = 1 - ((MaxZ - SceneDepth) / (MaxZ - MinZ)) * InvMaxSubjectDepth + SlopeDepthBias * Slope + ConstantDepthBias;
|
|
For short C0 = InvMaxSubjectDepth; C1 = 1 + SlopeDepthBias * Slope + ConstantDepthBias;
|
|
So the SceneDepth0 = MaxZ0 - (C1 - DeviceZ0) * (MaxZ0 - MinZ0) / C0 ;
|
|
SceneDepth1 = SceneDepth0 + ZOffset;
|
|
The reconstruct DeviceZ1 = C1 - ((MaxZ1 - SceneDepth1) / (MaxZ1 - MinZ1)) * C0;
|
|
So DeviceZ1 = DeviceZ0 * (MaxZ0 - MinZ0) / (MaxZ1 - MinZ1) + (C0 * (MaxZ0 + ZOffset - MaxZ1) - C1 * (MaxZ0 - MinZ0)) / (MaxZ1 - MinZ1) + C1;
|
|
*/
|
|
float MaxZ0MinusMinZ0 = CachedShadowMapData.MaxSubjectZ - CachedShadowMapData.MinSubjectZ;
|
|
float MaxZ1MinusMinZ1 = MaxSubjectZ - MinSubjectZ;
|
|
float MaxZ0PlusZOffsetMinusMaxZ1 = CachedShadowMapData.MaxSubjectZ + CSMScrollingZOffset - MaxSubjectZ;
|
|
float C1 = 1 + GetShaderDepthBias();
|
|
PassParameters->DepthOffsetScale = FVector4f((InvMaxSubjectDepth * MaxZ0PlusZOffsetMinusMaxZ1 - C1 * MaxZ0MinusMinZ0) / MaxZ1MinusMinZ1 + C1, MaxZ0MinusMinZ0 / MaxZ1MinusMinZ1, 0.0f, 0.0f);
|
|
|
|
GraphBuilder.AddPass(
|
|
RDG_EVENT_NAME("ScrollingCachedWholeSceneDirectionalShadowMap"),
|
|
PassParameters,
|
|
ERDGPassFlags::Raster,
|
|
[this, ScreenVertexShader, PixelShader, GraphicsPSOInit, PassParameters, ShadowDepthExtent, StencilRef](FRHICommandList& RHICmdList) mutable
|
|
{
|
|
checkSlow(OverlappedUVOnCachedShadowMap != FVector4f(-1.0f, -1.0f, -1.0f, -1.0f));
|
|
checkSlow(OverlappedUVOnCurrentShadowMap != FVector4f(-1.0f, -1.0f, -1.0f, -1.0f));
|
|
|
|
FIntPoint ResolutionWithBorder = FIntPoint(ResolutionX + 2 * BorderSize, ResolutionY + 2 * BorderSize);
|
|
|
|
uint32 UStart = OverlappedUVOnCachedShadowMap.X * ResolutionWithBorder.X + 0.5f;
|
|
uint32 USize = (OverlappedUVOnCachedShadowMap.Z - OverlappedUVOnCachedShadowMap.X) * ResolutionWithBorder.X + 0.5f;
|
|
|
|
uint32 VStart = OverlappedUVOnCachedShadowMap.Y * ResolutionWithBorder.Y + 0.5f;
|
|
uint32 VSize = (OverlappedUVOnCachedShadowMap.W - OverlappedUVOnCachedShadowMap.Y) * ResolutionWithBorder.Y + 0.5f;
|
|
|
|
FIntVector4 OutputViewport = FIntVector4(OverlappedUVOnCurrentShadowMap.X * ResolutionWithBorder.X + 0.5f, OverlappedUVOnCurrentShadowMap.Y * ResolutionWithBorder.Y + 0.5f, OverlappedUVOnCurrentShadowMap.Z * ResolutionWithBorder.X + 0.5f, OverlappedUVOnCurrentShadowMap.W * ResolutionWithBorder.Y + 0.5f);
|
|
|
|
SetStateForView(RHICmdList);
|
|
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
|
|
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, StencilRef);
|
|
SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), *PassParameters);
|
|
|
|
DrawRectangle(
|
|
RHICmdList,
|
|
OutputViewport.X, OutputViewport.Y,
|
|
OutputViewport.Z - OutputViewport.X, OutputViewport.W - OutputViewport.Y,
|
|
UStart, VStart,
|
|
USize, VSize,
|
|
ResolutionWithBorder,
|
|
ShadowDepthExtent,
|
|
ScreenVertexShader,
|
|
EDRF_Default);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
void FProjectedShadowInfo::BeginRenderView(FRDGBuilder& GraphBuilder, FScene* Scene)
|
|
{
|
|
if (DependentView)
|
|
{
|
|
const ERHIFeatureLevel::Type FeatureLevel = ShadowDepthView->FeatureLevel;
|
|
if (FSceneInterface::GetShadingPath(FeatureLevel) == EShadingPath::Deferred)
|
|
{
|
|
extern TSet<IPersistentViewUniformBufferExtension*> PersistentViewUniformBufferExtensions;
|
|
|
|
for (IPersistentViewUniformBufferExtension* Extension : PersistentViewUniformBufferExtensions)
|
|
{
|
|
Extension->BeginRenderView(DependentView);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool IsShadowDepthPassWaitForTasksEnabled()
|
|
{
|
|
return CVarRHICmdFlushRenderThreadTasksShadowPass.GetValueOnRenderThread() > 0 || CVarRHICmdFlushRenderThreadTasks.GetValueOnRenderThread() > 0;
|
|
}
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FShadowDepthPassParameters, )
|
|
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
|
|
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FMobileShadowDepthPassUniformParameters, MobilePassUniformBuffer)
|
|
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FShadowDepthPassUniformParameters, DeferredPassUniformBuffer)
|
|
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FVirtualShadowMapUniformParameters, VirtualShadowMap)
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FInstanceCullingDrawParams, InstanceCullingDrawParams)
|
|
RENDER_TARGET_BINDING_SLOTS()
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
#if WITH_MGPU
|
|
void CopyCachedShadowMapCrossGPU(FRHICommandList& RHICmdList, FRHITexture* ShadowDepthTexture, FRHIGPUMask SourceGPUMask)
|
|
{
|
|
if (SourceGPUMask != FRHIGPUMask::All())
|
|
{
|
|
uint32 SourceGPUIndex = SourceGPUMask.GetFirstIndex();
|
|
|
|
TArray<FTransferResourceParams, TFixedAllocator<MAX_NUM_GPUS>> CrossGPUTransferBuffers;
|
|
for (uint32 DestGPUIndex : FRHIGPUMask::All())
|
|
{
|
|
if (!SourceGPUMask.Contains(DestGPUIndex))
|
|
{
|
|
CrossGPUTransferBuffers.Add(FTransferResourceParams(ShadowDepthTexture, SourceGPUIndex, DestGPUIndex, false, false));
|
|
}
|
|
}
|
|
|
|
RHICmdList.TransferResources(CrossGPUTransferBuffers);
|
|
}
|
|
}
|
|
#endif // WITH_MGPU
|
|
|
|
void FProjectedShadowInfo::RenderDepth(
|
|
FRDGBuilder& GraphBuilder,
|
|
const FSceneRenderer* SceneRenderer,
|
|
FRDGTextureRef ShadowDepthTexture,
|
|
bool bDoParallelDispatch,
|
|
bool bDoCrossGPUCopy)
|
|
{
|
|
#if WANTS_DRAW_MESH_EVENTS
|
|
FString EventName;
|
|
|
|
if (GetEmitDrawEvents())
|
|
{
|
|
GetShadowTypeNameForDrawEvent(EventName);
|
|
EventName += FString(TEXT(" ")) + FString::FromInt(ResolutionX) + TEXT("x") + FString::FromInt(ResolutionY);
|
|
}
|
|
|
|
RDG_EVENT_SCOPE(GraphBuilder, "%s", *EventName);
|
|
#endif
|
|
|
|
CONDITIONAL_SCOPE_CYCLE_COUNTER(STAT_RenderWholeSceneShadowDepthsTime, bWholeSceneShadow);
|
|
CONDITIONAL_SCOPE_CYCLE_COUNTER(STAT_RenderPerObjectShadowDepthsTime, !bWholeSceneShadow);
|
|
QUICK_SCOPE_CYCLE_COUNTER(STAT_RenderShadowDepth);
|
|
|
|
FScene* Scene = SceneRenderer->Scene;
|
|
const ERHIFeatureLevel::Type FeatureLevel = ShadowDepthView->FeatureLevel;
|
|
BeginRenderView(GraphBuilder, Scene);
|
|
|
|
FShadowDepthPassParameters* PassParameters = GraphBuilder.AllocParameters<FShadowDepthPassParameters>();
|
|
PassParameters->View = ShadowDepthView->ViewUniformBuffer;
|
|
PassParameters->RenderTargets.DepthStencil = FDepthStencilBinding(
|
|
ShadowDepthTexture,
|
|
ERenderTargetLoadAction::ELoad,
|
|
ERenderTargetLoadAction::ENoAction,
|
|
FExclusiveDepthStencil::DepthWrite_StencilNop);
|
|
|
|
if (CacheMode == SDCM_MovablePrimitivesOnly || CacheMode == SDCM_CSMScrolling)
|
|
{
|
|
// Copy in depths of static primitives before we render movable primitives.
|
|
FMeshPassProcessorRenderState DrawRenderState;
|
|
SetStateForShadowDepth(bOnePassPointLightShadow, bDirectionalLight, DrawRenderState, MeshPassTargetType);
|
|
CopyCachedShadowMap(GraphBuilder, *ShadowDepthView, SceneRenderer, PassParameters->RenderTargets, DrawRenderState);
|
|
}
|
|
|
|
PassParameters->VirtualShadowMap = SceneRenderer->ActiveViewFamily->VirtualShadowMapArray.GetUniformBuffer(GraphBuilder);
|
|
|
|
switch (FSceneInterface::GetShadingPath(FeatureLevel))
|
|
{
|
|
case EShadingPath::Deferred:
|
|
{
|
|
auto* ShadowDepthPassParameters = GraphBuilder.AllocParameters<FShadowDepthPassUniformParameters>();
|
|
SetupShadowDepthPassUniformBuffer(this, GraphBuilder, *ShadowDepthView, *ShadowDepthPassParameters);
|
|
PassParameters->DeferredPassUniformBuffer = GraphBuilder.CreateUniformBuffer(ShadowDepthPassParameters);
|
|
}
|
|
break;
|
|
case EShadingPath::Mobile:
|
|
{
|
|
auto* ShadowDepthPassParameters = GraphBuilder.AllocParameters<FMobileShadowDepthPassUniformParameters>();
|
|
SetupShadowDepthPassUniformBuffer(this, GraphBuilder, *ShadowDepthView, *ShadowDepthPassParameters);
|
|
PassParameters->MobilePassUniformBuffer = GraphBuilder.CreateUniformBuffer(ShadowDepthPassParameters);
|
|
}
|
|
break;
|
|
default:
|
|
checkNoEntry();
|
|
}
|
|
|
|
ShadowDepthPass.BuildRenderingCommands(GraphBuilder, Scene->GPUScene, PassParameters->InstanceCullingDrawParams);
|
|
|
|
#if WITH_MGPU
|
|
// Need to fetch GPU mask outside "AddPass", as it's not updated during pass execution
|
|
FRHIGPUMask GPUMask = GraphBuilder.RHICmdList.GetGPUMask();
|
|
#endif
|
|
|
|
if (bDoParallelDispatch)
|
|
{
|
|
RDG_WAIT_FOR_TASKS_CONDITIONAL(GraphBuilder, IsShadowDepthPassWaitForTasksEnabled());
|
|
|
|
GraphBuilder.AddPass(
|
|
RDG_EVENT_NAME("ShadowDepthPassParallel"),
|
|
PassParameters,
|
|
ERDGPassFlags::Raster | ERDGPassFlags::SkipRenderPass,
|
|
[this, PassParameters
|
|
#if WITH_MGPU
|
|
, ShadowDepthTexture, GPUMask, bDoCrossGPUCopy
|
|
#endif
|
|
](const FRDGPass* InPass, FRHICommandListImmediate& RHICmdList)
|
|
{
|
|
FShadowParallelCommandListSet ParallelCommandListSet(InPass, RHICmdList, *ShadowDepthView, *this, FParallelCommandListBindings(PassParameters));
|
|
ShadowDepthPass.DispatchDraw(&ParallelCommandListSet, RHICmdList, &PassParameters->InstanceCullingDrawParams);
|
|
|
|
#if WITH_MGPU
|
|
if (bDoCrossGPUCopy)
|
|
{
|
|
CopyCachedShadowMapCrossGPU(RHICmdList, ShadowDepthTexture->GetRHI(), GPUMask);
|
|
}
|
|
#endif
|
|
});
|
|
}
|
|
else
|
|
{
|
|
GraphBuilder.AddPass(
|
|
RDG_EVENT_NAME("ShadowDepthPass"),
|
|
PassParameters,
|
|
ERDGPassFlags::Raster,
|
|
[this, PassParameters
|
|
#if WITH_MGPU
|
|
, ShadowDepthTexture, GPUMask, bDoCrossGPUCopy
|
|
#endif
|
|
](FRHICommandList& RHICmdList)
|
|
{
|
|
SetStateForView(RHICmdList);
|
|
ShadowDepthPass.DispatchDraw(nullptr, RHICmdList, &PassParameters->InstanceCullingDrawParams);
|
|
|
|
#if WITH_MGPU
|
|
if (bDoCrossGPUCopy)
|
|
{
|
|
CopyCachedShadowMapCrossGPU(RHICmdList, ShadowDepthTexture->GetRHI(), GPUMask);
|
|
}
|
|
#endif
|
|
});
|
|
}
|
|
}
|
|
|
|
void FProjectedShadowInfo::ModifyViewForShadow(FViewInfo* FoundView) const
|
|
{
|
|
FIntRect OriginalViewRect = FoundView->ViewRect;
|
|
FoundView->ViewRect = GetOuterViewRect();
|
|
|
|
FoundView->ViewMatrices.HackRemoveTemporalAAProjectionJitter();
|
|
|
|
if (CascadeSettings.bFarShadowCascade)
|
|
{
|
|
(int32&)FoundView->DrawDynamicFlags |= (int32)EDrawDynamicFlags::FarShadowCascade;
|
|
}
|
|
|
|
// Don't do material texture mip biasing in shadow maps.
|
|
FoundView->MaterialTextureMipBias = 0;
|
|
|
|
FoundView->CachedViewUniformShaderParameters = MakeUnique<FViewUniformShaderParameters>();
|
|
|
|
// Override the view matrix so that billboarding primitives will be aligned to the light
|
|
FoundView->ViewMatrices.HackOverrideViewMatrixForShadows(TranslatedWorldToView);
|
|
FBox VolumeBounds[TVC_MAX];
|
|
FoundView->SetupUniformBufferParameters(
|
|
VolumeBounds,
|
|
TVC_MAX,
|
|
*FoundView->CachedViewUniformShaderParameters);
|
|
|
|
FoundView->ViewUniformBuffer = TUniformBufferRef<FViewUniformShaderParameters>::CreateUniformBufferImmediate(*FoundView->CachedViewUniformShaderParameters, UniformBuffer_SingleFrame);
|
|
|
|
// we are going to set this back now because we only want the correct view rect for the uniform buffer. For LOD calculations, we want the rendering viewrect and proj matrix.
|
|
FoundView->ViewRect = OriginalViewRect;
|
|
|
|
extern int32 GPreshadowsForceLowestLOD;
|
|
|
|
if (bPreShadow && GPreshadowsForceLowestLOD)
|
|
{
|
|
(int32&)FoundView->DrawDynamicFlags |= EDrawDynamicFlags::ForceLowestLOD;
|
|
}
|
|
}
|
|
|
|
FViewInfo* FProjectedShadowInfo::FindViewForShadow(FSceneRenderer* SceneRenderer) const
|
|
{
|
|
// Choose an arbitrary view where this shadow's subject is relevant.
|
|
FViewInfo* FoundView = NULL;
|
|
for (int32 ViewIndex = 0; ViewIndex < SceneRenderer->Views.Num(); ViewIndex++)
|
|
{
|
|
FViewInfo* CheckView = &SceneRenderer->Views[ViewIndex];
|
|
const FVisibleLightViewInfo& VisibleLightViewInfo = CheckView->VisibleLightInfos[LightSceneInfo->Id];
|
|
FPrimitiveViewRelevance ViewRel = VisibleLightViewInfo.ProjectedShadowViewRelevanceMap[ShadowId];
|
|
if (ViewRel.bShadowRelevance)
|
|
{
|
|
FoundView = CheckView;
|
|
break;
|
|
}
|
|
}
|
|
check(FoundView);
|
|
return FoundView;
|
|
}
|
|
|
|
void FProjectedShadowInfo::SetupShadowDepthView(FSceneRenderer* SceneRenderer)
|
|
{
|
|
FViewInfo* FoundView = FindViewForShadow(SceneRenderer);
|
|
check(FoundView && IsInRenderingThread());
|
|
FViewInfo* DepthPassView = FoundView->CreateSnapshot();
|
|
// We are starting a new collection of dynamic primitives for the shadow views.
|
|
DepthPassView->DynamicPrimitiveCollector = FGPUScenePrimitiveCollector(&SceneRenderer->GetGPUSceneDynamicContext());
|
|
|
|
ModifyViewForShadow(DepthPassView);
|
|
ShadowDepthView = DepthPassView;
|
|
}
|
|
|
|
void FProjectedShadowInfo::GetShadowTypeNameForDrawEvent(FString& TypeName) const
|
|
{
|
|
const FName ParentName = ParentSceneInfo ? ParentSceneInfo->Proxy->GetOwnerName() : NAME_None;
|
|
|
|
if (bWholeSceneShadow)
|
|
{
|
|
if (CascadeSettings.ShadowSplitIndex >= 0)
|
|
{
|
|
TypeName = FString(TEXT("WholeScene split")) + FString::FromInt(CascadeSettings.ShadowSplitIndex);
|
|
}
|
|
else
|
|
{
|
|
if (CacheMode == SDCM_MovablePrimitivesOnly)
|
|
{
|
|
TypeName = FString(TEXT("WholeScene MovablePrimitives"));
|
|
}
|
|
else if (CacheMode == SDCM_StaticPrimitivesOnly)
|
|
{
|
|
TypeName = FString(TEXT("WholeScene StaticPrimitives"));
|
|
}
|
|
else
|
|
{
|
|
TypeName = FString(TEXT("WholeScene"));
|
|
}
|
|
}
|
|
}
|
|
else if (bPreShadow)
|
|
{
|
|
TypeName = FString(TEXT("PreShadow ")) + ParentName.ToString();
|
|
}
|
|
else
|
|
{
|
|
TypeName = FString(TEXT("PerObject ")) + ParentName.ToString();
|
|
}
|
|
}
|
|
|
|
#if WITH_MGPU
|
|
// Shadows that are cached need to be copied to other GPUs after they render
|
|
bool FSceneRenderer::IsShadowCached(FProjectedShadowInfo* ProjectedShadowInfo) const
|
|
{
|
|
// Preshadows that are going to be cached this frame should be copied to other GPUs.
|
|
if (ProjectedShadowInfo->bPreShadow)
|
|
{
|
|
return !ProjectedShadowInfo->bDepthsCached && ProjectedShadowInfo->bAllocatedInPreshadowCache;
|
|
}
|
|
// SDCM_StaticPrimitivesOnly shadows don't update every frame so we need to copy
|
|
// their depths to all possible GPUs.
|
|
else if (!ProjectedShadowInfo->IsWholeSceneDirectionalShadow() && ProjectedShadowInfo->CacheMode == SDCM_StaticPrimitivesOnly)
|
|
{
|
|
// Cached whole scene shadows shouldn't be view dependent.
|
|
checkSlow(ProjectedShadowInfo->DependentView == nullptr);
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
FRHIGPUMask FSceneRenderer::GetGPUMaskForShadow(FProjectedShadowInfo* ProjectedShadowInfo) const
|
|
{
|
|
// View dependent shadows only need to render depths on their view's GPUs.
|
|
if (ProjectedShadowInfo->DependentView != nullptr)
|
|
{
|
|
return ProjectedShadowInfo->DependentView->GPUMask;
|
|
}
|
|
else
|
|
{
|
|
return AllViewsGPUMask;
|
|
}
|
|
}
|
|
#endif // WITH_MGPU
|
|
|
|
static void UpdatePackedViewParamsFromPrevShadowState(Nanite::FPackedViewParams& Params, const FPersistentShadowState* PrevShadowState)
|
|
{
|
|
if(PrevShadowState)
|
|
{
|
|
Params.PrevViewMatrices = PrevShadowState->ViewMatrices;
|
|
Params.HZBTestViewRect = PrevShadowState->HZBTestViewRect;
|
|
Params.Flags |= NANITE_VIEW_FLAG_HZBTEST;
|
|
}
|
|
}
|
|
|
|
static void UpdateCurrentFrameHZB(FLightSceneInfo& LightSceneInfo, const FPersistentShadowStateKey& ShadowKey, const FProjectedShadowInfo* ProjectedShadowInfo, const TRefCountPtr<IPooledRenderTarget>& HZB, int32 CubeFaceIndex = -1)
|
|
{
|
|
FPersistentShadowState State;
|
|
State.ViewMatrices = ProjectedShadowInfo->GetShadowDepthRenderingViewMatrices(CubeFaceIndex);
|
|
State.HZBTestViewRect = ProjectedShadowInfo->GetInnerViewRect();
|
|
State.HZB = HZB;
|
|
LightSceneInfo.PersistentShadows.Add(ShadowKey, State);
|
|
}
|
|
|
|
static void RenderShadowDepthAtlasNanite(
|
|
FRDGBuilder& GraphBuilder,
|
|
ERHIFeatureLevel::Type FeatureLevel,
|
|
FScene& Scene,
|
|
const FViewInfo& SceneView,
|
|
const FSortedShadowMapAtlas& ShadowMapAtlas,
|
|
const int32 AtlasIndex)
|
|
{
|
|
const FIntPoint AtlasSize = ShadowMapAtlas.RenderTargets.DepthTarget->GetDesc().Extent;
|
|
|
|
const bool bUseHZB = (CVarNaniteShadowsUseHZB.GetValueOnRenderThread() != 0);
|
|
TArray<TRefCountPtr<IPooledRenderTarget>>& PrevAtlasHZBs = Scene.PrevAtlasHZBs;
|
|
|
|
TArray<Nanite::FPackedView, SceneRenderingAllocator> PackedViews;
|
|
TArray<FProjectedShadowInfo*, SceneRenderingAllocator> ShadowsToEmit;
|
|
for (int32 ShadowIndex = 0; ShadowIndex < ShadowMapAtlas.Shadows.Num(); ShadowIndex++)
|
|
{
|
|
FProjectedShadowInfo* ProjectedShadowInfo = ShadowMapAtlas.Shadows[ShadowIndex];
|
|
|
|
// TODO: We avoid rendering Nanite geometry into both movable AND static cached shadows, but has a side effect
|
|
// that if there is *only* a movable cached shadow map (and not static), it won't rendering anything.
|
|
// Logic around Nanite and the cached shadows is fuzzy in a bunch of places and the whole thing needs some rethinking
|
|
// so leaving this like this for now as it is unlikely to happen in realistic scenes.
|
|
if (!ProjectedShadowInfo->bNaniteGeometry ||
|
|
ProjectedShadowInfo->CacheMode == SDCM_MovablePrimitivesOnly)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
Nanite::FPackedViewParams Initializer;
|
|
Initializer.ViewMatrices = ProjectedShadowInfo->GetShadowDepthRenderingViewMatrices();
|
|
Initializer.ViewRect = ProjectedShadowInfo->GetOuterViewRect();
|
|
Initializer.RasterContextSize = AtlasSize;
|
|
Initializer.LODScaleFactor = ComputeNaniteShadowsLODScaleFactor();
|
|
Initializer.PrevViewMatrices = Initializer.ViewMatrices;
|
|
Initializer.HZBTestViewRect = ProjectedShadowInfo->GetInnerViewRect();
|
|
|
|
// Orthographic shadow projections want depth clamping rather than clipping
|
|
Initializer.Flags = ProjectedShadowInfo->ShouldClampToNearPlane() ? 0u : NANITE_VIEW_FLAG_NEAR_CLIP;
|
|
|
|
FLightSceneInfo& LightSceneInfo = ProjectedShadowInfo->GetLightSceneInfo();
|
|
|
|
FPersistentShadowStateKey ShadowKey;
|
|
ShadowKey.AtlasIndex = AtlasIndex;
|
|
ShadowKey.ProjectionId = ProjectedShadowInfo->ProjectionIndex;
|
|
ShadowKey.SubjectPrimitiveComponentIndex = ProjectedShadowInfo->SubjectPrimitiveComponentIndex;
|
|
|
|
FPersistentShadowState* PrevShadowState = LightSceneInfo.PrevPersistentShadows.Find(ShadowKey);
|
|
|
|
UpdatePackedViewParamsFromPrevShadowState(Initializer, PrevShadowState);
|
|
UpdateCurrentFrameHZB(LightSceneInfo, ShadowKey, ProjectedShadowInfo, nullptr);
|
|
|
|
PackedViews.Add(Nanite::CreatePackedView(Initializer));
|
|
ShadowsToEmit.Add(ProjectedShadowInfo);
|
|
}
|
|
|
|
if (PackedViews.Num() > 0)
|
|
{
|
|
RDG_EVENT_SCOPE(GraphBuilder, "Nanite Shadows");
|
|
|
|
Nanite::FSharedContext SharedContext{};
|
|
SharedContext.FeatureLevel = Scene.GetFeatureLevel();
|
|
SharedContext.ShaderMap = GetGlobalShaderMap(SharedContext.FeatureLevel);
|
|
SharedContext.Pipeline = Nanite::EPipeline::Shadows;
|
|
|
|
// NOTE: Rendering into an atlas like this is not going to work properly with HZB, but we are not currently using HZB here.
|
|
// It might be worthwhile going through the virtual SM rendering path even for "dense" cases even just for proper handling of all the details.
|
|
FIntRect FullAtlasViewRect(FIntPoint(0, 0), AtlasSize);
|
|
TRefCountPtr<IPooledRenderTarget> PrevAtlasHZB = bUseHZB ? PrevAtlasHZBs[AtlasIndex] : nullptr;
|
|
|
|
Nanite::FCullingContext::FConfiguration CullingConfig = { 0 };
|
|
CullingConfig.bTwoPassOcclusion = true;
|
|
CullingConfig.bUpdateStreaming = CVarNaniteShadowsUpdateStreaming.GetValueOnRenderThread() != 0;
|
|
CullingConfig.bProgrammableRaster = GNaniteProgrammableRasterShadows != 0;
|
|
CullingConfig.SetViewFlags(SceneView);
|
|
|
|
Nanite::FCullingContext CullingContext = Nanite::InitCullingContext(GraphBuilder, SharedContext, Scene, PrevAtlasHZB, FullAtlasViewRect, CullingConfig);
|
|
Nanite::FRasterContext RasterContext = Nanite::InitRasterContext(GraphBuilder, SharedContext, AtlasSize, false, Nanite::EOutputBufferMode::DepthOnly);
|
|
|
|
bool bExtractStats = false;
|
|
if (GNaniteShowStats != 0)
|
|
{
|
|
FString AtlasFilterName = FString::Printf(TEXT("ShadowAtlas%d"), AtlasIndex);
|
|
bExtractStats = Nanite::IsStatFilterActive(AtlasFilterName);
|
|
}
|
|
|
|
if (PackedViews.Num() > 0)
|
|
{
|
|
Nanite::FRasterState RasterState;
|
|
|
|
Nanite::CullRasterize(
|
|
GraphBuilder,
|
|
Scene.NaniteRasterPipelines[ENaniteMeshPass::BasePass],
|
|
Scene,
|
|
SceneView,
|
|
PackedViews,
|
|
SharedContext,
|
|
CullingContext,
|
|
RasterContext,
|
|
RasterState,
|
|
nullptr, // InstanceDraws
|
|
bExtractStats
|
|
);
|
|
}
|
|
|
|
if (bUseHZB)
|
|
{
|
|
FRDGTextureRef FurthestHZBTexture;
|
|
BuildHZBFurthest(
|
|
GraphBuilder,
|
|
GraphBuilder.RegisterExternalTexture(GSystemTextures.BlackDummy),
|
|
RasterContext.DepthBuffer,
|
|
FullAtlasViewRect,
|
|
FeatureLevel,
|
|
Scene.GetShaderPlatform(),
|
|
TEXT("Shadow.AtlasHZB"),
|
|
/* OutFurthestHZBTexture = */ &FurthestHZBTexture,
|
|
PF_R32_FLOAT);
|
|
PrevAtlasHZBs[AtlasIndex] = GraphBuilder.ConvertToExternalTexture(FurthestHZBTexture);
|
|
}
|
|
else
|
|
{
|
|
PrevAtlasHZBs[AtlasIndex] = nullptr;
|
|
}
|
|
|
|
FRDGTextureRef ShadowMap = GraphBuilder.RegisterExternalTexture(ShadowMapAtlas.RenderTargets.DepthTarget);
|
|
|
|
for (FProjectedShadowInfo* ProjectedShadowInfo : ShadowsToEmit)
|
|
{
|
|
const FIntRect AtlasViewRect = ProjectedShadowInfo->GetOuterViewRect();
|
|
|
|
Nanite::EmitShadowMap(
|
|
GraphBuilder,
|
|
SharedContext,
|
|
RasterContext,
|
|
ShadowMap,
|
|
AtlasViewRect,
|
|
AtlasViewRect.Min,
|
|
ProjectedShadowInfo->GetShadowDepthRenderingViewMatrices().GetProjectionMatrix(),
|
|
ProjectedShadowInfo->GetShaderDepthBias(),
|
|
ProjectedShadowInfo->bDirectionalLight
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool IsParallelDispatchEnabled(const FProjectedShadowInfo* ProjectedShadowInfo, EShaderPlatform ShaderPlatform)
|
|
{
|
|
return GRHICommandList.UseParallelAlgorithms() && CVarParallelShadows.GetValueOnRenderThread()
|
|
&& (ProjectedShadowInfo->IsWholeSceneDirectionalShadow() || CVarParallelShadowsNonWholeScene.GetValueOnRenderThread())
|
|
// Parallel dispatch is not supported on mobile platform
|
|
&& !IsMobilePlatform(ShaderPlatform);
|
|
}
|
|
|
|
void FSceneRenderer::RenderShadowDepthMapAtlases(FRDGBuilder& GraphBuilder)
|
|
{
|
|
const bool bNaniteEnabled =
|
|
UseNanite(ShaderPlatform) &&
|
|
ActiveViewFamily->EngineShowFlags.NaniteMeshes &&
|
|
CVarNaniteShadows.GetValueOnRenderThread() != 0 &&
|
|
Nanite::GStreamingManager.HasResourceEntries();
|
|
|
|
auto& SortedShadowsForShadowDepthPass = ActiveViewFamily->SortedShadowsForShadowDepthPass;
|
|
|
|
Scene->PrevAtlasHZBs.SetNum(SortedShadowsForShadowDepthPass.ShadowMapAtlases.Num());
|
|
|
|
FRDGExternalAccessQueue ExternalAccessQueue;
|
|
|
|
for (int32 AtlasIndex = 0; AtlasIndex < SortedShadowsForShadowDepthPass.ShadowMapAtlases.Num(); AtlasIndex++)
|
|
{
|
|
FSortedShadowMapAtlas& ShadowMapAtlas = SortedShadowsForShadowDepthPass.ShadowMapAtlases[AtlasIndex];
|
|
FRDGTextureRef AtlasDepthTexture = GraphBuilder.RegisterExternalTexture(ShadowMapAtlas.RenderTargets.DepthTarget);
|
|
const FIntPoint AtlasSize = AtlasDepthTexture->Desc.Extent;
|
|
|
|
RDG_EVENT_SCOPE(GraphBuilder, "Atlas%u %ux%u", AtlasIndex, AtlasSize.X, AtlasSize.Y);
|
|
|
|
TArray<FProjectedShadowInfo*, SceneRenderingAllocator> ParallelShadowPasses;
|
|
TArray<FProjectedShadowInfo*, SceneRenderingAllocator> SerialShadowPasses;
|
|
|
|
// Gather our passes here to minimize switching render passes
|
|
for (FProjectedShadowInfo* ProjectedShadowInfo : ShadowMapAtlas.Shadows)
|
|
{
|
|
if (IsParallelDispatchEnabled(ProjectedShadowInfo, ShaderPlatform))
|
|
{
|
|
ParallelShadowPasses.Add(ProjectedShadowInfo);
|
|
}
|
|
else
|
|
{
|
|
SerialShadowPasses.Add(ProjectedShadowInfo);
|
|
}
|
|
}
|
|
|
|
#if WANTS_DRAW_MESH_EVENTS
|
|
FLightSceneProxy* CurrentLightForDrawEvent = nullptr;
|
|
FDrawEvent LightEvent;
|
|
#endif
|
|
|
|
const auto SetLightEventForShadow = [&](FProjectedShadowInfo* ProjectedShadowInfo)
|
|
{
|
|
#if WANTS_DRAW_MESH_EVENTS
|
|
if (!CurrentLightForDrawEvent || ProjectedShadowInfo->GetLightSceneInfo().Proxy != CurrentLightForDrawEvent)
|
|
{
|
|
if (CurrentLightForDrawEvent)
|
|
{
|
|
GraphBuilder.EndEventScope();
|
|
}
|
|
|
|
CurrentLightForDrawEvent = ProjectedShadowInfo->GetLightSceneInfo().Proxy;
|
|
FString LightNameWithLevel;
|
|
GetLightNameForDrawEvent(CurrentLightForDrawEvent, LightNameWithLevel);
|
|
GraphBuilder.BeginEventScope(RDG_EVENT_NAME("%s", *LightNameWithLevel));
|
|
}
|
|
#endif
|
|
};
|
|
|
|
const auto EndLightEvent = [&]()
|
|
{
|
|
#if WANTS_DRAW_MESH_EVENTS
|
|
if (CurrentLightForDrawEvent)
|
|
{
|
|
GraphBuilder.EndEventScope();
|
|
CurrentLightForDrawEvent = nullptr;
|
|
}
|
|
#endif
|
|
};
|
|
|
|
AddClearShadowDepthPass(GraphBuilder, AtlasDepthTexture);
|
|
|
|
if (ParallelShadowPasses.Num() > 0)
|
|
{
|
|
for (FProjectedShadowInfo* ProjectedShadowInfo : ParallelShadowPasses)
|
|
{
|
|
RDG_GPU_MASK_SCOPE(GraphBuilder, GetGPUMaskForShadow(ProjectedShadowInfo));
|
|
SetLightEventForShadow(ProjectedShadowInfo);
|
|
|
|
const bool bParallelDispatch = true;
|
|
bool bDoCrossGPUCopy = false;
|
|
#if WITH_MGPU
|
|
bDoCrossGPUCopy = IsShadowCached(ProjectedShadowInfo);
|
|
#endif
|
|
ProjectedShadowInfo->RenderDepth(GraphBuilder, this, AtlasDepthTexture, bParallelDispatch, bDoCrossGPUCopy);
|
|
}
|
|
}
|
|
|
|
EndLightEvent();
|
|
|
|
if (SerialShadowPasses.Num() > 0)
|
|
{
|
|
for (FProjectedShadowInfo* ProjectedShadowInfo : SerialShadowPasses)
|
|
{
|
|
RDG_GPU_MASK_SCOPE(GraphBuilder, GetGPUMaskForShadow(ProjectedShadowInfo));
|
|
SetLightEventForShadow(ProjectedShadowInfo);
|
|
|
|
const bool bParallelDispatch = false;
|
|
bool bDoCrossGPUCopy = false;
|
|
#if WITH_MGPU
|
|
bDoCrossGPUCopy = IsShadowCached(ProjectedShadowInfo);
|
|
#endif
|
|
ProjectedShadowInfo->RenderDepth(GraphBuilder, this, AtlasDepthTexture, bParallelDispatch, bDoCrossGPUCopy);
|
|
}
|
|
}
|
|
|
|
EndLightEvent();
|
|
|
|
if (bNaniteEnabled)
|
|
{
|
|
const FViewInfo& SceneView = Views[0];
|
|
RenderShadowDepthAtlasNanite(GraphBuilder, FeatureLevel, *Scene, SceneView, ShadowMapAtlas, AtlasIndex);
|
|
}
|
|
|
|
// Make readable because AtlasDepthTexture is not tracked via RDG yet
|
|
// On mobile CSM atlas sampled only in pixel shaders
|
|
ERHIAccess AtlasDepthTextureAccessFinal = (FeatureLevel == ERHIFeatureLevel::ES3_1 ? ERHIAccess::SRVGraphics : ERHIAccess::SRVMask);
|
|
ShadowMapAtlas.RenderTargets.DepthTarget = ConvertToExternalAccessTexture(GraphBuilder, ExternalAccessQueue, AtlasDepthTexture, AtlasDepthTextureAccessFinal);
|
|
}
|
|
|
|
ExternalAccessQueue.Submit(GraphBuilder);
|
|
}
|
|
|
|
void FSceneRenderer::RenderVirtualShadowMaps(FRDGBuilder& GraphBuilder, bool bNaniteEnabled)
|
|
{
|
|
auto& SortedShadowsForShadowDepthPass = ActiveViewFamily->SortedShadowsForShadowDepthPass;
|
|
auto& VirtualShadowMapArray = ActiveViewFamily->VirtualShadowMapArray;
|
|
|
|
if (SortedShadowsForShadowDepthPass.VirtualShadowMapShadows.Num() == 0 &&
|
|
SortedShadowsForShadowDepthPass.VirtualShadowMapClipmaps.Num() == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
FVirtualShadowMapArrayCacheManager *CacheManager = ActiveViewFamily->VirtualShadowMapArray.CacheManager;
|
|
|
|
// TODO: Separate out the decision about nanite using HZB and stuff like HZB culling invalidations?
|
|
const bool bVSMUseHZB = VirtualShadowMapArray.UseHzbOcclusion();
|
|
|
|
const FIntPoint VirtualShadowSize = VirtualShadowMapArray.GetPhysicalPoolSize();
|
|
const FIntRect VirtualShadowViewRect = FIntRect(0, 0, VirtualShadowSize.X, VirtualShadowSize.Y);
|
|
|
|
Nanite::FSharedContext SharedContext{};
|
|
SharedContext.FeatureLevel = FeatureLevel;
|
|
SharedContext.ShaderMap = GetGlobalShaderMap(SharedContext.FeatureLevel);
|
|
SharedContext.Pipeline = Nanite::EPipeline::Shadows;
|
|
|
|
if (bNaniteEnabled)
|
|
{
|
|
const TRefCountPtr<IPooledRenderTarget> PrevHZBPhysical = bVSMUseHZB ? CacheManager->PrevBuffers.HZBPhysical : nullptr;
|
|
|
|
{
|
|
RDG_EVENT_SCOPE(GraphBuilder, "RenderVirtualShadowMaps(Nanite)");
|
|
|
|
check(VirtualShadowMapArray.PhysicalPagePoolRDG != nullptr);
|
|
|
|
Nanite::FRasterContext RasterContext = Nanite::InitRasterContext(
|
|
GraphBuilder,
|
|
SharedContext,
|
|
VirtualShadowSize,
|
|
false,
|
|
Nanite::EOutputBufferMode::DepthOnly,
|
|
false, // Clear entire texture
|
|
nullptr, 0,
|
|
VirtualShadowMapArray.PhysicalPagePoolRDG );
|
|
|
|
const bool bUpdateStreaming = CVarNaniteShadowsUpdateStreaming.GetValueOnRenderThread() != 0;
|
|
|
|
const FViewInfo& SceneView = Views[0];
|
|
|
|
auto FilterAndRenderVirtualShadowMaps = [
|
|
&SortedShadowsForShadowDepthPass = SortedShadowsForShadowDepthPass,
|
|
&SharedContext,
|
|
&RasterContext,
|
|
&VirtualShadowMapArray = VirtualShadowMapArray,
|
|
&GraphBuilder,
|
|
&SceneView,
|
|
bUpdateStreaming,
|
|
bVSMUseHZB,
|
|
Scene = Scene,
|
|
CacheManager = CacheManager,
|
|
PrevHZBPhysical,
|
|
VirtualShadowViewRect
|
|
](bool bShouldClampToNearPlane, const FString &VirtualFilterName)
|
|
{
|
|
TArray<Nanite::FPackedView, SceneRenderingAllocator> VirtualShadowViews;
|
|
|
|
// Add any clipmaps first to the ortho rendering pass
|
|
if (bShouldClampToNearPlane)
|
|
{
|
|
for (const TSharedPtr<FVirtualShadowMapClipmap>& Clipmap : SortedShadowsForShadowDepthPass.VirtualShadowMapClipmaps)
|
|
{
|
|
VirtualShadowMapArray.AddRenderViews(
|
|
Clipmap,
|
|
ComputeNaniteShadowsLODScaleFactor(),
|
|
PrevHZBPhysical.IsValid(),
|
|
bVSMUseHZB,
|
|
bShouldClampToNearPlane,
|
|
VirtualShadowViews);
|
|
}
|
|
}
|
|
|
|
for (FProjectedShadowInfo* ProjectedShadowInfo : SortedShadowsForShadowDepthPass.VirtualShadowMapShadows)
|
|
{
|
|
if (ProjectedShadowInfo->ShouldClampToNearPlane() == bShouldClampToNearPlane && ProjectedShadowInfo->HasVirtualShadowMap())
|
|
{
|
|
VirtualShadowMapArray.AddRenderViews(
|
|
ProjectedShadowInfo,
|
|
ComputeNaniteShadowsLODScaleFactor(),
|
|
PrevHZBPhysical.IsValid(),
|
|
bVSMUseHZB,
|
|
bShouldClampToNearPlane,
|
|
VirtualShadowViews);
|
|
}
|
|
}
|
|
|
|
if (VirtualShadowViews.Num() > 0)
|
|
{
|
|
int32 NumPrimaryViews = VirtualShadowViews.Num();
|
|
VirtualShadowMapArray.CreateMipViews( VirtualShadowViews );
|
|
|
|
Nanite::FRasterState RasterState;
|
|
if (bShouldClampToNearPlane)
|
|
{
|
|
RasterState.bNearClip = false;
|
|
}
|
|
|
|
Nanite::FCullingContext::FConfiguration CullingConfig = { 0 };
|
|
CullingConfig.bUpdateStreaming = CVarNaniteShadowsUpdateStreaming.GetValueOnRenderThread() != 0;
|
|
CullingConfig.bTwoPassOcclusion = VirtualShadowMapArray.UseTwoPassHzbOcclusion();
|
|
CullingConfig.bProgrammableRaster = GNaniteProgrammableRasterShadows != 0;
|
|
CullingConfig.SetViewFlags(SceneView);
|
|
|
|
Nanite::FCullingContext CullingContext = Nanite::InitCullingContext(
|
|
GraphBuilder,
|
|
SharedContext,
|
|
*Scene,
|
|
PrevHZBPhysical,
|
|
VirtualShadowViewRect,
|
|
CullingConfig
|
|
);
|
|
|
|
const bool bExtractStats = Nanite::IsStatFilterActive(VirtualFilterName);
|
|
|
|
Nanite::CullRasterize(
|
|
GraphBuilder,
|
|
Scene->NaniteRasterPipelines[ENaniteMeshPass::BasePass],
|
|
*Scene,
|
|
SceneView,
|
|
VirtualShadowViews,
|
|
NumPrimaryViews,
|
|
SharedContext,
|
|
CullingContext,
|
|
RasterContext,
|
|
RasterState,
|
|
nullptr,
|
|
&VirtualShadowMapArray,
|
|
bExtractStats
|
|
);
|
|
}
|
|
};
|
|
|
|
{
|
|
RDG_EVENT_SCOPE(GraphBuilder, "DirectionalLights");
|
|
static FString VirtualFilterName = TEXT("VSM_Directional");
|
|
FilterAndRenderVirtualShadowMaps(true, VirtualFilterName);
|
|
}
|
|
|
|
{
|
|
RDG_EVENT_SCOPE(GraphBuilder, "LocalLights");
|
|
static FString VirtualFilterName = TEXT("VSM_Local");
|
|
FilterAndRenderVirtualShadowMaps(false, VirtualFilterName);
|
|
}
|
|
|
|
if (bVSMUseHZB)
|
|
{
|
|
VirtualShadowMapArray.UpdateHZB(GraphBuilder);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (UseNonNaniteVirtualShadowMaps(ShaderPlatform, FeatureLevel))
|
|
{
|
|
VirtualShadowMapArray.RenderVirtualShadowMapsNonNanite(GraphBuilder, SortedShadowsForShadowDepthPass.VirtualShadowMapShadows, *Scene, Views);
|
|
}
|
|
|
|
// If separate static/dynamic caching is enabled, we may need to merge some pages after rendering
|
|
VirtualShadowMapArray.MergeStaticPhysicalPages(GraphBuilder);
|
|
}
|
|
|
|
void FSceneRenderer::RenderShadowDepthMaps(FRDGBuilder& GraphBuilder, FInstanceCullingManager &InstanceCullingManager)
|
|
{
|
|
ensureMsgf(!ActiveViewFamily->bShadowDepthRenderCompleted, TEXT("RenderShadowDepthMaps called twice in the same frame"));
|
|
|
|
CSV_SCOPED_TIMING_STAT_EXCLUSIVE(RenderShadows);
|
|
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(FSceneRenderer::RenderShadowDepthMaps);
|
|
SCOPED_NAMED_EVENT(FSceneRenderer_RenderShadowDepthMaps, FColor::Emerald);
|
|
|
|
RDG_EVENT_SCOPE(GraphBuilder, "ShadowDepths");
|
|
RDG_GPU_STAT_SCOPE(GraphBuilder, ShadowDepths);
|
|
|
|
auto& SortedShadowsForShadowDepthPass = ActiveViewFamily->SortedShadowsForShadowDepthPass;
|
|
|
|
FRDGExternalAccessQueue ExternalAccessQueue;
|
|
|
|
// Ensure all shadow view dynamic primitives are uploaded before shadow-culling batching pass.
|
|
// TODO: automate this such that:
|
|
// 1. we only process views that need it (have dynamic primitives)
|
|
// 2. it is integrated in the GPU-scene (it already collects the dynamic primives and know about them...)
|
|
// 3. BUT: we need to touch the views to update the GPUScene buffer references in the FViewInfo
|
|
// so need to refactor that into its own binding point, probably. Or something.
|
|
for (FSortedShadowMapAtlas& ShadowMapAtlas : SortedShadowsForShadowDepthPass.ShadowMapAtlases)
|
|
{
|
|
for (FProjectedShadowInfo* ProjectedShadowInfo : ShadowMapAtlas.Shadows)
|
|
{
|
|
Scene->GPUScene.UploadDynamicPrimitiveShaderDataForView(GraphBuilder, Scene, *ProjectedShadowInfo->ShadowDepthView, ExternalAccessQueue, true);
|
|
}
|
|
}
|
|
for (FSortedShadowMapAtlas& ShadowMap : SortedShadowsForShadowDepthPass.ShadowMapCubemaps)
|
|
{
|
|
check(ShadowMap.Shadows.Num() == 1);
|
|
FProjectedShadowInfo* ProjectedShadowInfo = ShadowMap.Shadows[0];
|
|
Scene->GPUScene.UploadDynamicPrimitiveShaderDataForView(GraphBuilder, Scene, *ProjectedShadowInfo->ShadowDepthView, ExternalAccessQueue, true);
|
|
}
|
|
for (FProjectedShadowInfo* ProjectedShadowInfo : SortedShadowsForShadowDepthPass.PreshadowCache.Shadows)
|
|
{
|
|
if (!ProjectedShadowInfo->bDepthsCached)
|
|
{
|
|
Scene->GPUScene.UploadDynamicPrimitiveShaderDataForView(GraphBuilder, Scene, *ProjectedShadowInfo->ShadowDepthView, ExternalAccessQueue, true);
|
|
}
|
|
}
|
|
for (const FSortedShadowMapAtlas& ShadowMapAtlas : SortedShadowsForShadowDepthPass.TranslucencyShadowMapAtlases)
|
|
{
|
|
for (FProjectedShadowInfo* ProjectedShadowInfo : ShadowMapAtlas.Shadows)
|
|
{
|
|
Scene->GPUScene.UploadDynamicPrimitiveShaderDataForView(GraphBuilder, Scene, *ProjectedShadowInfo->ShadowDepthView, ExternalAccessQueue, true);
|
|
}
|
|
}
|
|
for (FProjectedShadowInfo* ProjectedShadowInfo : SortedShadowsForShadowDepthPass.VirtualShadowMapShadows)
|
|
{
|
|
Scene->GPUScene.UploadDynamicPrimitiveShaderDataForView(GraphBuilder, Scene, *ProjectedShadowInfo->ShadowDepthView, ExternalAccessQueue, true);
|
|
}
|
|
|
|
ExternalAccessQueue.Submit(GraphBuilder);
|
|
|
|
// Begin new deferred culling bacthing scope to catch shadow render passes, as there can use dynamic primitives that have not been uploaded before
|
|
// the previous batching scope. Also flushes the culling views registered during the setup (in InitViewsAfterPrepass) that are referenced in the shadow view
|
|
// culling.
|
|
InstanceCullingManager.BeginDeferredCulling(GraphBuilder, Scene->GPUScene);
|
|
|
|
const bool bNaniteEnabled =
|
|
UseNanite(ShaderPlatform) &&
|
|
ActiveViewFamily->EngineShowFlags.NaniteMeshes &&
|
|
Nanite::GStreamingManager.HasResourceEntries();
|
|
|
|
RenderVirtualShadowMaps(GraphBuilder, bNaniteEnabled);
|
|
|
|
// Render non-VSM shadows
|
|
RenderShadowDepthMapAtlases(GraphBuilder);
|
|
|
|
const bool bUseGeometryShader = !GRHISupportsArrayIndexFromAnyShader;
|
|
|
|
for (int32 CubemapIndex = 0; CubemapIndex < SortedShadowsForShadowDepthPass.ShadowMapCubemaps.Num(); CubemapIndex++)
|
|
{
|
|
FSortedShadowMapAtlas& ShadowMap = SortedShadowsForShadowDepthPass.ShadowMapCubemaps[CubemapIndex];
|
|
FRDGTextureRef ShadowDepthTexture = GraphBuilder.RegisterExternalTexture(ShadowMap.RenderTargets.DepthTarget);
|
|
const FIntPoint TargetSize = ShadowDepthTexture->Desc.Extent;
|
|
|
|
check(ShadowMap.Shadows.Num() == 1);
|
|
FProjectedShadowInfo* ProjectedShadowInfo = ShadowMap.Shadows[0];
|
|
RDG_GPU_MASK_SCOPE(GraphBuilder, GetGPUMaskForShadow(ProjectedShadowInfo));
|
|
|
|
FString LightNameWithLevel;
|
|
GetLightNameForDrawEvent(ProjectedShadowInfo->GetLightSceneInfo().Proxy, LightNameWithLevel);
|
|
RDG_EVENT_SCOPE(GraphBuilder, "Cubemap %s %u^2", *LightNameWithLevel, TargetSize.X, TargetSize.Y);
|
|
|
|
// Only clear when we're not copying from a cached shadow map.
|
|
if (ProjectedShadowInfo->CacheMode != SDCM_MovablePrimitivesOnly || !Scene->GetCachedShadowMapDataRef(ProjectedShadowInfo->GetLightSceneInfo().Id, FMath::Max(ProjectedShadowInfo->CascadeSettings.ShadowSplitIndex, 0)).bCachedShadowMapHasPrimitives)
|
|
{
|
|
AddClearShadowDepthPass(GraphBuilder, ShadowDepthTexture);
|
|
}
|
|
|
|
{
|
|
const bool bDoParallelDispatch = IsParallelDispatchEnabled(ProjectedShadowInfo, ShaderPlatform);
|
|
const bool bDoCrossGPUCopy = false;
|
|
ProjectedShadowInfo->RenderDepth(GraphBuilder, this, ShadowDepthTexture, bDoParallelDispatch, bDoCrossGPUCopy);
|
|
}
|
|
|
|
if (bNaniteEnabled &&
|
|
CVarNaniteShadows.GetValueOnRenderThread() &&
|
|
ProjectedShadowInfo->bNaniteGeometry &&
|
|
ProjectedShadowInfo->CacheMode != SDCM_MovablePrimitivesOnly // See note in RenderShadowDepthMapAtlases
|
|
)
|
|
{
|
|
const bool bUseHZB = (CVarNaniteShadowsUseHZB.GetValueOnRenderThread() != 0);
|
|
|
|
FString LightName;
|
|
GetLightNameForDrawEvent(ProjectedShadowInfo->GetLightSceneInfo().Proxy, LightName);
|
|
|
|
{
|
|
RDG_EVENT_SCOPE( GraphBuilder, "Nanite Cubemap %s %ux%u", *LightName, ProjectedShadowInfo->ResolutionX, ProjectedShadowInfo->ResolutionY );
|
|
|
|
FRDGTextureRef RDGShadowMap = GraphBuilder.RegisterExternalTexture( ShadowMap.RenderTargets.DepthTarget, TEXT("ShadowDepthBuffer") );
|
|
|
|
// Cubemap shadows reverse the cull mode due to the face matrices (see FShadowDepthPassMeshProcessor::AddMeshBatch)
|
|
Nanite::FRasterState RasterState;
|
|
RasterState.CullMode = CM_CCW;
|
|
|
|
const bool bUpdateStreaming = CVarNaniteShadowsUpdateStreaming.GetValueOnRenderThread() != 0;
|
|
|
|
FLightSceneInfo& LightSceneInfo = ProjectedShadowInfo->GetLightSceneInfo();
|
|
|
|
FString CubeFilterName;
|
|
if (GNaniteShowStats != 0)
|
|
{
|
|
// Get the base light filter name.
|
|
CubeFilterName = Nanite::GetFilterNameForLight(LightSceneInfo.Proxy);
|
|
CubeFilterName.Append(TEXT("_Face_"));
|
|
}
|
|
|
|
for (int32 CubemapFaceIndex = 0; CubemapFaceIndex < 6; CubemapFaceIndex++)
|
|
{
|
|
RDG_EVENT_SCOPE( GraphBuilder, "Face %u", CubemapFaceIndex );
|
|
|
|
// We always render to a whole face at once
|
|
const FIntRect ShadowViewRect = FIntRect(0, 0, TargetSize.X, TargetSize.Y);
|
|
check(ProjectedShadowInfo->X == ShadowViewRect.Min.X);
|
|
check(ProjectedShadowInfo->Y == ShadowViewRect.Min.Y);
|
|
check(ProjectedShadowInfo->ResolutionX == ShadowViewRect.Max.X);
|
|
check(ProjectedShadowInfo->ResolutionY == ShadowViewRect.Max.Y);
|
|
check(ProjectedShadowInfo->BorderSize == 0);
|
|
|
|
FPersistentShadowStateKey ShadowKey;
|
|
ShadowKey.ProjectionId = CubemapFaceIndex;
|
|
ShadowKey.SubjectPrimitiveComponentIndex = 0;
|
|
|
|
FPersistentShadowState* PrevShadowState = LightSceneInfo.PrevPersistentShadows.Find(ShadowKey);
|
|
|
|
const FViewInfo& SceneView = Views[0];
|
|
|
|
Nanite::FSharedContext SharedContext{};
|
|
SharedContext.FeatureLevel = Scene->GetFeatureLevel();
|
|
SharedContext.ShaderMap = GetGlobalShaderMap(SharedContext.FeatureLevel);
|
|
SharedContext.Pipeline = Nanite::EPipeline::Shadows;
|
|
|
|
TRefCountPtr<IPooledRenderTarget> PrevHZB = (PrevShadowState && bUseHZB) ? PrevShadowState->HZB : nullptr;
|
|
|
|
Nanite::FCullingContext::FConfiguration CullingConfig = { 0 };
|
|
CullingConfig.bTwoPassOcclusion = true;
|
|
CullingConfig.bUpdateStreaming = bUpdateStreaming;
|
|
CullingConfig.bProgrammableRaster = GNaniteProgrammableRasterShadows != 0;
|
|
CullingConfig.SetViewFlags(SceneView);
|
|
|
|
Nanite::FCullingContext CullingContext = Nanite::InitCullingContext(GraphBuilder, SharedContext, *Scene, PrevHZB, ShadowViewRect, CullingConfig);
|
|
Nanite::FRasterContext RasterContext = Nanite::InitRasterContext(GraphBuilder, SharedContext, TargetSize, false, Nanite::EOutputBufferMode::DepthOnly);
|
|
|
|
// Setup packed view
|
|
TArray<Nanite::FPackedView, SceneRenderingAllocator> PackedViews;
|
|
{
|
|
Nanite::FPackedViewParams Params;
|
|
Params.ViewMatrices = ProjectedShadowInfo->GetShadowDepthRenderingViewMatrices(CubemapFaceIndex);
|
|
Params.ViewRect = ShadowViewRect;
|
|
Params.RasterContextSize = TargetSize;
|
|
Params.LODScaleFactor = ComputeNaniteShadowsLODScaleFactor();
|
|
Params.PrevViewMatrices = Params.ViewMatrices;
|
|
Params.HZBTestViewRect = ShadowViewRect;
|
|
UpdatePackedViewParamsFromPrevShadowState(Params, PrevShadowState);
|
|
|
|
PackedViews.Add(Nanite::CreatePackedView(Params));
|
|
}
|
|
|
|
FString CubeFaceFilterName;
|
|
if (GNaniteShowStats != 0)
|
|
{
|
|
CubeFaceFilterName = CubeFilterName;
|
|
CubeFaceFilterName.AppendInt(CubemapFaceIndex);
|
|
}
|
|
|
|
const bool bExtractStats = Nanite::IsStatFilterActive(CubeFaceFilterName);
|
|
|
|
Nanite::CullRasterize(
|
|
GraphBuilder,
|
|
Scene->NaniteRasterPipelines[ENaniteMeshPass::BasePass],
|
|
*Scene,
|
|
SceneView,
|
|
PackedViews,
|
|
SharedContext,
|
|
CullingContext,
|
|
RasterContext,
|
|
RasterState,
|
|
nullptr,
|
|
bExtractStats
|
|
);
|
|
|
|
Nanite::EmitCubemapShadow(
|
|
GraphBuilder,
|
|
SharedContext,
|
|
RasterContext,
|
|
RDGShadowMap,
|
|
ShadowViewRect,
|
|
CubemapFaceIndex,
|
|
bUseGeometryShader);
|
|
|
|
TRefCountPtr<IPooledRenderTarget> HZB;
|
|
if (bUseHZB)
|
|
{
|
|
FRDGTextureRef FurthestHZBTexture;
|
|
BuildHZBFurthest(
|
|
GraphBuilder,
|
|
GraphBuilder.RegisterExternalTexture(GSystemTextures.BlackDummy),
|
|
RasterContext.DepthBuffer,
|
|
ShadowViewRect,
|
|
FeatureLevel,
|
|
ShaderPlatform,
|
|
TEXT("Shadow.CubemapHZB"),
|
|
/* OutFurthestHZBTexture = */ &FurthestHZBTexture);
|
|
|
|
HZB = GraphBuilder.ConvertToExternalTexture(FurthestHZBTexture);
|
|
}
|
|
UpdateCurrentFrameHZB(LightSceneInfo, ShadowKey, ProjectedShadowInfo, HZB, CubemapFaceIndex);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Make readable because ShadowDepthTexture is not tracked via RDG yet
|
|
ShadowMap.RenderTargets.DepthTarget = ConvertToExternalAccessTexture(GraphBuilder, ExternalAccessQueue, ShadowDepthTexture);
|
|
}
|
|
|
|
ExternalAccessQueue.Submit(GraphBuilder);
|
|
|
|
if (SortedShadowsForShadowDepthPass.PreshadowCache.Shadows.Num() > 0)
|
|
{
|
|
RDG_EVENT_SCOPE(GraphBuilder, "PreshadowCache");
|
|
|
|
FRDGTextureRef PreshadowCacheTexture = GraphBuilder.RegisterExternalTexture(SortedShadowsForShadowDepthPass.PreshadowCache.RenderTargets.DepthTarget);
|
|
|
|
for (FProjectedShadowInfo* ProjectedShadowInfo : SortedShadowsForShadowDepthPass.PreshadowCache.Shadows)
|
|
{
|
|
if (!ProjectedShadowInfo->bDepthsCached)
|
|
{
|
|
RDG_GPU_MASK_SCOPE(GraphBuilder, GetGPUMaskForShadow(ProjectedShadowInfo));
|
|
AddClearShadowDepthPass(GraphBuilder, PreshadowCacheTexture, ProjectedShadowInfo);
|
|
|
|
const bool bParallelDispatch = IsParallelDispatchEnabled(ProjectedShadowInfo, ShaderPlatform);
|
|
const bool bDoCrossGPUCopy = true;
|
|
ProjectedShadowInfo->RenderDepth(GraphBuilder, this, PreshadowCacheTexture, bParallelDispatch, bDoCrossGPUCopy);
|
|
ProjectedShadowInfo->bDepthsCached = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int32 AtlasIndex = 0; AtlasIndex < SortedShadowsForShadowDepthPass.TranslucencyShadowMapAtlases.Num(); AtlasIndex++)
|
|
{
|
|
const FSortedShadowMapAtlas& ShadowMapAtlas = SortedShadowsForShadowDepthPass.TranslucencyShadowMapAtlases[AtlasIndex];
|
|
|
|
FRDGTextureRef ColorTarget0 = GraphBuilder.RegisterExternalTexture(ShadowMapAtlas.RenderTargets.ColorTargets[0]);
|
|
FRDGTextureRef ColorTarget1 = GraphBuilder.RegisterExternalTexture(ShadowMapAtlas.RenderTargets.ColorTargets[1]);
|
|
const FIntPoint TargetSize = ColorTarget0->Desc.Extent;
|
|
|
|
FRenderTargetBindingSlots RenderTargets;
|
|
RenderTargets[0] = FRenderTargetBinding(ColorTarget0, ERenderTargetLoadAction::ELoad);
|
|
RenderTargets[1] = FRenderTargetBinding(ColorTarget1, ERenderTargetLoadAction::ELoad);
|
|
|
|
RDG_EVENT_SCOPE(GraphBuilder, "TranslucencyAtlas%u %u^2", AtlasIndex, TargetSize.X, TargetSize.Y);
|
|
|
|
for (FProjectedShadowInfo* ProjectedShadowInfo : ShadowMapAtlas.Shadows)
|
|
{
|
|
RDG_GPU_MASK_SCOPE(GraphBuilder, GetGPUMaskForShadow(ProjectedShadowInfo));
|
|
ProjectedShadowInfo->RenderTranslucencyDepths(GraphBuilder, this, RenderTargets, InstanceCullingManager);
|
|
}
|
|
}
|
|
|
|
// Move current persistent shadow state to previous and clear current.
|
|
// TODO: This could be very slow.
|
|
for (const FLightSceneInfoCompact& Light : Scene->Lights)
|
|
{
|
|
Light.LightSceneInfo->PrevPersistentShadows = Light.LightSceneInfo->PersistentShadows;
|
|
Light.LightSceneInfo->PersistentShadows.Empty();
|
|
}
|
|
|
|
ActiveViewFamily->bShadowDepthRenderCompleted = true;
|
|
}
|
|
|
|
bool FShadowDepthPassMeshProcessor::Process(
|
|
const FMeshBatch& RESTRICT MeshBatch,
|
|
uint64 BatchElementMask,
|
|
int32 StaticMeshId,
|
|
const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy,
|
|
const FMaterialRenderProxy& RESTRICT MaterialRenderProxy,
|
|
const FMaterial& RESTRICT MaterialResource,
|
|
ERasterizerFillMode MeshFillMode,
|
|
ERasterizerCullMode MeshCullMode)
|
|
{
|
|
const FVertexFactory* VertexFactory = MeshBatch.VertexFactory;
|
|
|
|
TMeshProcessorShaders<
|
|
FShadowDepthVS,
|
|
FShadowDepthBasePS> ShadowDepthPassShaders;
|
|
|
|
const bool bUsePositionOnlyVS =
|
|
VertexFactory->SupportsPositionAndNormalOnlyStream()
|
|
&& MaterialResource.WritesEveryPixel(true)
|
|
&& !MaterialResource.MaterialModifiesMeshPosition_RenderThread();
|
|
|
|
// Use perspective correct shadow depths for shadow types which typically render low poly meshes into the shadow depth buffer.
|
|
// Depth will be interpolated to the pixel shader and written out, which disables HiZ and double speed Z.
|
|
// Directional light shadows use an ortho projection and can use the non-perspective correct path without artifacts.
|
|
// One pass point lights don't output a linear depth, so they are already perspective correct.
|
|
bool bUsePerspectiveCorrectShadowDepths = !ShadowDepthType.bDirectionalLight && !ShadowDepthType.bOnePassPointLightShadow;
|
|
bool bOnePassPointLightShadow = ShadowDepthType.bOnePassPointLightShadow;
|
|
|
|
bool bVirtualShadowMap = MeshPassTargetType == EMeshPass::VSMShadowDepth;
|
|
if (bVirtualShadowMap)
|
|
{
|
|
bUsePerspectiveCorrectShadowDepths = false;
|
|
bOnePassPointLightShadow = false;
|
|
}
|
|
|
|
if (!GetShadowDepthPassShaders(
|
|
MaterialResource,
|
|
VertexFactory,
|
|
FeatureLevel,
|
|
ShadowDepthType.bDirectionalLight,
|
|
bOnePassPointLightShadow,
|
|
bUsePositionOnlyVS,
|
|
bUsePerspectiveCorrectShadowDepths,
|
|
bVirtualShadowMap,
|
|
ShadowDepthPassShaders.VertexShader,
|
|
ShadowDepthPassShaders.PixelShader))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
FShadowDepthShaderElementData ShaderElementData;
|
|
ShaderElementData.InitializeMeshMaterialData(ViewIfDynamicMeshCommand, PrimitiveSceneProxy, MeshBatch, StaticMeshId, false);
|
|
|
|
const FMeshDrawCommandSortKey SortKey = CalculateMeshStaticSortKey(ShadowDepthPassShaders.VertexShader, ShadowDepthPassShaders.PixelShader);
|
|
|
|
const bool bUseGpuSceneInstancing = UseGPUScene(GShaderPlatformForFeatureLevel[FeatureLevel], FeatureLevel) && VertexFactory->SupportsGPUScene(FeatureLevel);
|
|
|
|
// Need to replicate for cube faces on host if GPU-scene is not available (for this draw).
|
|
const bool bPerformHostCubeFaceReplication = ShadowDepthType.bOnePassPointLightShadow && !bUseGpuSceneInstancing;
|
|
const uint32 InstanceFactor = bPerformHostCubeFaceReplication ? 6 : 1;
|
|
|
|
for (uint32 i = 0; i < InstanceFactor; i++)
|
|
{
|
|
ShaderElementData.LayerId = i;
|
|
ShaderElementData.bUseGpuSceneInstancing = bUseGpuSceneInstancing;
|
|
|
|
BuildMeshDrawCommands(
|
|
MeshBatch,
|
|
BatchElementMask,
|
|
PrimitiveSceneProxy,
|
|
MaterialRenderProxy,
|
|
MaterialResource,
|
|
PassDrawRenderState,
|
|
ShadowDepthPassShaders,
|
|
MeshFillMode,
|
|
MeshCullMode,
|
|
SortKey,
|
|
bUsePositionOnlyVS ? EMeshPassFeatures::PositionAndNormalOnly : EMeshPassFeatures::Default,
|
|
ShaderElementData);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FShadowDepthPassMeshProcessor::TryAddMeshBatch(const FMeshBatch& RESTRICT MeshBatch,
|
|
uint64 BatchElementMask,
|
|
const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy,
|
|
int32 StaticMeshId,
|
|
const FMaterialRenderProxy& MaterialRenderProxy,
|
|
const FMaterial& Material)
|
|
{
|
|
const EBlendMode BlendMode = Material.GetBlendMode();
|
|
const bool bShouldCastShadow = Material.ShouldCastDynamicShadows();
|
|
|
|
const FMeshDrawingPolicyOverrideSettings OverrideSettings = ComputeMeshOverrideSettings(MeshBatch);
|
|
const ERasterizerFillMode MeshFillMode = ComputeMeshFillMode(MeshBatch, Material, OverrideSettings);
|
|
|
|
ERasterizerCullMode FinalCullMode;
|
|
|
|
{
|
|
const ERasterizerCullMode MeshCullMode = ComputeMeshCullMode(MeshBatch, Material, OverrideSettings);
|
|
|
|
const bool bTwoSided = Material.IsTwoSided() || PrimitiveSceneProxy->CastsShadowAsTwoSided();
|
|
// Invert culling order when mobile HDR == false.
|
|
auto ShaderPlatform = GShaderPlatformForFeatureLevel[FeatureLevel];
|
|
static auto* MobileHDRCvar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.MobileHDR"));
|
|
check(MobileHDRCvar);
|
|
const bool bPlatformReversesCulling = (RHINeedsToSwitchVerticalAxis(ShaderPlatform) && MobileHDRCvar->GetValueOnAnyThread() == 0);
|
|
|
|
const bool bRenderSceneTwoSided = bTwoSided;
|
|
const bool bShadowReversesCulling = MeshPassTargetType == EMeshPass::VSMShadowDepth ? false : ShadowDepthType.bOnePassPointLightShadow;
|
|
const bool bReverseCullMode = XOR(bPlatformReversesCulling, bShadowReversesCulling);
|
|
|
|
FinalCullMode = bRenderSceneTwoSided ? CM_None : bReverseCullMode ? InverseCullMode(MeshCullMode) : MeshCullMode;
|
|
}
|
|
|
|
bool bResult = true;
|
|
if (bShouldCastShadow
|
|
&& ShouldIncludeDomainInMeshPass(Material.GetMaterialDomain())
|
|
&& ShouldIncludeMaterialInDefaultOpaquePass(Material)
|
|
&& EnumHasAnyFlags(MeshSelectionMask, MeshBatch.VertexFactory->SupportsGPUScene(FeatureLevel) ? EShadowMeshSelection::VSM : EShadowMeshSelection::SM))
|
|
{
|
|
const FMaterialRenderProxy* EffectiveMaterialRenderProxy = &MaterialRenderProxy;
|
|
const FMaterial* EffectiveMaterial = &Material;
|
|
|
|
OverrideWithDefaultMaterialForShadowDepth(EffectiveMaterialRenderProxy, EffectiveMaterial, FeatureLevel);
|
|
|
|
bResult = Process(MeshBatch, BatchElementMask, StaticMeshId, PrimitiveSceneProxy, *EffectiveMaterialRenderProxy, *EffectiveMaterial, MeshFillMode, FinalCullMode);
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
void FShadowDepthPassMeshProcessor::AddMeshBatch(const FMeshBatch& RESTRICT MeshBatch, uint64 BatchElementMask, const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy, int32 StaticMeshId)
|
|
{
|
|
if (MeshBatch.CastShadow)
|
|
{
|
|
const FMaterialRenderProxy* MaterialRenderProxy = MeshBatch.MaterialRenderProxy;
|
|
while (MaterialRenderProxy)
|
|
{
|
|
const FMaterial* Material = MaterialRenderProxy->GetMaterialNoFallback(FeatureLevel);
|
|
if (Material)
|
|
{
|
|
if (TryAddMeshBatch(MeshBatch, BatchElementMask, PrimitiveSceneProxy, StaticMeshId, *MaterialRenderProxy, *Material))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
MaterialRenderProxy = MaterialRenderProxy->GetFallback(FeatureLevel);
|
|
}
|
|
}
|
|
}
|
|
|
|
FShadowDepthPassMeshProcessor::FShadowDepthPassMeshProcessor(
|
|
const FScene* Scene,
|
|
const FSceneView* InViewIfDynamicMeshCommand,
|
|
FShadowDepthType InShadowDepthType,
|
|
FMeshPassDrawListContext* InDrawListContext,
|
|
EMeshPass::Type InMeshPassTargetType)
|
|
: FMeshPassProcessor(Scene, Scene->GetFeatureLevel(), InViewIfDynamicMeshCommand, InDrawListContext)
|
|
, ShadowDepthType(InShadowDepthType)
|
|
, MeshPassTargetType(InMeshPassTargetType)
|
|
{
|
|
if (UseNonNaniteVirtualShadowMaps(Scene->GetShaderPlatform(), Scene->GetFeatureLevel()))
|
|
{
|
|
// set up mesh filtering.
|
|
MeshSelectionMask = MeshPassTargetType == EMeshPass::VSMShadowDepth ? EShadowMeshSelection::VSM : EShadowMeshSelection::SM;
|
|
}
|
|
else
|
|
{
|
|
// If VSMs are disabled, pipe all kinds of draws into the regular SMs
|
|
MeshSelectionMask = EShadowMeshSelection::All;
|
|
}
|
|
SetStateForShadowDepth(ShadowDepthType.bOnePassPointLightShadow, ShadowDepthType.bDirectionalLight, PassDrawRenderState, MeshPassTargetType);
|
|
}
|
|
|
|
FShadowDepthType CSMShadowDepthType(true, false);
|
|
|
|
FMeshPassProcessor* CreateCSMShadowDepthPassProcessor(const FScene* Scene, const FSceneView* InViewIfDynamicMeshCommand, FMeshPassDrawListContext* InDrawListContext)
|
|
{
|
|
return new(FMemStack::Get()) FShadowDepthPassMeshProcessor(
|
|
Scene,
|
|
InViewIfDynamicMeshCommand,
|
|
CSMShadowDepthType,
|
|
InDrawListContext,
|
|
EMeshPass::CSMShadowDepth);
|
|
}
|
|
|
|
FRegisterPassProcessorCreateFunction RegisterCSMShadowDepthPass(&CreateCSMShadowDepthPassProcessor, EShadingPath::Deferred, EMeshPass::CSMShadowDepth, EMeshPassFlags::CachedMeshCommands);
|
|
|
|
FMeshPassProcessor* CreateVSMShadowDepthPassProcessor(const FScene* Scene, const FSceneView* InViewIfDynamicMeshCommand, FMeshPassDrawListContext* InDrawListContext)
|
|
{
|
|
// Only create the mesh pass processor if VSMs are not enabled as this prevents wasting time caching the SM draw commands
|
|
if (UseNonNaniteVirtualShadowMaps(Scene->GetShaderPlatform(), Scene->GetFeatureLevel()))
|
|
{
|
|
return new(FMemStack::Get()) FShadowDepthPassMeshProcessor(
|
|
Scene,
|
|
InViewIfDynamicMeshCommand,
|
|
CSMShadowDepthType,
|
|
InDrawListContext,
|
|
EMeshPass::VSMShadowDepth);
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
FRegisterPassProcessorCreateFunction RegisterVSMShadowDepthPass(&CreateVSMShadowDepthPassProcessor, EShadingPath::Deferred, EMeshPass::VSMShadowDepth, EMeshPassFlags::CachedMeshCommands);
|
|
|
|
|
|
FRegisterPassProcessorCreateFunction RegisterMobileCSMShadowDepthPass(&CreateCSMShadowDepthPassProcessor, EShadingPath::Mobile, EMeshPass::CSMShadowDepth, EMeshPassFlags::CachedMeshCommands);
|