// Copyright Epic Games, Inc. All Rights Reserved. #include "LumenSceneLighting.h" #include "Materials/Material.h" #include "RendererPrivate.h" #include "ScenePrivate.h" #include "SceneUtils.h" #include "PipelineStateCache.h" #include "ShaderParameterStruct.h" #include "ShaderPermutationUtils.h" #include "VolumeLighting.h" #include "DistanceFieldLightingShared.h" #include "VolumetricCloudRendering.h" #include "LumenTracingUtils.h" #include "LightFunctionAtlas.h" #include "LightFunctionRendering.h" using namespace LightFunctionAtlas; static TAutoConsoleVariable CVarLumenLumenSceneDirectLighting( TEXT("r.LumenScene.DirectLighting"), 1, TEXT("Whether to compute direct ligshting for surface cache."), ECVF_Scalability | ECVF_RenderThreadSafe); int32 GLumenDirectLightingOffscreenShadowingTraceMeshSDFs = 1; FAutoConsoleVariableRef CVarLumenDirectLightingOffscreenShadowingTraceMeshSDFs( TEXT("r.LumenScene.DirectLighting.OffscreenShadowing.TraceMeshSDFs"), GLumenDirectLightingOffscreenShadowingTraceMeshSDFs, TEXT("Whether to trace against Mesh Signed Distance Fields for offscreen shadowing, or to trace against the lower resolution Global SDF."), ECVF_Scalability | ECVF_RenderThreadSafe ); static TAutoConsoleVariable CVarLumenDirectLightingMaxLightsPerTile( TEXT("r.LumenScene.DirectLighting.MaxLightsPerTile"), 8, TEXT("Max number of lights to pick per tile based on their intenstiy and attenuation. Valid values are 4/8/16/32. Increasing this value will cause more memory usage and will slow down Lumen surface cache direct lighting pass."), ECVF_Scalability | ECVF_RenderThreadSafe ); static TAutoConsoleVariable CVarLumenDirectLightingCullToTileDepthRange( TEXT("r.LumenScene.DirectLighting.CullToTileDepthRange"), 1, TEXT("Whether to calculate each Card Tile's depth range and use it for tighter light culling."), ECVF_Scalability | ECVF_RenderThreadSafe ); float GOffscreenShadowingTraceStepFactor = 5; FAutoConsoleVariableRef CVarOffscreenShadowingTraceStepFactor( TEXT("r.LumenScene.DirectLighting.OffscreenShadowingTraceStepFactor"), GOffscreenShadowingTraceStepFactor, TEXT(""), ECVF_Scalability | ECVF_RenderThreadSafe ); int32 GLumenDirectLightingCloudTransmittance = 1; FAutoConsoleVariableRef CVarLumenDirectLightingCloudTransmittance( TEXT("r.LumenScene.DirectLighting.CloudTransmittance"), GLumenDirectLightingCloudTransmittance, TEXT("Whether to sample cloud shadows when avaible."), ECVF_Scalability | ECVF_RenderThreadSafe ); static TAutoConsoleVariable CVarLumenDirectLightingMeshSDFShadowRayBias( TEXT("r.LumenScene.DirectLighting.MeshSDF.ShadowRayBias"), 2.0f, TEXT("Bias for tracing mesh SDF shadow rays."), ECVF_Scalability | ECVF_RenderThreadSafe ); static TAutoConsoleVariable CVarLumenDirectLightingHeightfieldShadowRayBias( TEXT("r.LumenScene.DirectLighting.Heightfield.ShadowRayBias"), 2.0f, TEXT("Bias for tracing heightfield shadow rays."), ECVF_Scalability | ECVF_RenderThreadSafe ); static TAutoConsoleVariable CVarLumenDirectLightingGlobalSDFShadowRayBias( TEXT("r.LumenScene.DirectLighting.GlobalSDF.ShadowRayBias"), 1.0f, TEXT("Bias for tracing global SDF shadow rays."), ECVF_Scalability | ECVF_RenderThreadSafe ); static TAutoConsoleVariable CVarLumenDirectLightingHardwareRayTracingShadowRayBias( TEXT("r.LumenScene.DirectLighting.HardwareRayTracing.ShadowRayBias"), 1.0f, TEXT("Bias for hardware ray tracing shadow rays."), ECVF_Scalability | ECVF_RenderThreadSafe ); static TAutoConsoleVariable CVarLumenDirectLightingHWRTAdaptiveShadowTracing( TEXT("r.LumenScene.DirectLighting.HardwareRayTracing.AdaptiveShadowTracing"), 1, TEXT("Whether to allow shooting fewer shadow rays for light tiles that were uniformly shadowed in the last lighting update."), ECVF_Scalability | ECVF_RenderThreadSafe ); static TAutoConsoleVariable CVarLumenDirectLightingBatchShadows( TEXT("r.LumenScene.DirectLighting.BatchShadows"), 2, TEXT("Whether to enable batching lumen light shadow passes. This cvar mainly exists for debugging."), ECVF_RenderThreadSafe); uint32 GetLumenLightingStatMode(); float LumenSceneDirectLighting::GetMeshSDFShadowRayBias() { return FMath::Max(CVarLumenDirectLightingMeshSDFShadowRayBias.GetValueOnRenderThread(), 0.0f); } float LumenSceneDirectLighting::GetHeightfieldShadowRayBias() { return FMath::Max(CVarLumenDirectLightingHeightfieldShadowRayBias.GetValueOnRenderThread(), 0.0f); } float LumenSceneDirectLighting::GetGlobalSDFShadowRayBias() { return FMath::Max(CVarLumenDirectLightingGlobalSDFShadowRayBias.GetValueOnRenderThread(), 0.0f); } float LumenSceneDirectLighting::GetHardwareRayTracingShadowRayBias() { return FMath::Max(CVarLumenDirectLightingHardwareRayTracingShadowRayBias.GetValueOnRenderThread(), 0.0f); } bool LumenSceneDirectLighting::UseLightTilesPerLightType() { return CVarLumenDirectLightingBatchShadows.GetValueOnRenderThread() == 2; } EPixelFormat Lumen::GetDirectLightingAtlasFormat() { return Lumen::GetLightingDataFormat(); } EPixelFormat Lumen::GetIndirectLightingAtlasFormat() { return Lumen::GetLightingDataFormat(); } class FLumenGatheredLight { public: FLumenGatheredLight( const FScene* Scene, TConstArrayView Views, const FLumenSceneFrameTemporaries& FrameTemporaries, const FLightSceneInfo* InLightSceneInfo, uint32 InLightIndex) { LightIndex = InLightIndex; LightSceneInfo = InLightSceneInfo; bHasShadows = InLightSceneInfo->Proxy->CastsDynamicShadow(); const FViewInfo& View = Views[0]; const FLightSceneProxy* Proxy = LightSceneInfo->Proxy; Type = ELumenLightType::MAX; const ELightComponentType LightType = (ELightComponentType)Proxy->GetLightType(); switch (LightType) { case LightType_Directional: Type = ELumenLightType::Directional; break; case LightType_Point: Type = ELumenLightType::Point; break; case LightType_Spot: Type = ELumenLightType::Spot; break; case LightType_Rect: Type = ELumenLightType::Rect; break; } if (Type == ELumenLightType::Directional) { bMayCastCloudTransmittance = LightMayCastCloudShadow(Scene, View, LightSceneInfo); } LightFunctionMaterialProxy = Proxy->GetLightFunctionMaterial(); if (LightFunctionMaterialProxy && (!View.Family->EngineShowFlags.LightFunctions || !LightFunctionMaterialProxy->GetIncompleteMaterialWithFallback(Scene->GetFeatureLevel()).IsLightFunction())) { LightFunctionMaterialProxy = nullptr; } const bool bBatchableLightFunction = LightFunctionMaterialProxy == nullptr || (LightFunctionAtlas::IsEnabled(View, ELightFunctionAtlasSystem::Lumen) && LightSceneInfo->Proxy->HasValidLightFunctionAtlasSlot()); FSceneRenderer::GetLightNameForDrawEvent(Proxy, Name); bNeedsShadowMask = bHasShadows || bMayCastCloudTransmittance || LightFunctionMaterialProxy; // If evaluates to false, the light may still be eligible for batching during a raytraced shadow pass. // The assumption is that such lights are not common so we are not optimizing for them. bBatchedShadowsEligible = !bMayCastCloudTransmittance && bBatchableLightFunction && Type != ELumenLightType::Directional; // Non-raytraced and distance field shadows require the light uniform buffer struct for each view but // only for standalone lights if we do a single dispatch per light type. if (NeedsShadowMask() && (!LumenSceneDirectLighting::UseLightTilesPerLightType() || !CanUseBatchedShadows())) { int32 NumViewOrigins = FrameTemporaries.ViewOrigins.Num(); DeferredLightUniformBuffers.SetNum(NumViewOrigins); for (int32 OriginIndex = 0; OriginIndex < NumViewOrigins; ++OriginIndex) { FDeferredLightUniformStruct DeferredLightUniforms = GetDeferredLightParameters(*FrameTemporaries.ViewOrigins[OriginIndex].ReferenceView, *LightSceneInfo); if (LightSceneInfo->Proxy->IsInverseSquared()) { DeferredLightUniforms.LightParameters.FalloffExponent = 0; } DeferredLightUniforms.LightParameters.Color *= LightSceneInfo->Proxy->GetIndirectLightingScale(); DeferredLightUniformBuffers[OriginIndex] = CreateUniformBufferImmediate(DeferredLightUniforms, UniformBuffer_SingleFrame); } } } bool NeedsShadowMask() const { return bNeedsShadowMask; } bool CanUseBatchedShadows() const { return bBatchedShadowsEligible; } const FLightSceneInfo* LightSceneInfo = nullptr; const FMaterialRenderProxy* LightFunctionMaterialProxy = nullptr; uint32 LightIndex = 0; // Index in the GatheredLights array ELumenLightType Type = ELumenLightType::MAX; bool bHasShadows = false; bool bMayCastCloudTransmittance = false; bool bNeedsShadowMask = false; bool bBatchedShadowsEligible = false; FString Name; TArray, TInlineAllocator<4>> DeferredLightUniformBuffers; }; BEGIN_SHADER_PARAMETER_STRUCT(FLumenLightTileScatterParameters, ) RDG_BUFFER_ACCESS(DrawIndirectArgs, ERHIAccess::IndirectArgs) RDG_BUFFER_ACCESS(DispatchIndirectArgs, ERHIAccess::IndirectArgs) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, LightTileAllocator) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, LightTiles) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, LightTileOffsetsPerLight) SHADER_PARAMETER(int32, bUseLightTilesPerLightType) END_SHADER_PARAMETER_STRUCT() class FSpliceCardPagesIntoTilesCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FSpliceCardPagesIntoTilesCS); SHADER_USE_PARAMETER_STRUCT(FSpliceCardPagesIntoTilesCS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) RDG_BUFFER_ACCESS(IndirectArgBuffer, ERHIAccess::IndirectArgs) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FLumenCardScene, LumenCardScene) SHADER_PARAMETER_STRUCT_INCLUDE(LumenSceneDirectLighting::FLightDataParameters, LumenLightData) SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWCardTileAllocator) SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWCardTiles) SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWLightTileAllocatorPerLight) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, CardPageIndexAllocator) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, CardPageIndexData) SHADER_PARAMETER(uint32, MaxLightsPerTile) SHADER_PARAMETER(uint32, NumLights) END_SHADER_PARAMETER_STRUCT() static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return DoesPlatformSupportLumenGI(Parameters.Platform); } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE"), GetGroupSize()); } static int32 GetGroupSize() { return 8; } }; IMPLEMENT_GLOBAL_SHADER(FSpliceCardPagesIntoTilesCS, "/Engine/Private/Lumen/LumenSceneDirectLightingCulling.usf", "SpliceCardPagesIntoTilesCS", SF_Compute); class FInitializeCardTileIndirectArgsCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FInitializeCardTileIndirectArgsCS) SHADER_USE_PARAMETER_STRUCT(FInitializeCardTileIndirectArgsCS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, RWDispatchCardTilesIndirectArgs) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, CardTileAllocator) END_SHADER_PARAMETER_STRUCT() static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return DoesPlatformSupportLumenGI(Parameters.Platform); } static uint32 GetGroupSize() { return 64; } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE"), GetGroupSize()); } }; IMPLEMENT_GLOBAL_SHADER(FInitializeCardTileIndirectArgsCS, "/Engine/Private/Lumen/LumenSceneDirectLightingCulling.usf", "InitializeCardTileIndirectArgsCS", SF_Compute) void Lumen::SpliceCardPagesIntoTiles( FRDGBuilder& GraphBuilder, const FGlobalShaderMap* GlobalShaderMap, const FLumenCardUpdateContext& CardUpdateContext, const TRDGUniformBufferRef& LumenCardSceneUniformBuffer, FLumenCardTileUpdateContext& OutCardTileUpdateContext, ERDGPassFlags ComputePassFlags) { const uint32 MaxLightTilesTilesX = FMath::DivideAndRoundUp(CardUpdateContext.UpdateAtlasSize.X, Lumen::CardTileSize); const uint32 MaxLightTilesTilesY = FMath::DivideAndRoundUp(CardUpdateContext.UpdateAtlasSize.Y, Lumen::CardTileSize); const uint32 MaxLightTiles = MaxLightTilesTilesX * MaxLightTilesTilesY; FRDGBufferRef CardTileAllocator = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32), 1), TEXT("Lumen.CardTileAllocator")); FRDGBufferRef CardTiles = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32), MaxLightTiles), TEXT("Lumen.CardTiles")); AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(CardTileAllocator), 0, ComputePassFlags); // Splice card pages into card tiles { FSpliceCardPagesIntoTilesCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->IndirectArgBuffer = CardUpdateContext.DispatchCardPageIndicesIndirectArgs; PassParameters->LumenCardScene = LumenCardSceneUniformBuffer; PassParameters->RWCardTileAllocator = GraphBuilder.CreateUAV(CardTileAllocator); PassParameters->RWCardTiles = GraphBuilder.CreateUAV(CardTiles); PassParameters->CardPageIndexAllocator = GraphBuilder.CreateSRV(CardUpdateContext.CardPageIndexAllocator); PassParameters->CardPageIndexData = GraphBuilder.CreateSRV(CardUpdateContext.CardPageIndexData); auto ComputeShader = GlobalShaderMap->GetShader(); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("SpliceCardPagesIntoTiles"), ComputePassFlags, ComputeShader, PassParameters, CardUpdateContext.DispatchCardPageIndicesIndirectArgs, FLumenCardUpdateContext::EIndirectArgOffset::ThreadPerTile); } // Setup indirect args for card tile processing FRDGBufferRef DispatchCardTilesIndirectArgs = GraphBuilder.CreateBuffer( FRDGBufferDesc::CreateIndirectDesc((uint32)ELumenDispatchCardTilesIndirectArgsOffset::Num), TEXT("Lumen.DispatchCardTilesIndirectArgs")); { FInitializeCardTileIndirectArgsCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->RWDispatchCardTilesIndirectArgs = GraphBuilder.CreateUAV(DispatchCardTilesIndirectArgs); PassParameters->CardTileAllocator = GraphBuilder.CreateSRV(CardTileAllocator); auto ComputeShader = GlobalShaderMap->GetShader(); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("InitializeCardTileIndirectArgs"), ComputePassFlags, ComputeShader, PassParameters, FIntVector(1, 1, 1)); } OutCardTileUpdateContext.CardTileAllocator = CardTileAllocator; OutCardTileUpdateContext.CardTiles = CardTiles; OutCardTileUpdateContext.DispatchCardTilesIndirectArgs = DispatchCardTilesIndirectArgs; } class FCalculateCardTileDepthRangesCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FCalculateCardTileDepthRangesCS); SHADER_USE_PARAMETER_STRUCT(FCalculateCardTileDepthRangesCS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) RDG_BUFFER_ACCESS(IndirectArgBuffer, ERHIAccess::IndirectArgs) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FLumenCardScene, LumenCardScene) SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWCardTileDepthRanges) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, CardTileAllocator) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, CardTiles) END_SHADER_PARAMETER_STRUCT() using FPermutationDomain = TShaderPermutationDomain<>; static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return DoesPlatformSupportLumenGI(Parameters.Platform); } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE"), GetGroupSize()); } static int32 GetGroupSize() { return Lumen::CardTileSize; } }; IMPLEMENT_GLOBAL_SHADER(FCalculateCardTileDepthRangesCS, "/Engine/Private/Lumen/LumenSceneDirectLightingCulling.usf", "CalculateCardTileDepthRangesCS", SF_Compute); class FBuildLightTilesCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FBuildLightTilesCS); SHADER_USE_PARAMETER_STRUCT(FBuildLightTilesCS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) RDG_BUFFER_ACCESS(IndirectArgBuffer, ERHIAccess::IndirectArgs) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FLumenCardScene, LumenCardScene) SHADER_PARAMETER_STRUCT_INCLUDE(LumenSceneDirectLighting::FLightDataParameters, LumenLightData) SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWLightTileAllocator) SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWLightTileAllocatorForPerCardTileDispatch) SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWLightTiles) SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWLightTileAllocatorPerLight) SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWLightTileOffsetNumPerCardTile) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, CardTileAllocator) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, CardTiles) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, CardTileDepthRanges) SHADER_PARAMETER(uint32, CullToCardTileDepthRange) SHADER_PARAMETER(uint32, MaxLightsPerTile) SHADER_PARAMETER(uint32, NumLights) SHADER_PARAMETER(uint32, NumViews) SHADER_PARAMETER_ARRAY(FMatrix44f, FrustumTranslatedWorldToClip, [LUMEN_MAX_VIEWS]) SHADER_PARAMETER_ARRAY(FVector4f, PreViewTranslationHigh, [LUMEN_MAX_VIEWS]) SHADER_PARAMETER_ARRAY(FVector4f, PreViewTranslationLow, [LUMEN_MAX_VIEWS]) SHADER_PARAMETER(FVector2f, ViewExposure) SHADER_PARAMETER(int32, bUseLightTilesPerLightType) END_SHADER_PARAMETER_STRUCT() class FMaxLightSamples : SHADER_PERMUTATION_SPARSE_INT("MAX_LIGHT_SAMPLES", 1, 2, 4, 8, 16, 32); using FPermutationDomain = TShaderPermutationDomain; static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return DoesPlatformSupportLumenGI(Parameters.Platform); } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE"), GetGroupSize()); OutEnvironment.CompilerFlags.Add(CFLAG_Wave32); } static int32 GetGroupSize() { return 64; } }; IMPLEMENT_GLOBAL_SHADER(FBuildLightTilesCS, "/Engine/Private/Lumen/LumenSceneDirectLightingCulling.usf", "BuildLightTilesCS", SF_Compute); class FComputeLightTileOffsetsPerLightCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FComputeLightTileOffsetsPerLightCS) SHADER_USE_PARAMETER_STRUCT(FComputeLightTileOffsetsPerLightCS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWLightTileOffsetsPerLight) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, LightTileAllocatorPerLight) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, StandaloneLightIndices) SHADER_PARAMETER(uint32, NumLights) SHADER_PARAMETER(uint32, NumViews) SHADER_PARAMETER(uint32, NumStandaloneLights) END_SHADER_PARAMETER_STRUCT() class FUseStandaloneLightIndices : SHADER_PERMUTATION_BOOL("USE_STANDALONE_LIGHT_INDICES"); using FPermutationDomain = TShaderPermutationDomain; static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return DoesPlatformSupportLumenGI(Parameters.Platform); } static uint32 GetGroupSize() { return 64; } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE"), GetGroupSize()); } }; IMPLEMENT_GLOBAL_SHADER(FComputeLightTileOffsetsPerLightCS, "/Engine/Private/Lumen/LumenSceneDirectLightingCulling.usf", "ComputeLightTileOffsetsPerLightCS", SF_Compute); class FCompactLightTilesCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FCompactLightTilesCS); SHADER_USE_PARAMETER_STRUCT(FCompactLightTilesCS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) RDG_BUFFER_ACCESS(IndirectArgBuffer, ERHIAccess::IndirectArgs) SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWCompactedLightTiles) SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWLightTilesPerCardTile) SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWCompactedLightTileAllocatorPerLight) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, LightTileAllocator) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, LightTiles) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, LightTileOffsetsPerLight) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, CardTiles) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, LightTileOffsetNumPerCardTile) SHADER_PARAMETER(uint32, NumLights) SHADER_PARAMETER(uint32, NumViews) SHADER_PARAMETER(int32, bUseLightTilesPerLightType) END_SHADER_PARAMETER_STRUCT() static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return DoesPlatformSupportLumenGI(Parameters.Platform); } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE"), GetGroupSize()); } static int32 GetGroupSize() { return 64; } }; IMPLEMENT_GLOBAL_SHADER(FCompactLightTilesCS, "/Engine/Private/Lumen/LumenSceneDirectLightingCulling.usf", "CompactLightTilesCS", SF_Compute); class FInitializeLightTileIndirectArgsCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FInitializeLightTileIndirectArgsCS) SHADER_USE_PARAMETER_STRUCT(FInitializeLightTileIndirectArgsCS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, RWDispatchLightTilesIndirectArgs) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, RWDrawTilesPerLightIndirectArgs) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, RWDispatchTilesPerLightIndirectArgs) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, LightTileAllocator) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, LightTileAllocatorPerLight) SHADER_PARAMETER(uint32, VertexCountPerInstanceIndirect) SHADER_PARAMETER(uint32, PerLightDispatchFactor) SHADER_PARAMETER(uint32, NumLights) SHADER_PARAMETER(uint32, NumViews) END_SHADER_PARAMETER_STRUCT() static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return DoesPlatformSupportLumenGI(Parameters.Platform); } static uint32 GetGroupSize() { return 64; } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE"), GetGroupSize()); } }; IMPLEMENT_GLOBAL_SHADER(FInitializeLightTileIndirectArgsCS, "/Engine/Private/Lumen/LumenSceneDirectLightingCulling.usf", "InitializeLightTileIndirectArgsCS", SF_Compute) BEGIN_SHADER_PARAMETER_STRUCT(FClearLumenCardsParameters, ) SHADER_PARAMETER_STRUCT_INCLUDE(FRasterizeToCardsVS::FParameters, VS) SHADER_PARAMETER_STRUCT_INCLUDE(FClearLumenCardsPS::FParameters, PS) RENDER_TARGET_BINDING_SLOTS() END_SHADER_PARAMETER_STRUCT() void ClearLumenSceneDirectLighting( const FViewInfo& View, FRDGBuilder& GraphBuilder, const FLumenSceneData& LumenSceneData, const FLumenSceneFrameTemporaries& FrameTemporaries, FLumenCardUpdateContext CardUpdateContext) { FClearLumenCardsParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->RenderTargets[0] = FRenderTargetBinding(FrameTemporaries.DirectLightingAtlas, ERenderTargetLoadAction::ELoad); PassParameters->VS.LumenCardScene = FrameTemporaries.LumenCardSceneUniformBuffer; PassParameters->VS.DrawIndirectArgs = CardUpdateContext.DrawCardPageIndicesIndirectArgs; PassParameters->VS.CardPageIndexAllocator = GraphBuilder.CreateSRV(CardUpdateContext.CardPageIndexAllocator); PassParameters->VS.CardPageIndexData = GraphBuilder.CreateSRV(CardUpdateContext.CardPageIndexData); PassParameters->VS.IndirectLightingAtlasSize = LumenSceneData.GetRadiosityAtlasSize(); PassParameters->PS.View = View.ViewUniformBuffer; PassParameters->PS.LumenCardScene = FrameTemporaries.LumenCardSceneUniformBuffer; GraphBuilder.AddPass( RDG_EVENT_NAME("ClearDirectLighting"), PassParameters, ERDGPassFlags::Raster, [ViewportSize = LumenSceneData.GetPhysicalAtlasSize(), PassParameters, GlobalShaderMap = View.ShaderMap](FRDGAsyncTask, FRHICommandList& RHICmdList) { FClearLumenCardsPS::FPermutationDomain PermutationVector; PermutationVector.Set(1); auto PixelShader = GlobalShaderMap->GetShader(PermutationVector); auto VertexShader = GlobalShaderMap->GetShader(); DrawQuadsToAtlas( ViewportSize, VertexShader, PixelShader, PassParameters, GlobalShaderMap, TStaticBlendState<>::GetRHI(), RHICmdList, [](FRHICommandList& RHICmdList, TShaderRefBase Shader, FRHIPixelShader* ShaderRHI, const typename FClearLumenCardsPS::FParameters& Parameters) {}, PassParameters->VS.DrawIndirectArgs, 0); }); } class FLumenCardBatchDirectLightingCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FLumenCardBatchDirectLightingCS) SHADER_USE_PARAMETER_STRUCT(FLumenCardBatchDirectLightingCS, FGlobalShader) BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) RDG_BUFFER_ACCESS(IndirectArgBuffer, ERHIAccess::IndirectArgs) // This shader isn't view specific but the RectLightAtlasTexture, though doesn't vary per view, is accessed through the view uniform buffer SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FLumenCardScene, LumenCardScene) SHADER_PARAMETER_STRUCT_INCLUDE(LumenSceneDirectLighting::FLightDataParameters, LumenLightData) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, ShadowMaskTiles) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, CardTiles) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, LightTileOffsetNumPerCardTile) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, LightTilesPerCardTile) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, RWDirectLightingAtlas) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, RWTileShadowDownsampleFactorAtlas) SHADER_PARAMETER_ARRAY(FVector4f, PreViewTranslationHigh, [LUMEN_MAX_VIEWS]) SHADER_PARAMETER_ARRAY(FVector4f, PreViewTranslationLow, [LUMEN_MAX_VIEWS]) SHADER_PARAMETER(FVector2f, ViewExposure) SHADER_PARAMETER(FVector3f, TargetFormatQuantizationError) SHADER_PARAMETER(float, CachedLightingPreExposure) END_SHADER_PARAMETER_STRUCT() class FMultiView : SHADER_PERMUTATION_BOOL("HAS_MULTIPLE_VIEWS"); class FHasRectLights : SHADER_PERMUTATION_BOOL("HAS_RECT_LIGHTS"); class FWaveOpWaveSize : SHADER_PERMUTATION_SPARSE_INT("WAVE_OP_WAVE_SIZE", 0, 64); // TODO: wave32 support using FPermutationDomain = TShaderPermutationDomain; static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { FPermutationDomain PermutationVector(Parameters.PermutationId); if (!UE::ShaderPermutationUtils::ShouldCompileWithWaveSize(Parameters, PermutationVector.Get())) { return false; } return DoesPlatformSupportLumenGI(Parameters.Platform); } static EShaderPermutationPrecacheRequest ShouldPrecachePermutation(const FShaderPermutationParameters& Parameters) { FPermutationDomain PermutationVector(Parameters.PermutationId); if (!UE::ShaderPermutationUtils::ShouldPrecacheWithWaveSize(Parameters, PermutationVector.Get())) { return EShaderPermutationPrecacheRequest::NotUsed; } return FGlobalShader::ShouldPrecachePermutation(Parameters); } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("USE_LIGHT_UNIFORM_BUFFER"), 0); FPermutationDomain PermutationVector(Parameters.PermutationId); if (PermutationVector.Get() > 0) { OutEnvironment.CompilerFlags.Add(CFLAG_WaveOperations); } } }; IMPLEMENT_GLOBAL_SHADER(FLumenCardBatchDirectLightingCS, "/Engine/Private/Lumen/LumenSceneDirectLighting.usf", "LumenCardBatchDirectLightingCS", SF_Compute); BEGIN_SHADER_PARAMETER_STRUCT(FPerLightParameters, ) SHADER_PARAMETER(uint32, LightIndex) SHADER_PARAMETER(float, TanLightSourceAngle) SHADER_PARAMETER_STRUCT_REF(FDeferredLightUniformStruct, DeferredLightUniforms) END_SHADER_PARAMETER_STRUCT() BEGIN_SHADER_PARAMETER_STRUCT(FLumenDirectLightingNonRayTracedShadowsParameters, ) RDG_BUFFER_ACCESS(IndirectArgBuffer, ERHIAccess::IndirectArgs) SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWShadowMaskTiles) SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWShadowTraceAllocator) SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWShadowTraces) SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer, TileShadowDownsampleFactorAtlas) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FLumenCardScene, LumenCardScene) SHADER_PARAMETER_STRUCT_INCLUDE(FPerLightParameters, LightParameters) SHADER_PARAMETER_STRUCT_INCLUDE(FLumenLightTileScatterParameters, LightTileScatterParameters) SHADER_PARAMETER_STRUCT_INCLUDE(LumenSceneDirectLighting::FLightDataParameters, LumenLightData) SHADER_PARAMETER(uint32, CardScatterInstanceIndex) SHADER_PARAMETER(uint32, ViewIndex) SHADER_PARAMETER(uint32, NumViews) SHADER_PARAMETER(uint32, DummyZeroForFixingShaderCompilerBug) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FForwardLightUniformParameters, ForwardLightStruct) SHADER_PARAMETER_STRUCT_INCLUDE(FLightCloudTransmittanceParameters, LightCloudTransmittanceParameters) SHADER_PARAMETER(float, HeightfieldShadowReceiverBias) SHADER_PARAMETER(float, StepFactor) SHADER_PARAMETER(float, MaxTraceDistance) SHADER_PARAMETER(int32, bAdaptiveShadowTracing) END_SHADER_PARAMETER_STRUCT() class FLumenDirectLightingShadowMaskFromLightAttenuationCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FLumenDirectLightingShadowMaskFromLightAttenuationCS) SHADER_USE_PARAMETER_STRUCT(FLumenDirectLightingShadowMaskFromLightAttenuationCS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_INCLUDE(FLumenDirectLightingNonRayTracedShadowsParameters, Common) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FLightFunctionAtlasGlobalParameters, LightFunctionAtlas) END_SHADER_PARAMETER_STRUCT() class FThreadGroupSize32 : SHADER_PERMUTATION_BOOL("THREADGROUP_SIZE_32"); class FCompactShadowTraces : SHADER_PERMUTATION_BOOL("COMPACT_SHADOW_TRACES"); class FLightType : SHADER_PERMUTATION_ENUM_CLASS("LIGHT_TYPE", ELumenLightType); class FCloudTransmittance : SHADER_PERMUTATION_BOOL("USE_CLOUD_TRANSMITTANCE"); class FLightFunctionAtlas : SHADER_PERMUTATION_BOOL("USE_LIGHT_FUNCTION_ATLAS"); using FPermutationDomain = TShaderPermutationDomain; static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { FPermutationDomain PermutationVector(Parameters.PermutationId); if (PermutationVector.Get() && PermutationVector.Get() != ELumenLightType::Directional) { return false; } return DoesPlatformSupportLumenGI(Parameters.Platform); } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.CompilerFlags.Add(CFLAG_Wave32); OutEnvironment.SetDefine(TEXT("LIGHT_FUNCTION"), 0); OutEnvironment.SetDefine(TEXT("USE_IES_PROFILE"), 1); OutEnvironment.SetDefine(TEXT("SUBSTRATE_INLINE_SHADING"), 1); } }; IMPLEMENT_GLOBAL_SHADER(FLumenDirectLightingShadowMaskFromLightAttenuationCS, "/Engine/Private/Lumen/LumenSceneDirectLightingShadowMask.usf", "LumenSceneDirectLightingShadowMaskFromLightAttenuationCS", SF_Compute); BEGIN_SHADER_PARAMETER_STRUCT(FLightFunctionParameters, ) SHADER_PARAMETER_STRUCT_REF(FPrimitiveUniformShaderParameters, PrimitiveUniformBuffer) SHADER_PARAMETER(FVector4f, LightFunctionParameters) SHADER_PARAMETER(FMatrix44f, LightFunctionTranslatedWorldToLight) SHADER_PARAMETER(FVector3f, LightFunctionParameters2) SHADER_PARAMETER(FVector3f, CameraRelativeLightPosition) END_SHADER_PARAMETER_STRUCT() class FLumenDirectLightingShadowMaskFromLightAttenuationWithLightFunctionCS : public FMaterialShader { DECLARE_SHADER_TYPE(FLumenDirectLightingShadowMaskFromLightAttenuationWithLightFunctionCS, Material); FLumenDirectLightingShadowMaskFromLightAttenuationWithLightFunctionCS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) : FMaterialShader(Initializer) { Bindings.BindForLegacyShaderParameters( this, Initializer.PermutationId, Initializer.ParameterMap, *FParameters::FTypeInfo::GetStructMetadata(), // Don't require full bindings, we use FMaterialShader::SetParameters false); } FLumenDirectLightingShadowMaskFromLightAttenuationWithLightFunctionCS() {} BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_INCLUDE(FLumenDirectLightingNonRayTracedShadowsParameters, Common) SHADER_PARAMETER_STRUCT_INCLUDE(FLightFunctionParameters, LightFunctionParameters) END_SHADER_PARAMETER_STRUCT() class FThreadGroupSize32 : SHADER_PERMUTATION_BOOL("THREADGROUP_SIZE_32"); class FCompactShadowTraces : SHADER_PERMUTATION_BOOL("COMPACT_SHADOW_TRACES"); class FLightType : SHADER_PERMUTATION_ENUM_CLASS("LIGHT_TYPE", ELumenLightType); class FCloudTransmittance : SHADER_PERMUTATION_BOOL("USE_CLOUD_TRANSMITTANCE"); using FPermutationDomain = TShaderPermutationDomain; static bool ShouldCompilePermutation(const FMaterialShaderPermutationParameters& Parameters) { FPermutationDomain PermutationVector(Parameters.PermutationId); if (PermutationVector.Get() && PermutationVector.Get() != ELumenLightType::Directional) { return false; } return Parameters.MaterialParameters.MaterialDomain == EMaterialDomain::MD_LightFunction && DoesPlatformSupportLumenGI(Parameters.Platform); } static void ModifyCompilationEnvironment(const FMaterialShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FMaterialShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.CompilerFlags.Add(CFLAG_Wave32); OutEnvironment.SetDefine(TEXT("LIGHT_FUNCTION"), 1); OutEnvironment.SetDefine(TEXT("USE_IES_PROFILE"), 1); OutEnvironment.SetDefine(TEXT("SUBSTRATE_INLINE_SHADING"), 1); } }; IMPLEMENT_MATERIAL_SHADER_TYPE(, FLumenDirectLightingShadowMaskFromLightAttenuationWithLightFunctionCS, TEXT("/Engine/Private/Lumen/LumenSceneDirectLightingShadowMask.usf"), TEXT("LumenSceneDirectLightingShadowMaskFromLightAttenuationCS"), SF_Compute); class FInitShadowTraceIndirectArgsCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FInitShadowTraceIndirectArgsCS) SHADER_USE_PARAMETER_STRUCT(FInitShadowTraceIndirectArgsCS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, RWShadowTraceIndirectArgs) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, ShadowTraceAllocator) END_SHADER_PARAMETER_STRUCT() static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return DoesPlatformSupportLumenGI(Parameters.Platform); } static uint32 GetGroupSize() { return 64; } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE"), GetGroupSize()); } }; IMPLEMENT_GLOBAL_SHADER(FInitShadowTraceIndirectArgsCS, "/Engine/Private/Lumen/LumenSceneDirectLightingSoftwareRayTracing.usf", "InitShadowTraceIndirectArgsCS", SF_Compute) class FLumenSceneDirectLightingTraceDistanceFieldShadowsCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FLumenSceneDirectLightingTraceDistanceFieldShadowsCS) SHADER_USE_PARAMETER_STRUCT(FLumenSceneDirectLightingTraceDistanceFieldShadowsCS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) RDG_BUFFER_ACCESS(IndirectArgBuffer, ERHIAccess::IndirectArgs) SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWShadowMaskTiles) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FLumenCardScene, LumenCardScene) SHADER_PARAMETER_STRUCT_INCLUDE(FPerLightParameters, LightParameters) SHADER_PARAMETER_STRUCT_INCLUDE(FLumenLightTileScatterParameters, LightTileScatterParameters) SHADER_PARAMETER_STRUCT_INCLUDE(LumenSceneDirectLighting::FLightDataParameters, LumenLightData) SHADER_PARAMETER(uint32, ViewIndex) SHADER_PARAMETER(uint32, NumViews) SHADER_PARAMETER(uint32, DummyZeroForFixingShaderCompilerBug) SHADER_PARAMETER_STRUCT_INCLUDE(FDistanceFieldObjectBufferParameters, ObjectBufferParameters) SHADER_PARAMETER_STRUCT_INCLUDE(FLightTileIntersectionParameters, LightTileIntersectionParameters) SHADER_PARAMETER_STRUCT_INCLUDE(FDistanceFieldAtlasParameters, DistanceFieldAtlasParameters) SHADER_PARAMETER(FMatrix44f, TranslatedWorldToShadow) SHADER_PARAMETER(float, TwoSidedMeshDistanceBiasScale) SHADER_PARAMETER(float, StepFactor) SHADER_PARAMETER(float, MaxTraceDistance) SHADER_PARAMETER(float, MeshSDFShadowRayBias) SHADER_PARAMETER(float, HeightfieldShadowRayBias) SHADER_PARAMETER(float, GlobalSDFShadowRayBias) SHADER_PARAMETER(int32, HeightfieldMaxTracingSteps) END_SHADER_PARAMETER_STRUCT() class FThreadGroupSize32 : SHADER_PERMUTATION_BOOL("THREADGROUP_SIZE_32"); class FLightType : SHADER_PERMUTATION_ENUM_CLASS("LIGHT_TYPE", ELumenLightType); class FTraceGlobalSDF : SHADER_PERMUTATION_BOOL("OFFSCREEN_SHADOWING_TRACE_GLOBAL_SDF"); class FSimpleCoverageBasedExpand : SHADER_PERMUTATION_BOOL("GLOBALSDF_SIMPLE_COVERAGE_BASED_EXPAND"); class FTraceMeshSDFs : SHADER_PERMUTATION_BOOL("OFFSCREEN_SHADOWING_TRACE_MESH_SDF"); class FTraceHeightfields : SHADER_PERMUTATION_BOOL("OFFSCREEN_SHADOWING_TRACE_HEIGHTFIELDS"); class FOffsetDataStructure : SHADER_PERMUTATION_INT("OFFSET_DATA_STRUCT", 3); using FPermutationDomain = TShaderPermutationDomain; static FPermutationDomain RemapPermutation(FPermutationDomain PermutationVector) { // Only directional lights support mesh SDF offscreen shadowing if (PermutationVector.Get() != ELumenLightType::Directional) { PermutationVector.Set(false); PermutationVector.Set(false); } // Don't trace global SDF if per mesh object traces are enabled if (PermutationVector.Get() || PermutationVector.Get()) { PermutationVector.Set(false); } // FOffsetDataStructure is only used for mesh SDFs if (!PermutationVector.Get()) { PermutationVector.Set(0); } if (!PermutationVector.Get()) { PermutationVector.Set(false); } return PermutationVector; } static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { FPermutationDomain PermutationVector(Parameters.PermutationId); if (RemapPermutation(PermutationVector) != PermutationVector) { return false; } return DoesPlatformSupportLumenGI(Parameters.Platform); } FORCENOINLINE static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.CompilerFlags.Add(CFLAG_Wave32); } }; IMPLEMENT_GLOBAL_SHADER(FLumenSceneDirectLightingTraceDistanceFieldShadowsCS, "/Engine/Private/Lumen/LumenSceneDirectLightingSoftwareRayTracing.usf", "LumenSceneDirectLightingTraceDistanceFieldShadowsCS", SF_Compute); void SetupLightFunctionParameters(const FViewInfo& View, const FLightSceneInfo* LightSceneInfo, float ShadowFadeFraction, FLightFunctionParameters& OutParameters) { const bool bIsSpotLight = LightSceneInfo->Proxy->GetLightType() == LightType_Spot; const bool bIsPointLight = LightSceneInfo->Proxy->GetLightType() == LightType_Point; const float TanOuterAngle = bIsSpotLight ? FMath::Tan(LightSceneInfo->Proxy->GetOuterConeAngle()) : 1.0f; OutParameters.LightFunctionParameters = FVector4f(TanOuterAngle, ShadowFadeFraction, bIsSpotLight ? 1.0f : 0.0f, bIsPointLight ? 1.0f : 0.0f); const FVector Scale = LightSceneInfo->Proxy->GetLightFunctionScale(); // Switch x and z so that z of the user specified scale affects the distance along the light direction const FVector InverseScale = FVector( 1.f / Scale.Z, 1.f / Scale.Y, 1.f / Scale.X ); const FMatrix WorldToLight = LightSceneInfo->Proxy->GetWorldToLight() * FScaleMatrix(FVector(InverseScale)); OutParameters.LightFunctionTranslatedWorldToLight = FMatrix44f(FTranslationMatrix(-View.ViewMatrices.GetPreViewTranslation()) * WorldToLight); const float PreviewShadowsMask = 0.0f; OutParameters.LightFunctionParameters2 = FVector3f( LightSceneInfo->Proxy->GetLightFunctionFadeDistance(), LightSceneInfo->Proxy->GetLightFunctionDisabledBrightness(), PreviewShadowsMask); OutParameters.CameraRelativeLightPosition = GetCamRelativeLightPosition(View.ViewMatrices, *LightSceneInfo); OutParameters.PrimitiveUniformBuffer = GIdentityPrimitiveUniformBuffer.GetUniformBufferRef(); } void SetupMeshSDFShadowInitializer( const FLightSceneInfo* LightSceneInfo, const FBox& LumenSceneBounds, FSphere& OutShadowBounds, FWholeSceneProjectedShadowInitializer& OutInitializer) { FSphere Bounds; { // Get the 8 corners of the cascade's camera frustum, in world space FVector CascadeFrustumVerts[8]; const FVector LumenSceneCenter = LumenSceneBounds.GetCenter(); const FVector LumenSceneExtent = LumenSceneBounds.GetExtent(); CascadeFrustumVerts[0] = LumenSceneCenter + FVector(LumenSceneExtent.X, LumenSceneExtent.Y, LumenSceneExtent.Z); CascadeFrustumVerts[1] = LumenSceneCenter + FVector(LumenSceneExtent.X, LumenSceneExtent.Y, -LumenSceneExtent.Z); CascadeFrustumVerts[2] = LumenSceneCenter + FVector(LumenSceneExtent.X, -LumenSceneExtent.Y, LumenSceneExtent.Z); CascadeFrustumVerts[3] = LumenSceneCenter + FVector(LumenSceneExtent.X, -LumenSceneExtent.Y, -LumenSceneExtent.Z); CascadeFrustumVerts[4] = LumenSceneCenter + FVector(-LumenSceneExtent.X, LumenSceneExtent.Y, LumenSceneExtent.Z); CascadeFrustumVerts[5] = LumenSceneCenter + FVector(-LumenSceneExtent.X, LumenSceneExtent.Y, -LumenSceneExtent.Z); CascadeFrustumVerts[6] = LumenSceneCenter + FVector(-LumenSceneExtent.X, -LumenSceneExtent.Y, LumenSceneExtent.Z); CascadeFrustumVerts[7] = LumenSceneCenter + FVector(-LumenSceneExtent.X, -LumenSceneExtent.Y, -LumenSceneExtent.Z); Bounds = FSphere(LumenSceneCenter, 0); for (int32 Index = 0; Index < 8; Index++) { Bounds.W = FMath::Max(Bounds.W, FVector::DistSquared(CascadeFrustumVerts[Index], Bounds.Center)); } Bounds.W = FMath::Max(FMath::Sqrt(Bounds.W), 1.0f); ComputeShadowCullingVolume(true, CascadeFrustumVerts, -LightSceneInfo->Proxy->GetDirection(), OutInitializer.CascadeSettings.ShadowBoundsAccurate, OutInitializer.CascadeSettings.NearFrustumPlane, OutInitializer.CascadeSettings.FarFrustumPlane); } OutInitializer.CascadeSettings.ShadowSplitIndex = 0; const float ShadowExtent = Bounds.W / FMath::Sqrt(3.0f); const FBoxSphereBounds SubjectBounds(Bounds.Center, FVector(ShadowExtent, ShadowExtent, ShadowExtent), Bounds.W); OutInitializer.PreShadowTranslation = -Bounds.Center; OutInitializer.WorldToLight = FInverseRotationMatrix(LightSceneInfo->Proxy->GetDirection().GetSafeNormal().Rotation()); OutInitializer.Scales = FVector2D(1.0f / Bounds.W, 1.0f / Bounds.W); OutInitializer.SubjectBounds = FBoxSphereBounds(FVector::ZeroVector, SubjectBounds.BoxExtent, SubjectBounds.SphereRadius); OutInitializer.WAxis = FVector4(0, 0, 0, 1); OutInitializer.MinLightW = FMath::Min(-0.5f * UE_OLD_WORLD_MAX, -SubjectBounds.SphereRadius); const float MaxLightW = SubjectBounds.SphereRadius; OutInitializer.MaxDistanceToCastInLightW = MaxLightW - OutInitializer.MinLightW; OutInitializer.bRayTracedDistanceField = true; OutInitializer.CascadeSettings.bFarShadowCascade = false; const float SplitNear = -Bounds.W; const float SplitFar = Bounds.W; OutInitializer.CascadeSettings.SplitFarFadeRegion = 0.0f; OutInitializer.CascadeSettings.SplitNearFadeRegion = 0.0f; OutInitializer.CascadeSettings.SplitFar = SplitFar; OutInitializer.CascadeSettings.SplitNear = SplitNear; OutInitializer.CascadeSettings.FadePlaneOffset = SplitFar; OutInitializer.CascadeSettings.FadePlaneLength = 0; OutInitializer.CascadeSettings.CascadeBiasDistribution = 0; OutInitializer.CascadeSettings.ShadowSplitIndex = 0; OutShadowBounds = Bounds; } void CullMeshObjectsForLightCards( FRDGBuilder& GraphBuilder, const FScene* Scene, const FViewInfo& View, const FLightSceneInfo* LightSceneInfo, EDistanceFieldPrimitiveType PrimitiveType, const FDistanceFieldObjectBufferParameters& ObjectBufferParameters, FMatrix& WorldToMeshSDFShadowValue, FLightTileIntersectionParameters& LightTileIntersectionParameters) { const FVector LumenSceneViewOrigin = Lumen::GetLumenSceneViewOrigin(View, Lumen::GetNumGlobalDFClipmaps(View) - 1); const FVector LumenSceneExtent = FVector(LumenScene::GetCardMaxDistance(View)); const FBox LumenSceneBounds(LumenSceneViewOrigin - LumenSceneExtent, LumenSceneViewOrigin + LumenSceneExtent); FSphere MeshSDFShadowBounds; FWholeSceneProjectedShadowInitializer MeshSDFShadowInitializer; SetupMeshSDFShadowInitializer(LightSceneInfo, LumenSceneBounds, MeshSDFShadowBounds, MeshSDFShadowInitializer); const FMatrix FaceMatrix( FPlane(0, 0, 1, 0), FPlane(0, 1, 0, 0), FPlane(-1, 0, 0, 0), FPlane(0, 0, 0, 1)); const FMatrix TranslatedWorldToView = MeshSDFShadowInitializer.WorldToLight * FaceMatrix; double MaxSubjectZ = TranslatedWorldToView.TransformPosition(MeshSDFShadowInitializer.SubjectBounds.Origin).Z + MeshSDFShadowInitializer.SubjectBounds.SphereRadius; MaxSubjectZ = FMath::Min(MaxSubjectZ, MeshSDFShadowInitializer.MaxDistanceToCastInLightW); const double MinSubjectZ = FMath::Max(MaxSubjectZ - MeshSDFShadowInitializer.SubjectBounds.SphereRadius * 2, MeshSDFShadowInitializer.MinLightW); const FMatrix ScaleMatrix = FScaleMatrix( FVector( MeshSDFShadowInitializer.Scales.X, MeshSDFShadowInitializer.Scales.Y, 1.0f ) ); const FMatrix ViewToClip = ScaleMatrix * FShadowProjectionMatrix(MinSubjectZ, MaxSubjectZ, MeshSDFShadowInitializer.WAxis); const FMatrix SubjectAndReceiverMatrix = TranslatedWorldToView * ViewToClip; int32 NumPlanes = MeshSDFShadowInitializer.CascadeSettings.ShadowBoundsAccurate.Planes.Num(); const FPlane* PlaneData = MeshSDFShadowInitializer.CascadeSettings.ShadowBoundsAccurate.Planes.GetData(); FVector PrePlaneTranslation = FVector::ZeroVector; FVector4f LocalLightShadowBoundingSphere = FVector4f::Zero(); WorldToMeshSDFShadowValue = FTranslationMatrix(MeshSDFShadowInitializer.PreShadowTranslation) * SubjectAndReceiverMatrix; FDistanceFieldCulledObjectBufferParameters CulledObjectBufferParameters; const bool bCullingForDirectShadowing = false; const bool bCullHeighfieldsNotInAtlas = false; CullDistanceFieldObjectsForLight( GraphBuilder, View, LightSceneInfo->Proxy, PrimitiveType, WorldToMeshSDFShadowValue, NumPlanes, PlaneData, PrePlaneTranslation, LocalLightShadowBoundingSphere, MeshSDFShadowBounds.W, bCullingForDirectShadowing, bCullHeighfieldsNotInAtlas, ObjectBufferParameters, CulledObjectBufferParameters, LightTileIntersectionParameters); } static void RenderDirectLightIntoLumenCardsBatched( FRDGBuilder& GraphBuilder, const TArray& Views, const FLumenSceneFrameTemporaries& FrameTemporaries, TRDGUniformBufferRef LumenCardSceneUniformBuffer, const LumenSceneDirectLighting::FLightDataParameters& LumenLightData, FRDGBufferSRVRef ShadowMaskTilesSRV, FRDGBufferSRVRef CardTilesSRV, FRDGBufferSRVRef LightTileOffsetNumPerCardTileSRV, FRDGBufferSRVRef LightTilesPerCardTileSRV, FRDGTextureUAVRef DirectLightingAtlasUAV, FRDGBufferRef IndirectArgBuffer, bool bHasRectLights, ERDGPassFlags ComputePassFlags) { FLumenCardBatchDirectLightingCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->IndirectArgBuffer = IndirectArgBuffer; PassParameters->View = Views[0].ViewUniformBuffer; PassParameters->LumenCardScene = LumenCardSceneUniformBuffer; PassParameters->LumenLightData = LumenLightData; PassParameters->ShadowMaskTiles = ShadowMaskTilesSRV; PassParameters->CardTiles = CardTilesSRV; PassParameters->LightTileOffsetNumPerCardTile = LightTileOffsetNumPerCardTileSRV; PassParameters->LightTilesPerCardTile = LightTilesPerCardTileSRV; PassParameters->RWDirectLightingAtlas = DirectLightingAtlasUAV; PassParameters->RWTileShadowDownsampleFactorAtlas = GraphBuilder.CreateUAV(FrameTemporaries.TileShadowDownsampleFactorAtlas, PF_R32G32B32A32_UINT); PassParameters->TargetFormatQuantizationError = Lumen::GetLightingQuantizationError(); PassParameters->CachedLightingPreExposure = Lumen::GetCachedLightingPreExposure(); int32 NumViewOrigins = FrameTemporaries.ViewOrigins.Num(); for (int32 OriginIndex = 0; OriginIndex < NumViewOrigins; ++OriginIndex) { const FLumenViewOrigin& ViewOrigin = FrameTemporaries.ViewOrigins[OriginIndex]; PassParameters->PreViewTranslationHigh[OriginIndex] = ViewOrigin.PreViewTranslationDF.High; PassParameters->PreViewTranslationLow[OriginIndex] = ViewOrigin.PreViewTranslationDF.Low; PassParameters->ViewExposure[OriginIndex] = ViewOrigin.LastEyeAdaptationExposure; } int32 WaveOpWaveSize = 0; if (GRHISupportsWaveOperations && RHISupportsWaveOperations(Views[0].GetShaderPlatform())) { // 64 wave size is preferred for FLumenCardBatchDirectLightingCS if (GRHIMinimumWaveSize <= 64 && GRHIMaximumWaveSize >= 64) { WaveOpWaveSize = 64; } else if (GRHIMinimumWaveSize <= 32 && GRHIMaximumWaveSize >= 32) { // TODO: wave32 support // WaveOpWaveSize = 32; } } FLumenCardBatchDirectLightingCS::FPermutationDomain PermutationVector; PermutationVector.Set(NumViewOrigins > 1); PermutationVector.Set(bHasRectLights); PermutationVector.Set(WaveOpWaveSize); auto ComputeShader = Views[0].ShaderMap->GetShader(PermutationVector); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("Batched lights"), ComputePassFlags, ComputeShader, PassParameters, IndirectArgBuffer, (uint32)ELumenDispatchCardTilesIndirectArgsOffset::OneGroupPerCardTile); } struct FViewBatchedLightParameters { TArray PerLightTypeParameters[(int32)ELumenLightType::MAX]; }; static void SetPerLightParameters(FPerLightParameters& DstParameters, const FLumenGatheredLight& Light, int32 ViewIndex) { DstParameters.LightIndex = Light.LightIndex; DstParameters.TanLightSourceAngle = FMath::Tan(Light.LightSceneInfo->Proxy->GetLightSourceAngle()); DstParameters.DeferredLightUniforms = Light.DeferredLightUniformBuffers[ViewIndex]; } // Compute for each light the shadow mask based on light attenuation properties (distance falloff, light functions, IES, volumetric cloud) // This pass allows to pre-cull needs for tracing shadow rays static int32 ComputeShadowMaskFromLightAttenuation( FRDGBuilder& GraphBuilder, const FScene* Scene, const FViewInfo& View, TRDGUniformBufferRef LumenCardSceneUniformBuffer, TConstArrayView GatheredLights, TConstArrayView StandaloneLightIndices, const FViewBatchedLightParameters& ViewBatchedLightParameters, const FLumenLightTileScatterParameters& LightTileScatterParameters, const LumenSceneDirectLighting::FLightDataParameters& LumenLightData, int32 ViewIndex, int32 NumViews, const bool bHasLightFunctions, FRDGBufferUAVRef ShadowMaskTilesUAV, FRDGBufferUAVRef ShadowTraceAllocatorUAV, FRDGBufferUAVRef ShadowTracesUAV, FRDGBufferSRVRef TileShadowDownsampleFactorAtlasSRV, ERDGPassFlags ComputePassFlags) { check(NumViews <= LUMEN_MAX_VIEWS); auto SetCommonParameters = [&](FLumenDirectLightingNonRayTracedShadowsParameters& CommonParameters, bool bStandaloneLight) { CommonParameters.IndirectArgBuffer = LightTileScatterParameters.DispatchIndirectArgs; CommonParameters.RWShadowMaskTiles = ShadowMaskTilesUAV; CommonParameters.RWShadowTraceAllocator = ShadowTraceAllocatorUAV; CommonParameters.RWShadowTraces = ShadowTracesUAV; CommonParameters.TileShadowDownsampleFactorAtlas = TileShadowDownsampleFactorAtlasSRV; CommonParameters.View = View.ViewUniformBuffer; CommonParameters.LumenCardScene = LumenCardSceneUniformBuffer; CommonParameters.LightTileScatterParameters = LightTileScatterParameters; CommonParameters.LumenLightData = LumenLightData; CommonParameters.CardScatterInstanceIndex = 0; CommonParameters.ViewIndex = ViewIndex; CommonParameters.NumViews = NumViews; CommonParameters.DummyZeroForFixingShaderCompilerBug = 0; CommonParameters.ForwardLightStruct = View.ForwardLightingResources.ForwardLightUniformBuffer; CommonParameters.MaxTraceDistance = Lumen::GetMaxTraceDistance(View); CommonParameters.StepFactor = FMath::Clamp(GOffscreenShadowingTraceStepFactor, .1f, 10.0f); CommonParameters.HeightfieldShadowReceiverBias = Lumen::GetHeightfieldReceiverBias(); CommonParameters.bAdaptiveShadowTracing = CVarLumenDirectLightingHWRTAdaptiveShadowTracing.GetValueOnRenderThread(); if (bStandaloneLight) { CommonParameters.LightTileScatterParameters.bUseLightTilesPerLightType = 0; } }; int32 NumLightsNeedShadowMasks = StandaloneLightIndices.Num(); for (const int32 StandaloneLightIndex : StandaloneLightIndices) { const FLumenGatheredLight& Light = GatheredLights[StandaloneLightIndex]; check(Light.NeedsShadowMask()); const FMaterialRenderProxy* LightFunctionMaterialProxy = Light.LightFunctionMaterialProxy; const bool bMayUseCloudTransmittance = GLumenDirectLightingCloudTransmittance != 0 && Light.bMayCastCloudTransmittance; const uint32 SlotIndex = LumenSceneDirectLighting::NumBatchableLightTypes + Light.LightIndex; const uint32 DispatchIndirectArgOffset = (SlotIndex * NumViews + ViewIndex) * sizeof(FRHIDispatchIndirectParameters); if (LightFunctionMaterialProxy) { FLumenDirectLightingShadowMaskFromLightAttenuationWithLightFunctionCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); SetCommonParameters(PassParameters->Common, true); SetPerLightParameters(PassParameters->Common.LightParameters, Light, ViewIndex); const bool bUseCloudTransmittance = SetupLightCloudTransmittanceParameters( GraphBuilder, Scene, View, bMayUseCloudTransmittance ? Light.LightSceneInfo : nullptr, PassParameters->Common.LightCloudTransmittanceParameters); SetupLightFunctionParameters(View, Light.LightSceneInfo, 1.0f, PassParameters->LightFunctionParameters); FLumenDirectLightingShadowMaskFromLightAttenuationWithLightFunctionCS::FPermutationDomain PermutationVector; PermutationVector.Set(Lumen::UseThreadGroupSize32()); PermutationVector.Set(ShadowTraceAllocatorUAV != nullptr); PermutationVector.Set(Light.Type); PermutationVector.Set(bUseCloudTransmittance); const FMaterial& Material = LightFunctionMaterialProxy->GetMaterialWithFallback(Scene->GetFeatureLevel(), LightFunctionMaterialProxy); const FMaterialShaderMap* MaterialShaderMap = Material.GetRenderingThreadShaderMap(); TShaderRef ComputeShader = MaterialShaderMap->GetShader(PermutationVector); FRDGBufferRef IndirectArgsBuffer = LightTileScatterParameters.DispatchIndirectArgs; ClearUnusedGraphResources(ComputeShader, PassParameters, { IndirectArgsBuffer }); GraphBuilder.AddPass( RDG_EVENT_NAME("ShadowMaskFromLightAttenuationPass(LF,%s)", *Light.Name), PassParameters, ComputePassFlags, [PassParameters, ComputeShader, IndirectArgsBuffer, DispatchIndirectArgOffset, LightFunctionMaterialProxy, &Material, &View](FRDGAsyncTask, FRHIComputeCommandList& RHICmdList) { IndirectArgsBuffer->MarkResourceAsUsed(); FComputeShaderUtils::ValidateIndirectArgsBuffer(IndirectArgsBuffer, DispatchIndirectArgOffset); FRHIComputeShader* ShaderRHI = ComputeShader.GetComputeShader(); SetComputePipelineState(RHICmdList, ShaderRHI); SetShaderParameters(RHICmdList, ComputeShader, ShaderRHI, *PassParameters); ComputeShader->SetParameters(RHICmdList, ShaderRHI, LightFunctionMaterialProxy, Material, View); RHICmdList.DispatchIndirectComputeShader(IndirectArgsBuffer->GetIndirectRHICallBuffer(), DispatchIndirectArgOffset); UnsetShaderUAVs(RHICmdList, ComputeShader, ShaderRHI); }); } else { FLumenDirectLightingShadowMaskFromLightAttenuationCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); SetCommonParameters(PassParameters->Common, true); SetPerLightParameters(PassParameters->Common.LightParameters, Light, ViewIndex); const bool bUseCloudTransmittance = SetupLightCloudTransmittanceParameters( GraphBuilder, Scene, View, bMayUseCloudTransmittance ? Light.LightSceneInfo : nullptr, PassParameters->Common.LightCloudTransmittanceParameters); FLumenDirectLightingShadowMaskFromLightAttenuationCS::FPermutationDomain PermutationVector; PermutationVector.Set(Lumen::UseThreadGroupSize32()); PermutationVector.Set(ShadowTraceAllocatorUAV != nullptr); PermutationVector.Set(Light.Type); PermutationVector.Set(bUseCloudTransmittance); PermutationVector.Set(false); TShaderRef ComputeShader = View.ShaderMap->GetShader(PermutationVector); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("ShadowMaskFromLightAttenuationPass(%s)", *Light.Name), ComputePassFlags, ComputeShader, PassParameters, LightTileScatterParameters.DispatchIndirectArgs, DispatchIndirectArgOffset); } } const bool bUseLightFunctionAtlas = bHasLightFunctions && LightFunctionAtlas::IsEnabled(View, ELightFunctionAtlasSystem::Lumen); for (int32 LightTypeIndex = 0; LightTypeIndex < (int32)ELumenLightType::MAX; ++LightTypeIndex) { TConstArrayView BatchedLightParameters = ViewBatchedLightParameters.PerLightTypeParameters[LightTypeIndex]; NumLightsNeedShadowMasks += BatchedLightParameters.Num(); if (BatchedLightParameters.Num() > 0) { FLumenDirectLightingShadowMaskFromLightAttenuationCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); SetCommonParameters(PassParameters->Common, false); SetupLightCloudTransmittanceParameters(GraphBuilder, Scene, View, nullptr, PassParameters->Common.LightCloudTransmittanceParameters); if (bUseLightFunctionAtlas) { PassParameters->LightFunctionAtlas = LightFunctionAtlas::BindGlobalParameters(GraphBuilder, View); } FLumenDirectLightingShadowMaskFromLightAttenuationCS::FPermutationDomain PermutationVector; PermutationVector.Set(Lumen::UseThreadGroupSize32()); PermutationVector.Set(ShadowTraceAllocatorUAV != nullptr); PermutationVector.Set((ELumenLightType)LightTypeIndex); PermutationVector.Set(false); PermutationVector.Set(bUseLightFunctionAtlas); TShaderRef ComputeShader = View.ShaderMap->GetShader(PermutationVector); if (LumenSceneDirectLighting::UseLightTilesPerLightType()) { check(LightTypeIndex > 0); const uint32 IndirectArgsOffset = ((LightTypeIndex - 1) * NumViews + ViewIndex) * sizeof(FRHIDispatchIndirectParameters); // This is skipped via a dynamic branch so not used but still needs to be bound PassParameters->Common.LightParameters = BatchedLightParameters[0]; FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("ShadowMaskFromLightAttenuationPass(LightType=%d)", LightTypeIndex), ComputePassFlags, ComputeShader, PassParameters, LightTileScatterParameters.DispatchIndirectArgs, IndirectArgsOffset); } else { const FShaderParametersMetadata* ParametersMetaData = FLumenDirectLightingShadowMaskFromLightAttenuationCS::FParameters::FTypeInfo::GetStructMetadata(); FRDGBufferRef IndirectArgsBuffer = LightTileScatterParameters.DispatchIndirectArgs; ClearUnusedGraphResourcesImpl(ComputeShader->Bindings, ParametersMetaData, PassParameters, { IndirectArgsBuffer }); GraphBuilder.AddPass( RDG_EVENT_NAME("ShadowMaskFromLightAttenuationPass(LightType=%d,BatchedNum=%d)", LightTypeIndex, BatchedLightParameters.Num()), PassParameters, ComputePassFlags, [PassParameters, ComputeShader, IndirectArgsBuffer, NumViews, ViewIndex, BatchedLightParameters](FRDGAsyncTask, FRHIComputeCommandList& RHICmdList) { // Marks the indirect draw parameter as used by the pass manually, given it can't be bound directly by any of the shader, // meaning SetShaderParameters() won't be able to do it. IndirectArgsBuffer->MarkResourceAsUsed(); for (const FPerLightParameters& LightParameterValues : BatchedLightParameters) { const uint32 SlotIndex = LumenSceneDirectLighting::NumBatchableLightTypes + LightParameterValues.LightIndex; const uint32 IndirectArgsOffset = (SlotIndex * NumViews + ViewIndex) * sizeof(FRHIDispatchIndirectParameters); // TODO: Only set changed paramters PassParameters->Common.LightParameters = LightParameterValues; FComputeShaderUtils::DispatchIndirect(RHICmdList, ComputeShader, *PassParameters, IndirectArgsBuffer->GetIndirectRHICallBuffer(), IndirectArgsOffset); } }); } } } return NumLightsNeedShadowMasks; } void TraceDistanceFieldShadows( FRDGBuilder& GraphBuilder, const FScene* Scene, const FViewInfo& View, TRDGUniformBufferRef LumenCardSceneUniformBuffer, TConstArrayView GatheredLights, TConstArrayView StandaloneLightIndices, FViewBatchedLightParameters& ViewBatchedLightParameters, const FLumenLightTileScatterParameters& LightTileScatterParameters, const LumenSceneDirectLighting::FLightDataParameters& LumenLightData, const FDistanceFieldObjectBufferParameters& ObjectBufferParameters, int32 ViewIndex, int32 NumViews, FRDGBufferUAVRef ShadowMaskTilesUAV, ERDGPassFlags ComputePassFlags) { extern int32 GDistanceFieldOffsetDataStructure; extern float GDFShadowTwoSidedMeshDistanceBiasScale; auto SetCommonParameters = [&]( FLumenSceneDirectLightingTraceDistanceFieldShadowsCS::FParameters* PassParameters, const FLightTileIntersectionParameters& LightTileIntersectionParameters, const FMatrix& WorldToMeshSDFShadowValue, bool bStandaloneLight) { PassParameters->IndirectArgBuffer = LightTileScatterParameters.DispatchIndirectArgs; PassParameters->RWShadowMaskTiles = ShadowMaskTilesUAV; PassParameters->View = View.ViewUniformBuffer; PassParameters->LumenCardScene = LumenCardSceneUniformBuffer; PassParameters->LightTileScatterParameters = LightTileScatterParameters; PassParameters->LumenLightData = LumenLightData; PassParameters->ViewIndex = ViewIndex; PassParameters->NumViews = NumViews; PassParameters->DummyZeroForFixingShaderCompilerBug = 0; PassParameters->ObjectBufferParameters = ObjectBufferParameters; PassParameters->LightTileIntersectionParameters = LightTileIntersectionParameters; FDistanceFieldAtlasParameters DistanceFieldAtlasParameters = DistanceField::SetupAtlasParameters(GraphBuilder, Scene->DistanceFieldSceneData); PassParameters->DistanceFieldAtlasParameters = DistanceFieldAtlasParameters; PassParameters->TranslatedWorldToShadow = FMatrix44f(FTranslationMatrix(-View.ViewMatrices.GetPreViewTranslation()) * WorldToMeshSDFShadowValue); PassParameters->TwoSidedMeshDistanceBiasScale = GDFShadowTwoSidedMeshDistanceBiasScale; PassParameters->MaxTraceDistance = Lumen::GetMaxTraceDistance(View); PassParameters->StepFactor = FMath::Clamp(GOffscreenShadowingTraceStepFactor, .1f, 10.0f); PassParameters->MeshSDFShadowRayBias = LumenSceneDirectLighting::GetMeshSDFShadowRayBias(); PassParameters->HeightfieldShadowRayBias = LumenSceneDirectLighting::GetHeightfieldShadowRayBias(); PassParameters->GlobalSDFShadowRayBias = LumenSceneDirectLighting::GetGlobalSDFShadowRayBias(); PassParameters->HeightfieldMaxTracingSteps = Lumen::GetHeightfieldMaxTracingSteps(); if (bStandaloneLight) { PassParameters->LightTileScatterParameters.bUseLightTilesPerLightType = 0; } }; const bool bThreadGroupSize32 = Lumen::UseThreadGroupSize32(); const bool bTraceGlobalSDF = Lumen::UseGlobalSDFTracing(View.Family->EngineShowFlags); const bool bSimpleCoverageBasedExpand = bTraceGlobalSDF && Lumen::UseGlobalSDFSimpleCoverageBasedExpand(); for (const int32 StandaloneLightIndex : StandaloneLightIndices) { const FLumenGatheredLight& Light = GatheredLights[StandaloneLightIndex]; if (!Light.bHasShadows) { continue; } const FLumenSceneData& LumenSceneData = *Scene->GetLumenSceneData(View); FLightTileIntersectionParameters LightTileIntersectionParameters; FMatrix WorldToMeshSDFShadowValue = FMatrix::Identity; // Whether to trace individual mesh SDFs or heightfield objects for higher quality offscreen shadowing const bool bTraceMeshObjects = Light.bHasShadows && Light.Type == ELumenLightType::Directional && DoesPlatformSupportDistanceFieldShadowing(View.GetShaderPlatform()) && GLumenDirectLightingOffscreenShadowingTraceMeshSDFs != 0; const bool bTraceMeshSDFs = bTraceMeshObjects && Lumen::UseMeshSDFTracing(View.Family->EngineShowFlags) && ObjectBufferParameters.NumSceneObjects > 0; const bool bTraceHeighfieldObjects = bTraceMeshObjects && Lumen::UseHeightfieldTracing(*View.Family, LumenSceneData); if (bTraceMeshSDFs) { CullMeshObjectsForLightCards( GraphBuilder, Scene, //@todo - this breaks second view if far away View, Light.LightSceneInfo, DFPT_SignedDistanceField, ObjectBufferParameters, WorldToMeshSDFShadowValue, LightTileIntersectionParameters); } if (bTraceHeighfieldObjects) { FLightTileIntersectionParameters LightTileHeightfieldIntersectionParameters; CullMeshObjectsForLightCards( GraphBuilder, Scene, View, Light.LightSceneInfo, DFPT_HeightField, ObjectBufferParameters, WorldToMeshSDFShadowValue, LightTileHeightfieldIntersectionParameters); if (!bTraceMeshSDFs) { LightTileIntersectionParameters = LightTileHeightfieldIntersectionParameters; } LightTileIntersectionParameters.HeightfieldShadowTileNumCulledObjects = LightTileHeightfieldIntersectionParameters.ShadowTileNumCulledObjects; LightTileIntersectionParameters.HeightfieldShadowTileStartOffsets = LightTileHeightfieldIntersectionParameters.ShadowTileStartOffsets; LightTileIntersectionParameters.HeightfieldShadowTileArrayData = LightTileHeightfieldIntersectionParameters.ShadowTileArrayData; } FLumenSceneDirectLightingTraceDistanceFieldShadowsCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); SetCommonParameters(PassParameters, LightTileIntersectionParameters, WorldToMeshSDFShadowValue, true); SetPerLightParameters(PassParameters->LightParameters, Light, ViewIndex); FLumenSceneDirectLightingTraceDistanceFieldShadowsCS::FPermutationDomain PermutationVector; PermutationVector.Set(bThreadGroupSize32); PermutationVector.Set(Light.Type); PermutationVector.Set(bTraceGlobalSDF); PermutationVector.Set(bSimpleCoverageBasedExpand); PermutationVector.Set(bTraceMeshSDFs); PermutationVector.Set(bTraceHeighfieldObjects); PermutationVector.Set(GDistanceFieldOffsetDataStructure); PermutationVector = FLumenSceneDirectLightingTraceDistanceFieldShadowsCS::RemapPermutation(PermutationVector); TShaderRef ComputeShader = View.ShaderMap->GetShader(PermutationVector); const uint32 SlotIndex = LumenSceneDirectLighting::NumBatchableLightTypes + Light.LightIndex; const uint32 DispatchIndirectArgOffset = (SlotIndex * NumViews + ViewIndex) * sizeof(FRHIDispatchIndirectParameters); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("DistanceFieldShadowPass %s", *Light.Name), ComputePassFlags, ComputeShader, PassParameters, LightTileScatterParameters.DispatchIndirectArgs, DispatchIndirectArgOffset); } for (int32 LightTypeIndex = 0; LightTypeIndex < (int32)ELumenLightType::MAX; ++LightTypeIndex) { TArray& BatchedLightParameters = ViewBatchedLightParameters.PerLightTypeParameters[LightTypeIndex]; if (BatchedLightParameters.Num() > 0) { FLumenSceneDirectLightingTraceDistanceFieldShadowsCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); SetCommonParameters(PassParameters, FLightTileIntersectionParameters(), FMatrix::Identity, false); FLumenSceneDirectLightingTraceDistanceFieldShadowsCS::FPermutationDomain PermutationVector; PermutationVector.Set(bThreadGroupSize32); PermutationVector.Set((ELumenLightType)LightTypeIndex); PermutationVector.Set(bTraceGlobalSDF); PermutationVector.Set(bSimpleCoverageBasedExpand); PermutationVector.Set(false); PermutationVector.Set(false); PermutationVector.Set(GDistanceFieldOffsetDataStructure); PermutationVector = FLumenSceneDirectLightingTraceDistanceFieldShadowsCS::RemapPermutation(PermutationVector); TShaderRef ComputeShader = View.ShaderMap->GetShader(PermutationVector); if (LumenSceneDirectLighting::UseLightTilesPerLightType()) { check(LightTypeIndex > 0); const uint32 IndirectArgsOffset = ((LightTypeIndex - 1) * NumViews + ViewIndex) * sizeof(FRHIDispatchIndirectParameters); // This is skipped via a dynamic branch so not used but still needs to be bound PassParameters->LightParameters = BatchedLightParameters[0]; FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("DistanceFieldShadowPass LightType=%d", LightTypeIndex), ComputePassFlags, ComputeShader, PassParameters, LightTileScatterParameters.DispatchIndirectArgs, IndirectArgsOffset); } else { const FShaderParametersMetadata* ParametersMetaData = FLumenSceneDirectLightingTraceDistanceFieldShadowsCS::FParameters::FTypeInfo::GetStructMetadata(); FRDGBufferRef IndirectArgsBuffer = LightTileScatterParameters.DispatchIndirectArgs; ClearUnusedGraphResourcesImpl(ComputeShader->Bindings, ParametersMetaData, PassParameters, { IndirectArgsBuffer }); GraphBuilder.AddPass( RDG_EVENT_NAME("DistanceFieldShadowPass LightType=%d BatchedNum=%d", LightTypeIndex, BatchedLightParameters.Num()), PassParameters, ComputePassFlags, [PassParameters, ComputeShader, IndirectArgsBuffer, NumViews, ViewIndex, LocalBatchedLightParameters = MoveTemp(BatchedLightParameters)](FRDGAsyncTask, FRHIComputeCommandList& RHICmdList) mutable { // Marks the indirect draw parameter as used by the pass manually, given it can't be bound directly by any of the shader, // meaning SetShaderParameters() won't be able to do it. IndirectArgsBuffer->MarkResourceAsUsed(); for (FPerLightParameters& LightParameterValues : LocalBatchedLightParameters) { const uint32 SlotIndex = LumenSceneDirectLighting::NumBatchableLightTypes + LightParameterValues.LightIndex; const uint32 IndirectArgsOffset = (SlotIndex * NumViews + ViewIndex) * sizeof(FRHIDispatchIndirectParameters); // TODO: Only set changed paramters PassParameters->LightParameters = MoveTemp(LightParameterValues); FComputeShaderUtils::DispatchIndirect(RHICmdList, ComputeShader, *PassParameters, IndirectArgsBuffer->GetIndirectRHICallBuffer(), IndirectArgsOffset); } }); } } } } // Must match FLumenPackedLight in LumenSceneDirectLighting.ush struct FLumenPackedLight { FVector3f WorldPositionHigh; uint32 LightingChannelMask; FVector3f WorldPositionLow; float InvRadius; FVector3f Color; float FalloffExponent; FVector3f Direction; uint32 DiffuseAndSpecularScale; FVector3f Tangent; float SourceRadius; FVector2f SpotAngles; float SoftSourceRadius; float SourceLength; float RectLightBarnCosAngle; float RectLightBarnLength; float RectLightAtlasMaxLevel; uint32 LightType; FVector2f SinCosConeAngleOrRectLightAtlasUVScale; FVector2f RectLightAtlasUVOffset; uint32 LightFunctionAtlasIndex_bHasShadowMask_bIsStandalone_bCastDynamicShadows; float IESAtlasIndex; float InverseExposureBlend; float Padding0; }; struct FLightTileCullContext { FLumenLightTileScatterParameters LightTileScatterParameters; FRDGBufferRef LightTileAllocator; FRDGBufferRef LightTiles; FRDGBufferRef DispatchLightTilesIndirectArgs; FRDGBufferRef LightTileOffsetNumPerCardTile; FRDGBufferRef LightTilesPerCardTile; uint32 MaxCulledCardTiles; }; // Build list of surface cache tiles per light for future processing static void CullDirectLightingTiles( FRDGBuilder& GraphBuilder, const TArray& Views, const FLumenSceneFrameTemporaries& FrameTemporaries, const FLumenCardUpdateContext& CardUpdateContext, TRDGUniformBufferRef LumenCardSceneUniformBuffer, TConstArrayView GatheredLights, TConstArrayView StandaloneLightIndices, const LumenSceneDirectLighting::FLightDataParameters& LumenLightData, FLightTileCullContext& CullContext, FLumenCardTileUpdateContext& CardTileUpdateContext, ERDGPassFlags ComputePassFlags) { RDG_EVENT_SCOPE(GraphBuilder, "CullTiles %d lights", GatheredLights.Num()); const FGlobalShaderMap* GlobalShaderMap = Views[0].ShaderMap; int32 NumViewOrigins = FrameTemporaries.ViewOrigins.Num(); const uint32 MaxLightTiles = CardUpdateContext.MaxUpdateTiles; const uint32 NumLightSlots = LumenSceneDirectLighting::NumBatchableLightTypes + GatheredLights.Num(); const uint32 NumLightsRoundedUp = FMath::RoundUpToPowerOfTwo(NumLightSlots) * NumViewOrigins; const uint32 MaxLightsPerTile = FMath::RoundUpToPowerOfTwo(FMath::Clamp(CVarLumenDirectLightingMaxLightsPerTile.GetValueOnRenderThread(), 1, 32)); const uint32 MaxCulledCardTiles = MaxLightsPerTile * MaxLightTiles; Lumen::SpliceCardPagesIntoTiles(GraphBuilder, GlobalShaderMap, CardUpdateContext, LumenCardSceneUniformBuffer, CardTileUpdateContext, ComputePassFlags); const bool bCullToCardTileDepthRange = CVarLumenDirectLightingCullToTileDepthRange.GetValueOnRenderThread() != 0; FRDGBufferRef CardTileDepthRanges = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32), bCullToCardTileDepthRange ? MaxLightTiles : 1), TEXT("Lumen.CardTileDepthRanges")); FRDGBufferRef CardTileAllocator = CardTileUpdateContext.CardTileAllocator; FRDGBufferRef CardTiles = CardTileUpdateContext.CardTiles; FRDGBufferRef DispatchCardTilesIndirectArgs = CardTileUpdateContext.DispatchCardTilesIndirectArgs; // Calculate min and max card tile depth for better light culling if (bCullToCardTileDepthRange) { FCalculateCardTileDepthRangesCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->IndirectArgBuffer = DispatchCardTilesIndirectArgs; PassParameters->View = Views[0].ViewUniformBuffer; PassParameters->LumenCardScene = LumenCardSceneUniformBuffer; PassParameters->RWCardTileDepthRanges = GraphBuilder.CreateUAV(CardTileDepthRanges); PassParameters->CardTileAllocator = GraphBuilder.CreateSRV(CardTileAllocator); PassParameters->CardTiles = GraphBuilder.CreateSRV(CardTiles); auto ComputeShader = GlobalShaderMap->GetShader(); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("CalculateCardTileDepthRanges"), ComputePassFlags, ComputeShader, PassParameters, DispatchCardTilesIndirectArgs, (uint32)ELumenDispatchCardTilesIndirectArgsOffset::OneGroupPerCardTile); } FRDGBufferRef LightTileAllocator = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32), 1), TEXT("Lumen.DirectLighting.LightTileAllocator")); FRDGBufferRef LightTiles = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(2 * sizeof(uint32), MaxCulledCardTiles), TEXT("Lumen.DirectLighting.LightTiles")); AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(LightTileAllocator), 0, ComputePassFlags); FRDGBufferRef LightTileAllocatorPerLight = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32), NumLightsRoundedUp), TEXT("Lumen.DirectLighting.LightTileAllocatorPerLight")); FRDGBufferRef LightTileOffsetsPerLight = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32), NumLightsRoundedUp), TEXT("Lumen.DirectLighting.LightTileOffsetsPerLight")); AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(LightTileAllocatorPerLight), 0, ComputePassFlags); // Used to figure out the offset to store light tiles for each card tile FRDGBufferRef LightTileAllocatorForPerCardTileDispatch = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32), 1), TEXT("Lumen.DirectLighting.LightTileAllocatorForPerCardTileDispatch")); FRDGBufferRef LightTileOffsetNumPerCardTile = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32), MaxLightTiles), TEXT("Lumen.DirectLighting.LightTileOffsetNumPerCardTile")); AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(LightTileAllocatorForPerCardTileDispatch), 0, ComputePassFlags); // Build a list of light tiles for future processing { FBuildLightTilesCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->IndirectArgBuffer = DispatchCardTilesIndirectArgs; PassParameters->View = Views[0].ViewUniformBuffer; PassParameters->LumenCardScene = LumenCardSceneUniformBuffer; PassParameters->LumenLightData = LumenLightData; PassParameters->RWLightTileAllocator = GraphBuilder.CreateUAV(LightTileAllocator); PassParameters->RWLightTileAllocatorForPerCardTileDispatch = GraphBuilder.CreateUAV(LightTileAllocatorForPerCardTileDispatch); PassParameters->RWLightTiles = GraphBuilder.CreateUAV(LightTiles); PassParameters->RWLightTileAllocatorPerLight = GraphBuilder.CreateUAV(LightTileAllocatorPerLight); PassParameters->RWLightTileOffsetNumPerCardTile = GraphBuilder.CreateUAV(LightTileOffsetNumPerCardTile); PassParameters->CardTileAllocator = GraphBuilder.CreateSRV(CardTileAllocator); PassParameters->CardTiles = GraphBuilder.CreateSRV(CardTiles); PassParameters->CardTileDepthRanges = bCullToCardTileDepthRange ? GraphBuilder.CreateSRV(CardTileDepthRanges) : PassParameters->CardTiles; PassParameters->CullToCardTileDepthRange = bCullToCardTileDepthRange ? 1 : 0; PassParameters->MaxLightsPerTile = MaxLightsPerTile; PassParameters->NumLights = GatheredLights.Num(); PassParameters->NumViews = NumViewOrigins; PassParameters->bUseLightTilesPerLightType = LumenSceneDirectLighting::UseLightTilesPerLightType() ? 1 : 0; check(NumViewOrigins <= PassParameters->FrustumTranslatedWorldToClip.Num()); for (int32 OriginIndex = 0; OriginIndex < NumViewOrigins; ++OriginIndex) { const FLumenViewOrigin& ViewOrigin = FrameTemporaries.ViewOrigins[OriginIndex]; PassParameters->FrustumTranslatedWorldToClip[OriginIndex] = ViewOrigin.FrustumTranslatedWorldToClip; PassParameters->PreViewTranslationHigh[OriginIndex] = ViewOrigin.PreViewTranslationDF.High; PassParameters->PreViewTranslationLow[OriginIndex] = ViewOrigin.PreViewTranslationDF.Low; PassParameters->ViewExposure[OriginIndex] = ViewOrigin.LastEyeAdaptationExposure; } FBuildLightTilesCS::FPermutationDomain PermutationVector; PermutationVector.Set(MaxLightsPerTile); auto ComputeShader = GlobalShaderMap->GetShader(PermutationVector); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("BuildLightTiles"), ComputePassFlags, ComputeShader, PassParameters, DispatchCardTilesIndirectArgs, (uint32)ELumenDispatchCardTilesIndirectArgsOffset::OneThreadPerCardTile); } // Compute prefix sum for card tile array { const bool bUseStandaloneLightIndices = LumenSceneDirectLighting::UseLightTilesPerLightType(); FComputeLightTileOffsetsPerLightCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->RWLightTileOffsetsPerLight = GraphBuilder.CreateUAV(LightTileOffsetsPerLight); PassParameters->LightTileAllocatorPerLight = GraphBuilder.CreateSRV(LightTileAllocatorPerLight); PassParameters->NumLights = GatheredLights.Num(); PassParameters->NumViews = NumViewOrigins; if (bUseStandaloneLightIndices) { FRDGBufferRef StandaloneLightIndicesBuffer = CreateStructuredBuffer(GraphBuilder, TEXT("Lumen.DirectLighting.StandaloneLightIndices"), StandaloneLightIndices, ERDGInitialDataFlags::NoCopy); PassParameters->StandaloneLightIndices = GraphBuilder.CreateSRV(StandaloneLightIndicesBuffer); PassParameters->NumStandaloneLights = (uint32)StandaloneLightIndices.Num(); } FComputeLightTileOffsetsPerLightCS::FPermutationDomain PermutationVector; PermutationVector.Set(bUseStandaloneLightIndices); auto ComputeShader = GlobalShaderMap->GetShader(PermutationVector); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("ComputeLightTileOffsetsPerLight"), ComputePassFlags, ComputeShader, PassParameters, FIntVector(1, 1, 1)); } enum class EDispatchTilesIndirectArgOffset { NumTilesDiv1 = 0 * sizeof(FRHIDispatchIndirectParameters), NumTilesDiv64 = 1 * sizeof(FRHIDispatchIndirectParameters), MAX = 2, }; // Initialize indirect args for culled tiles FRDGBufferRef DispatchLightTilesIndirectArgs = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateIndirectDesc((int32)EDispatchTilesIndirectArgOffset::MAX), TEXT("Lumen.DirectLighting.DispatchLightTilesIndirectArgs")); FRDGBufferRef DrawTilesPerLightIndirectArgs = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateIndirectDesc(NumLightsRoundedUp), TEXT("Lumen.DirectLighting.DrawTilesPerLightIndirectArgs")); FRDGBufferRef DispatchTilesPerLightIndirectArgs = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateIndirectDesc(NumLightsRoundedUp), TEXT("Lumen.DirectLighting.DispatchTilesPerLightIndirectArgs")); { FInitializeLightTileIndirectArgsCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->RWDispatchLightTilesIndirectArgs = GraphBuilder.CreateUAV(DispatchLightTilesIndirectArgs); PassParameters->RWDrawTilesPerLightIndirectArgs = GraphBuilder.CreateUAV(DrawTilesPerLightIndirectArgs); PassParameters->RWDispatchTilesPerLightIndirectArgs = GraphBuilder.CreateUAV(DispatchTilesPerLightIndirectArgs); PassParameters->LightTileAllocator = GraphBuilder.CreateSRV(LightTileAllocator); PassParameters->LightTileAllocatorPerLight = GraphBuilder.CreateSRV(LightTileAllocatorPerLight); PassParameters->VertexCountPerInstanceIndirect = GRHISupportsRectTopology ? 3 : 6; PassParameters->PerLightDispatchFactor = Lumen::UseThreadGroupSize32() ? 2 : 1; PassParameters->NumLights = NumLightSlots; PassParameters->NumViews = NumViewOrigins; auto ComputeShader = GlobalShaderMap->GetShader(); const FIntVector GroupSize = FComputeShaderUtils::GetGroupCount( FMath::Max((int32)NumLightSlots * NumViewOrigins, 1), // Dispatch at least one group in order to init global tile indirect arguments FInitializeLightTileIndirectArgsCS::GetGroupSize()); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("InitializeLightTileIndirectArgs"), ComputePassFlags, ComputeShader, PassParameters, GroupSize); } FRDGBufferRef LightTilesPerCardTile = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(2 * sizeof(uint32), MaxCulledCardTiles), TEXT("Lumen.DirectLighting.LightTilesPerCardTile")); // Compact card tile array { FRDGBufferRef CompactedLightTiles = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(2 * sizeof(uint32), MaxCulledCardTiles), TEXT("Lumen.DirectLighting.CompactedLightTiles")); FRDGBufferRef CompactedLightTileAllocatorPerLight = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32), NumLightsRoundedUp), TEXT("Lumen.DirectLighting.CompactedLightTileAllocatorPerLight")); AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(CompactedLightTileAllocatorPerLight), 0, ComputePassFlags); FCompactLightTilesCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->IndirectArgBuffer = DispatchLightTilesIndirectArgs; PassParameters->RWCompactedLightTiles = GraphBuilder.CreateUAV(CompactedLightTiles); PassParameters->RWCompactedLightTileAllocatorPerLight = GraphBuilder.CreateUAV(CompactedLightTileAllocatorPerLight); PassParameters->RWLightTilesPerCardTile = GraphBuilder.CreateUAV(LightTilesPerCardTile); PassParameters->LightTileAllocator = GraphBuilder.CreateSRV(LightTileAllocator); PassParameters->LightTiles = GraphBuilder.CreateSRV(LightTiles); PassParameters->LightTileOffsetsPerLight = GraphBuilder.CreateSRV(LightTileOffsetsPerLight); PassParameters->CardTiles = GraphBuilder.CreateSRV(CardTiles); PassParameters->LightTileOffsetNumPerCardTile = GraphBuilder.CreateSRV(LightTileOffsetNumPerCardTile); PassParameters->NumLights = GatheredLights.Num(); PassParameters->NumViews = NumViewOrigins; PassParameters->bUseLightTilesPerLightType = LumenSceneDirectLighting::UseLightTilesPerLightType() ? 1 : 0; auto ComputeShader = GlobalShaderMap->GetShader(); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("CompactLightTiles"), ComputePassFlags, ComputeShader, PassParameters, DispatchLightTilesIndirectArgs, (int32)EDispatchTilesIndirectArgOffset::NumTilesDiv64); LightTiles = CompactedLightTiles; } CullContext.LightTileScatterParameters.DrawIndirectArgs = DrawTilesPerLightIndirectArgs; CullContext.LightTileScatterParameters.DispatchIndirectArgs = DispatchTilesPerLightIndirectArgs; CullContext.LightTileScatterParameters.LightTileAllocator = GraphBuilder.CreateSRV(LightTileAllocator); CullContext.LightTileScatterParameters.LightTiles = GraphBuilder.CreateSRV(LightTiles); CullContext.LightTileScatterParameters.LightTileOffsetsPerLight = GraphBuilder.CreateSRV(LightTileOffsetsPerLight); CullContext.LightTileScatterParameters.bUseLightTilesPerLightType = LumenSceneDirectLighting::UseLightTilesPerLightType() ? 1 : 0; CullContext.LightTiles = LightTiles; CullContext.LightTileAllocator = LightTileAllocator; CullContext.DispatchLightTilesIndirectArgs = DispatchLightTilesIndirectArgs; CullContext.LightTileOffsetNumPerCardTile = LightTileOffsetNumPerCardTile; CullContext.LightTilesPerCardTile = LightTilesPerCardTile; CullContext.MaxCulledCardTiles = MaxCulledCardTiles; } struct FLumenDirectLightingTaskData { mutable UE::Tasks::FTask Task; TArray> GatheredLights; TArray> PackedLightData; TArray LightInfluenceSpheres; // Note: All batched lights cast ray traced shadows mutable TArray> ViewBatchedLightParameters; // Note: All standalone (non-batched) lights need shadow masks but may not cast ray traced shadows TArray> StandaloneLightIndices; // Needed when we batch lights of the same type into a single dispatch. The UB is not accessed but // still needs to be bound because it is skipped via a dynamic branch TUniformBufferRef DummyLightUniformBuffer; bool bHasIESLights = false; bool bHasRectLights = false; bool bHasLightFunctions = false; }; uint32 PackRG16(float In0, float In1); void FDeferredShadingSceneRenderer::BeginGatherLumenLights(const FLumenSceneFrameTemporaries& FrameTemporaries, FLumenDirectLightingTaskData*& TaskData, IVisibilityTaskData* VisibilityTaskData, UE::Tasks::FTask UpdateLightFunctionAtlasTask) { if (HasRayTracedOverlay(ViewFamily)) { return; } bool bAnyLumenActive = false; for (const FViewInfo& View : Views) { const FPerViewPipelineState& ViewPipelineState = GetViewPipelineState(View); bAnyLumenActive |= ViewPipelineState.DiffuseIndirectMethod == EDiffuseIndirectMethod::Lumen; } if (!bAnyLumenActive || CVarLumenLumenSceneDirectLighting.GetValueOnRenderThread() == 0) { return; } TaskData = Allocator.Create(); TArray> Prerequisites; Prerequisites.Add(VisibilityTaskData->GetLightVisibilityTask()); Prerequisites.Add(UpdateLightFunctionAtlasTask); TaskData->Task = LaunchSceneRenderTask(TEXT("GatherLumenLights"), [TaskData, Scene = this->Scene, &Views = this->Views, &ViewFamily = this->ViewFamily, &FrameTemporaries] { SCOPED_NAMED_EVENT_TEXT("GatherLumenLights", FColor::Green); const bool bUseHardwareRayTracing = Lumen::UseHardwareRayTracedDirectLighting(ViewFamily); const bool bUseBatchedShadows = CVarLumenDirectLightingBatchShadows.GetValueOnAnyThread() != 0; const bool bUseLightTilesPerLightType = LumenSceneDirectLighting::UseLightTilesPerLightType(); constexpr int32 NumLightTypes = (int32)ELumenLightType::MAX; int32 BatchedLightCounts[NumLightTypes] = {}; for (auto LightIt = Scene->Lights.CreateConstIterator(); LightIt; ++LightIt) { const FLightSceneInfoCompact& LightSceneInfoCompact = *LightIt; const FLightSceneInfo* LightSceneInfo = LightSceneInfoCompact.LightSceneInfo; if (LightSceneInfo->ShouldRenderLightViewIndependent() && LightSceneInfo->Proxy->GetIndirectLightingScale() > 0.0f) { for (const FViewInfo& View : Views) { if (LightSceneInfo->ShouldRenderLight(View, true)) { const FLumenGatheredLight GatheredLight(Scene, Views, FrameTemporaries, LightSceneInfo, /*LightIndex*/ TaskData->GatheredLights.Num()); if (GatheredLight.NeedsShadowMask()) { if (bUseBatchedShadows && GatheredLight.CanUseBatchedShadows()) { ++BatchedLightCounts[(int32)GatheredLight.Type]; } else { TaskData->StandaloneLightIndices.Add(GatheredLight.LightIndex); } } TaskData->bHasIESLights |= LightSceneInfo->Proxy->GetIESTexture() != nullptr; TaskData->bHasRectLights |= GatheredLight.Type == ELumenLightType::Rect; TaskData->bHasLightFunctions |= GatheredLight.LightFunctionMaterialProxy != nullptr; TaskData->GatheredLights.Add(GatheredLight); break; } } } } TaskData->PackedLightData.SetNum(FMath::RoundUpToPowerOfTwo(FMath::Max(TaskData->GatheredLights.Num(), 16))); TaskData->LightInfluenceSpheres.SetNum(FMath::RoundUpToPowerOfTwo(FMath::Max(TaskData->GatheredLights.Num(), 16))); int32 NumViewOrigins = FrameTemporaries.ViewOrigins.Num(); TaskData->ViewBatchedLightParameters.SetNum(NumViewOrigins); for (FViewBatchedLightParameters& ViewLightParameters : TaskData->ViewBatchedLightParameters) { for (int32 LightTypeIndex = 0; LightTypeIndex < NumLightTypes; ++LightTypeIndex) { if (bUseLightTilesPerLightType && BatchedLightCounts[LightTypeIndex] > 0) { if (!TaskData->DummyLightUniformBuffer) { FDeferredLightUniformStruct DeferredLightUniforms; FMemory::Memset(&DeferredLightUniforms, 0, sizeof(FDeferredLightUniformStruct)); TaskData->DummyLightUniformBuffer = CreateUniformBufferImmediate(DeferredLightUniforms, UniformBuffer_SingleFrame); } FPerLightParameters& LightParameters = ViewLightParameters.PerLightTypeParameters[LightTypeIndex].AddZeroed_GetRef(); LightParameters.DeferredLightUniforms = TaskData->DummyLightUniformBuffer; } else { ViewLightParameters.PerLightTypeParameters[LightTypeIndex].Empty(BatchedLightCounts[LightTypeIndex]); } } } for (int32 LightIndex = 0; LightIndex < TaskData->GatheredLights.Num(); ++LightIndex) { const FLumenGatheredLight& LumenLight = TaskData->GatheredLights[LightIndex]; const FLightSceneInfo* LightSceneInfo = LumenLight.LightSceneInfo; const FSphere LightBounds = LightSceneInfo->Proxy->GetBoundingSphere(); FVector4f& LightInfluenceSphere = TaskData->LightInfluenceSpheres[LightIndex]; LightInfluenceSphere = FVector4f(FVector3f(LightBounds.Center), LightBounds.W); FLightRenderParameters ShaderParameters; LightSceneInfo->Proxy->GetLightShaderParameters(ShaderParameters); const uint8 LightType = LightSceneInfo->Proxy->GetLightType(); if (LightType == LightType_Directional && LightSceneInfo->Proxy->GetUsePerPixelAtmosphereTransmittance()) { // When using PerPixelTransmittance, transmittance is evaluated per pixel by sampling the transmittance texture. It gives better gradient on large scale objects such as mountains. // However, to skip doing that texture sampling in Lumen card lighting or having the lumen shadow cache forced to be colored, we use the simple planet top ground transmittance as a simplification. // That will work for most of the cases for most of the map/terrain at the top of the virtual planet. ShaderParameters.Color *= LightSceneInfo->Proxy->GetAtmosphereTransmittanceTowardSun(); } if (LightSceneInfo->Proxy->IsInverseSquared()) { ShaderParameters.FalloffExponent = 0; } ShaderParameters.Color *= LightSceneInfo->Proxy->GetIndirectLightingScale(); // InverseExposureBlend applied in shader since it's view dependent FDFVector3 WorldPositionDF { ShaderParameters.WorldPosition }; FLumenPackedLight& LightData = TaskData->PackedLightData[LightIndex]; LightData.WorldPositionHigh = WorldPositionDF.High; LightData.LightingChannelMask = LightSceneInfo->Proxy->GetLightingChannelMask(); LightData.WorldPositionLow = WorldPositionDF.Low; LightData.InvRadius = ShaderParameters.InvRadius; LightData.Color = FVector3f(ShaderParameters.Color); LightData.FalloffExponent = ShaderParameters.FalloffExponent; LightData.Direction = ShaderParameters.Direction; LightData.DiffuseAndSpecularScale = PackRG16(ShaderParameters.DiffuseScale, ShaderParameters.SpecularScale); LightData.Tangent = ShaderParameters.Tangent; LightData.SourceRadius = ShaderParameters.SourceRadius; LightData.SpotAngles = ShaderParameters.SpotAngles; LightData.SoftSourceRadius = ShaderParameters.SoftSourceRadius; LightData.SourceLength = ShaderParameters.SourceLength; LightData.RectLightBarnCosAngle = ShaderParameters.RectLightBarnCosAngle; LightData.RectLightBarnLength = ShaderParameters.RectLightBarnLength; LightData.RectLightAtlasMaxLevel = ShaderParameters.RectLightAtlasMaxLevel > 0.0f ? ShaderParameters.RectLightAtlasMaxLevel : 0.0f; LightData.LightType = LightType; if (LightData.LightType == LightType_Rect) { LightData.SinCosConeAngleOrRectLightAtlasUVScale = ShaderParameters.RectLightAtlasUVScale; } else { LightData.SinCosConeAngleOrRectLightAtlasUVScale = FVector2f(FMath::Sin(LightSceneInfo->Proxy->GetOuterConeAngle()), FMath::Cos(LightSceneInfo->Proxy->GetOuterConeAngle())); } LightData.RectLightAtlasUVOffset = ShaderParameters.RectLightAtlasUVOffset; LightData.IESAtlasIndex = ShaderParameters.IESAtlasIndex; LightData.LightFunctionAtlasIndex_bHasShadowMask_bIsStandalone_bCastDynamicShadows = (ShaderParameters.LightFunctionAtlasLightIndex & 0x1FFFFFFFu) | ( LumenLight.NeedsShadowMask() ? (1 << 31) : 0) | (!LumenLight.CanUseBatchedShadows() ? (1 << 30) : 0) | (LumenLight.bHasShadows ? 1u << 29 : 0); LightData.InverseExposureBlend = ShaderParameters.InverseExposureBlend; LightData.Padding0 = 0; if (bUseBatchedShadows && !bUseLightTilesPerLightType && LumenLight.NeedsShadowMask() && LumenLight.CanUseBatchedShadows()) { for (int32 OriginIndex = 0; OriginIndex < NumViewOrigins; ++OriginIndex) { FPerLightParameters& LightParameters = TaskData->ViewBatchedLightParameters[OriginIndex].PerLightTypeParameters[(int32)LumenLight.Type].AddDefaulted_GetRef(); SetPerLightParameters(LightParameters, LumenLight, OriginIndex); } } } #if DO_CHECK for (FViewBatchedLightParameters& ViewLightParameters : TaskData->ViewBatchedLightParameters) { for (int32 LightTypeIndex = 0; LightTypeIndex < NumLightTypes; ++LightTypeIndex) { if (bUseLightTilesPerLightType) { check(ViewLightParameters.PerLightTypeParameters[LightTypeIndex].Num() == FMath::Min(BatchedLightCounts[LightTypeIndex], 1)); } else { check(ViewLightParameters.PerLightTypeParameters[LightTypeIndex].Num() == BatchedLightCounts[LightTypeIndex]); } } } #endif }, Prerequisites); } /////////////////////////////////////////////////////////////////////////////////////////////////// class FLumenSceneDirectLightingStatsCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FLumenSceneDirectLightingStatsCS) SHADER_USE_PARAMETER_STRUCT(FLumenSceneDirectLightingStatsCS, FGlobalShader) BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER(uint32, NumLights) SHADER_PARAMETER(uint32, NumViews) SHADER_PARAMETER(FIntPoint, AtlasResolution) SHADER_PARAMETER(FIntPoint, UpdateAtlasSize) SHADER_PARAMETER(uint32, MaxUpdateTiles) SHADER_PARAMETER(uint32, UpdateFactor) SHADER_PARAMETER(uint32, NumBatchedLights) SHADER_PARAMETER(uint32, NumStandaloneLights) SHADER_PARAMETER(uint32, bHasRectLights) SHADER_PARAMETER(uint32, bHasLightFunctionLights) SHADER_PARAMETER(uint32, bHasIESLights) SHADER_PARAMETER(uint32, bHWRT) SHADER_PARAMETER(uint32, bValidDebugData) // Scene SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, CardPageIndexAllocator) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, CardPageIndexData) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, CardTileAllocator) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, CardTiles) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FLumenCardScene, LumenCardScene) // Debug SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, DebugDataBuffer) // Traces SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, CompactedTraceAllocator) SHADER_PARAMETER_STRUCT_INCLUDE(ShaderPrint::FShaderParameters, ShaderPrintUniformBuffer) END_SHADER_PARAMETER_STRUCT() using FPermutationDomain = TShaderPermutationDomain<>; static int32 GetGroupSize() { return 8; } static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return DoesPlatformSupportLumenGI(Parameters.Platform); } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE"), GetGroupSize()); OutEnvironment.SetDefine(TEXT("SHADER_DEBUG"), 1); } static EShaderPermutationPrecacheRequest ShouldPrecachePermutation(const FGlobalShaderPermutationParameters& Parameters) { return EShaderPermutationPrecacheRequest::NotPrecached; } }; IMPLEMENT_GLOBAL_SHADER(FLumenSceneDirectLightingStatsCS, "/Engine/Private/Lumen/LumenSceneLightingDebug.usf", "LumenSceneDirectLightingStatsCS", SF_Compute); static void AddLumenSceneDirectLightingStatsPass( FRDGBuilder& GraphBuilder, const FScene* Scene, const FViewInfo& View, const FLumenSceneFrameTemporaries& FrameTemporaries, const FLumenDirectLightingTaskData* LightingTaskData, const FLumenCardUpdateContext& CardUpdateContext, const FLumenCardTileUpdateContext& CardTileUpdateContext, FRDGBufferRef CompactedTraceAllocator, ERDGPassFlags ComputePassFlags) { ShaderPrint::SetEnabled(true); ShaderPrint::RequestSpaceForCharacters(4096); ShaderPrint::RequestSpaceForLines(CardUpdateContext.MaxUpdateTiles * 12u * 2u); if (!ShaderPrint::IsEnabled(View.ShaderPrintData)) { return; } // Trace view ray to get debug info const bool bValidDebugData = FrameTemporaries.DebugData != nullptr; FRDGBufferSRVRef DebugData = bValidDebugData ? FrameTemporaries.DebugData : GraphBuilder.CreateSRV(GSystemTextures.GetDefaultStructuredBuffer(GraphBuilder, 4, 0u)); FLumenSceneDirectLightingStatsCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->NumLights = LightingTaskData->GatheredLights.Num(); PassParameters->NumViews = FrameTemporaries.ViewOrigins.Num(); PassParameters->AtlasResolution = FrameTemporaries.AlbedoAtlas->Desc.Extent; PassParameters->UpdateAtlasSize = CardUpdateContext.UpdateAtlasSize; PassParameters->MaxUpdateTiles = CardUpdateContext.MaxUpdateTiles; PassParameters->UpdateFactor = CardUpdateContext.UpdateFactor; PassParameters->bHWRT = Lumen::UseHardwareRayTracedDirectLighting(*View.Family) ? 1u : 0u; PassParameters->NumBatchedLights = LightingTaskData->GatheredLights.Num() - LightingTaskData->StandaloneLightIndices.Num(); PassParameters->NumStandaloneLights = LightingTaskData->StandaloneLightIndices.Num(); PassParameters->bHasRectLights = LightingTaskData->bHasRectLights ? 1u : 0u; PassParameters->bHasLightFunctionLights = LightingTaskData->bHasLightFunctions ? 1u : 0u; PassParameters->bHasIESLights = LightingTaskData->bHasIESLights ? 1u : 0u; PassParameters->CompactedTraceAllocator = GraphBuilder.CreateSRV(CompactedTraceAllocator); PassParameters->DebugDataBuffer = DebugData; PassParameters->bValidDebugData = bValidDebugData ? 1u : 0u; PassParameters->LumenCardScene = FrameTemporaries.LumenCardSceneUniformBuffer; PassParameters->CardPageIndexAllocator = GraphBuilder.CreateSRV(CardUpdateContext.CardPageIndexAllocator); PassParameters->CardPageIndexData = GraphBuilder.CreateSRV(CardUpdateContext.CardPageIndexData); PassParameters->CardTileAllocator = GraphBuilder.CreateSRV(CardTileUpdateContext.CardTileAllocator); PassParameters->CardTiles = GraphBuilder.CreateSRV(CardTileUpdateContext.CardTiles); ShaderPrint::SetParameters(GraphBuilder, View.ShaderPrintData, PassParameters->ShaderPrintUniformBuffer); FLumenSceneDirectLightingStatsCS::FPermutationDomain PermutationVector; auto ComputeShader = View.ShaderMap->GetShader(PermutationVector); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("LumenScene::Debug"), ComputeShader, PassParameters, FIntVector(1,1,1)); } /////////////////////////////////////////////////////////////////////////////////////////////////// // Stochastic direct lighting #include "LumenSceneDirectLightingStochastic.inl" /////////////////////////////////////////////////////////////////////////////////////////////////// void FDeferredShadingSceneRenderer::RenderDirectLightingForLumenScene( FRDGBuilder& GraphBuilder, const FLumenSceneFrameTemporaries& FrameTemporaries, const FLumenDirectLightingTaskData* LightingTaskData, const FLumenCardUpdateContext& CardUpdateContext, ERDGPassFlags ComputePassFlags) { LLM_SCOPE_BYTAG(Lumen); if (LightingTaskData) { LightingTaskData->Task.Wait(); } if (CVarLumenLumenSceneDirectLighting.GetValueOnRenderThread() != 0 && CardUpdateContext.MaxUpdateTiles > 0) { RDG_EVENT_SCOPE(GraphBuilder, "DirectLighting"); QUICK_SCOPE_CYCLE_COUNTER(RenderDirectLightingForLumenScene); check(LightingTaskData); const FViewInfo& MainView = Views[0]; FLumenSceneData& LumenSceneData = *Scene->GetLumenSceneData(Views[0]); int32 NumViewOrigins = FrameTemporaries.ViewOrigins.Num(); TRDGUniformBufferRef LumenCardSceneUniformBuffer = FrameTemporaries.LumenCardSceneUniformBuffer; TConstArrayView GatheredLights = LightingTaskData->GatheredLights; const bool bHasRectLights = LightingTaskData->bHasRectLights; FRDGBufferRef LumenPackedLights = CreateStructuredBuffer(GraphBuilder, TEXT("Lumen.DirectLighting.Lights"), LightingTaskData->PackedLightData, ERDGInitialDataFlags::NoCopy); FRDGBufferRef LumenLightInfluenceSpheres = CreateStructuredBuffer(GraphBuilder, TEXT("Lumen.DirectLighting.LightInfluenceSpheres"), LightingTaskData->LightInfluenceSpheres, ERDGInitialDataFlags::NoCopy); LumenSceneDirectLighting::FLightDataParameters LumenLightData; LumenLightData.LumenPackedLights = GraphBuilder.CreateSRV(LumenPackedLights); LumenLightData.LumenLightInfluenceSpheres = GraphBuilder.CreateSRV(LumenLightInfluenceSpheres); const bool bUseHardwareRayTracedDirectLighting = Lumen::UseHardwareRayTracedDirectLighting(ViewFamily); // Experimental Stochastic lighting path. if (LumenSceneDirectLighting::UseStochasticLighting(ViewFamily)) { ComputeStochasticLighting(GraphBuilder, Scene, Views[0], FrameTemporaries, LightingTaskData, CardUpdateContext, ComputePassFlags, LumenLightData); return; } FLightTileCullContext CullContext; FLumenCardTileUpdateContext CardTileUpdateContext; CullDirectLightingTiles(GraphBuilder, Views, FrameTemporaries, CardUpdateContext, LumenCardSceneUniformBuffer, GatheredLights, LightingTaskData->StandaloneLightIndices, LumenLightData, CullContext, CardTileUpdateContext, ComputePassFlags); // 8 bits per shadow mask texel. But if colored light function atlas is used, then 16bits per shadow mask texel. const uint32 ShadowMaskTilesSizeFactor = GetLightFunctionAtlasFormat() > 0 ? 2 : 1; const uint32 ShadowMaskTilesSize = FMath::Max(ShadowMaskTilesSizeFactor * 16 * CullContext.MaxCulledCardTiles, 1024u); FRDGBufferRef ShadowMaskTiles = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32), ShadowMaskTilesSize), TEXT("Lumen.DirectLighting.ShadowMaskTiles")); // 1 uint per packed shadow trace FRDGBufferRef ShadowTraceAllocator = nullptr; FRDGBufferRef ShadowTraces = nullptr; if (bUseHardwareRayTracedDirectLighting) { const uint32 MaxShadowTraces = FMath::Max(Lumen::CardTileSize * Lumen::CardTileSize * CullContext.MaxCulledCardTiles, 1024u); ShadowTraceAllocator = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32), 1), TEXT("Lumen.DirectLighting.ShadowTraceAllocator")); ShadowTraces = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32), MaxShadowTraces), TEXT("Lumen.DirectLighting.ShadowTraces")); AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(ShadowTraceAllocator), 0, ComputePassFlags); } // Compute shadow mask based on light attenuation (IES/LightFunction/Distance fall) to reduce need for shadow tracing done after. { SCOPED_NAMED_EVENT_TEXT("Light Attenuation ShadowMask ", FColor::Green); RDG_EVENT_SCOPE_FINAL(GraphBuilder, "Light Attenuation ShadowMask"); FRDGBufferUAVRef ShadowMaskTilesUAV = GraphBuilder.CreateUAV(ShadowMaskTiles, ERDGUnorderedAccessViewFlags::SkipBarrier); FRDGBufferUAVRef ShadowTraceAllocatorUAV = ShadowTraceAllocator ? GraphBuilder.CreateUAV(ShadowTraceAllocator, ERDGUnorderedAccessViewFlags::SkipBarrier) : nullptr; FRDGBufferUAVRef ShadowTracesUAV = ShadowTraces ? GraphBuilder.CreateUAV(ShadowTraces, ERDGUnorderedAccessViewFlags::SkipBarrier) : nullptr; FRDGBufferSRVRef TileShadowDownsampleFactorAtlasSRV = GraphBuilder.CreateSRV(FrameTemporaries.TileShadowDownsampleFactorAtlas, PF_R32_UINT); int32 NumShadowedLights = 0; for (int32 OriginIndex = 0; OriginIndex < NumViewOrigins; ++OriginIndex) { const FViewInfo& View = *FrameTemporaries.ViewOrigins[OriginIndex].ReferenceView; NumShadowedLights = ComputeShadowMaskFromLightAttenuation( GraphBuilder, Scene, View, LumenCardSceneUniformBuffer, GatheredLights, LightingTaskData->StandaloneLightIndices, LightingTaskData->ViewBatchedLightParameters[OriginIndex], CullContext.LightTileScatterParameters, LumenLightData, OriginIndex, NumViewOrigins, LightingTaskData->bHasLightFunctions, ShadowMaskTilesUAV, ShadowTraceAllocatorUAV, ShadowTracesUAV, TileShadowDownsampleFactorAtlasSRV, ComputePassFlags); } // Clear to mark resource as used if it wasn't ever written to if (ShadowTracesUAV && NumShadowedLights == 0) { AddClearUAVPass(GraphBuilder, ShadowTracesUAV, 0); } } FRDGBufferRef ShadowTraceIndirectArgs = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateIndirectDesc(1), TEXT("Lumen.DirectLighting.CompactedShadowTraceIndirectArgs")); if (ShadowTraceAllocator) { FInitShadowTraceIndirectArgsCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->RWShadowTraceIndirectArgs = GraphBuilder.CreateUAV(ShadowTraceIndirectArgs); PassParameters->ShadowTraceAllocator = GraphBuilder.CreateSRV(ShadowTraceAllocator); auto ComputeShader = Views[0].ShaderMap->GetShader(); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("InitShadowTraceIndirectArgs"), ComputePassFlags, ComputeShader, PassParameters, FIntVector(1, 1, 1)); } // Offscreen shadowing { SCOPED_NAMED_EVENT_TEXT("Offscreen shadows", FColor::Green); RDG_EVENT_SCOPE_FINAL(GraphBuilder, "Offscreen shadows"); FRDGBufferUAVRef ShadowMaskTilesUAV = GraphBuilder.CreateUAV(ShadowMaskTiles, ERDGUnorderedAccessViewFlags::SkipBarrier); FDistanceFieldObjectBufferParameters ObjectBufferParameters; if (!bUseHardwareRayTracedDirectLighting) { ObjectBufferParameters = DistanceField::SetupObjectBufferParameters(GraphBuilder, Scene->DistanceFieldSceneData); // Patch DF heightfields with Lumen heightfields ObjectBufferParameters.SceneHeightfieldObjectBounds = GraphBuilder.CreateSRV(GraphBuilder.RegisterExternalBuffer(LumenSceneData.HeightfieldBuffer)); ObjectBufferParameters.SceneHeightfieldObjectData = nullptr; ObjectBufferParameters.NumSceneHeightfieldObjects = LumenSceneData.Heightfields.Num(); } for (int32 OriginIndex = 0; OriginIndex < NumViewOrigins; ++OriginIndex) { const FViewInfo& View = *FrameTemporaries.ViewOrigins[OriginIndex].ReferenceView; if (bUseHardwareRayTracedDirectLighting) { FLumenDirectLightingStochasticData StochasticData; TraceLumenHardwareRayTracedDirectLightingShadows( GraphBuilder, Scene, View, OriginIndex, FrameTemporaries, StochasticData, LumenLightData, ShadowTraceIndirectArgs, ShadowTraceAllocator, ShadowTraces, CullContext.LightTileAllocator, CullContext.LightTiles, ShadowMaskTilesUAV, ComputePassFlags); } else { TraceDistanceFieldShadows( GraphBuilder, Scene, View, LumenCardSceneUniformBuffer, GatheredLights, LightingTaskData->StandaloneLightIndices, LightingTaskData->ViewBatchedLightParameters[OriginIndex], CullContext.LightTileScatterParameters, LumenLightData, ObjectBufferParameters, OriginIndex, NumViewOrigins, ShadowMaskTilesUAV, ComputePassFlags); } } } // Apply lights { RDG_EVENT_SCOPE(GraphBuilder, "Lights"); FRDGBufferSRVRef ShadowMaskTilesSRV = GraphBuilder.CreateSRV(ShadowMaskTiles->HasBeenProduced() ? ShadowMaskTiles : GSystemTextures.GetDefaultStructuredBuffer(GraphBuilder, sizeof(uint32))); FRDGBufferSRVRef CardTilesSRV = GraphBuilder.CreateSRV(CardTileUpdateContext.CardTiles); FRDGBufferSRVRef LightTileOffsetNumPerCardTileSRV = GraphBuilder.CreateSRV(CullContext.LightTileOffsetNumPerCardTile); FRDGBufferSRVRef LightTilesPerCardTileSRV = GraphBuilder.CreateSRV(CullContext.LightTilesPerCardTile); FRDGTextureUAVRef DirectLightingAtlasUAV = GraphBuilder.CreateUAV(FrameTemporaries.DirectLightingAtlas); RenderDirectLightIntoLumenCardsBatched( GraphBuilder, Views, FrameTemporaries, LumenCardSceneUniformBuffer, LumenLightData, ShadowMaskTilesSRV, CardTilesSRV, LightTileOffsetNumPerCardTileSRV, LightTilesPerCardTileSRV, DirectLightingAtlasUAV, CardTileUpdateContext.DispatchCardTilesIndirectArgs, bHasRectLights, ComputePassFlags); } // Update Final Lighting Lumen::CombineLumenSceneLighting( Scene, MainView, GraphBuilder, FrameTemporaries, CardUpdateContext, CardTileUpdateContext, ComputePassFlags); // Draw direct lighting stats & Lumen cards/tiles if (GetLumenLightingStatMode() == 3) { AddLumenSceneDirectLightingStatsPass( GraphBuilder, Scene, MainView, FrameTemporaries, LightingTaskData, CardUpdateContext, CardTileUpdateContext, ShadowTraceAllocator, ComputePassFlags); } } else if (CVarLumenLumenSceneDirectLighting.GetValueOnRenderThread() == 0) { AddClearRenderTargetPass(GraphBuilder, FrameTemporaries.DirectLightingAtlas); } }