// Copyright Epic Games, Inc. All Rights Reserved. #include "LumenSceneLighting.h" #include "RendererPrivate.h" #include "ScenePrivate.h" #include "PipelineStateCache.h" #include "ShaderParameterStruct.h" #include "SceneTextureParameters.h" #include "LumenMeshCards.h" #include "LumenRadianceCache.h" #include "ProfilingDebugging/CpuProfilerTrace.h" #include "LumenTracingUtils.h" #include "ShaderPrintParameters.h" #include "LumenRadiosity.h" static TAutoConsoleVariable CVarLumenSceneLightingForceFullUpdate( TEXT("r.LumenScene.Lighting.ForceLightingUpdate"), 0, TEXT("Force full Lumen Scene Lighting update every frame. Useful for debugging"), ECVF_RenderThreadSafe ); int32 GLumenSceneLightingFeedback = 1; FAutoConsoleVariableRef CVarLumenSceneLightingFeedback( TEXT("r.LumenScene.Lighting.Feedback"), GLumenSceneLightingFeedback, TEXT("Whether to prioritize surface cache lighting updates based on the feedback."), ECVF_Scalability | ECVF_RenderThreadSafe ); static TAutoConsoleVariable CVarLumenSceneLightingMaxDistanceFromFrustumToPrioritize( TEXT("r.LumenScene.Lighting.MaxDistanceFromFrustumToPrioritize"), 500.0f, TEXT("Max distance from view frustum to card page extents to prioritize this card updates.\n") TEXT("-1 to disable this feature."), ECVF_Scalability | ECVF_RenderThreadSafe ); int32 GLumenDirectLightingUpdateFactor = 32; FAutoConsoleVariableRef CVarLumenSceneDirectLightingUpdateFactor( TEXT("r.LumenScene.DirectLighting.UpdateFactor"), GLumenDirectLightingUpdateFactor, TEXT("Controls for how many texels direct lighting will be updated every frame. Texels = SurfaceCacheTexels / Factor."), ECVF_Scalability | ECVF_RenderThreadSafe ); int32 GLumenRadiosityUpdateFactor = 64; FAutoConsoleVariableRef CVarLumenSceneRadiosityUpdateFactor( TEXT("r.LumenScene.Radiosity.UpdateFactor"), GLumenRadiosityUpdateFactor, TEXT("Controls for how many texels radiosity will be updated every frame. Texels = SurfaceCacheTexels / Factor."), ECVF_Scalability | ECVF_RenderThreadSafe ); int32 GLumenLightingStats = 0; FAutoConsoleVariableRef CVarLumenSceneLightingStats( TEXT("r.LumenScene.Lighting.Stats"), GLumenLightingStats, TEXT("GPU print out Lumen lighting update stats."), ECVF_Scalability | ECVF_RenderThreadSafe ); static TAutoConsoleVariable CVarLumenSceneLightingAsyncCompute( TEXT("r.LumenScene.Lighting.AsyncCompute"), 1, TEXT("Whether to run LumenSceneLighting on the compute pipe if possible."), ECVF_Scalability | ECVF_RenderThreadSafe ); uint32 GetLumenLightingStatMode() { return FMath::Clamp(GLumenLightingStats, 0, 3); } namespace LumenSceneLighting { bool UseFeedback(const FSceneViewFamily& ViewFamily) { return Lumen::UseHardwareRayTracing(ViewFamily) && GLumenSceneLightingFeedback != 0; } } bool Lumen::UseHardwareRayTracedSceneLighting(const FSceneViewFamily& ViewFamily) { return Lumen::UseHardwareRayTracedDirectLighting(ViewFamily) || Lumen::UseHardwareRayTracedRadiosity(ViewFamily); } namespace LumenCardUpdateContext { // Must match LumenSceneLighting.usf constexpr uint32 CARD_UPDATE_CONTEXT_MAX = 2; constexpr uint32 PRIORITY_HISTOGRAM_SIZE = 16; constexpr uint32 MAX_UPDATE_BUCKET_STRIDE = 2; constexpr uint32 CARD_PAGE_TILE_ALLOCATOR_STRIDE = 2; }; void SetLightingUpdateAtlasSize(FIntPoint PhysicalAtlasSize, int32 UpdateFactor, FLumenCardUpdateContext& Context) { Context.UpdateAtlasSize = FIntPoint(0, 0); Context.MaxUpdateTiles = 0; Context.UpdateFactor = FMath::Clamp(UpdateFactor, 1, 1024); if (!Lumen::IsSurfaceCacheFrozen()) { if (CVarLumenSceneLightingForceFullUpdate.GetValueOnRenderThread() != 0) { Context.UpdateFactor = 1; } const float MultPerComponent = 1.0f / FMath::Sqrt((float)Context.UpdateFactor); FIntPoint UpdateAtlasSize; UpdateAtlasSize.X = FMath::DivideAndRoundUp(PhysicalAtlasSize.X * MultPerComponent + 0.5f, Lumen::CardTileSize) * Lumen::CardTileSize; UpdateAtlasSize.Y = FMath::DivideAndRoundUp(PhysicalAtlasSize.Y * MultPerComponent + 0.5f, Lumen::CardTileSize) * Lumen::CardTileSize; // Update at least one full res card page so that we don't get stuck UpdateAtlasSize.X = FMath::Max(UpdateAtlasSize.X, Lumen::PhysicalPageSize); UpdateAtlasSize.Y = FMath::Max(UpdateAtlasSize.Y, Lumen::PhysicalPageSize); const FIntPoint UpdateAtlasSizeInTiles = UpdateAtlasSize / Lumen::CardTileSize; Context.UpdateAtlasSize = UpdateAtlasSize; Context.MaxUpdateTiles = UpdateAtlasSizeInTiles.X * UpdateAtlasSizeInTiles.Y; } } IMPLEMENT_GLOBAL_SHADER(FClearLumenCardsPS, "/Engine/Private/Lumen/LumenSceneLighting.usf", "ClearLumenCardsPS", SF_Pixel); IMPLEMENT_GLOBAL_SHADER(FCopyCardCaptureLightingToAtlasPS, "/Engine/Private/Lumen/LumenSceneLighting.usf", "CopyCardCaptureLightingToAtlasPS", SF_Pixel); bool FRasterizeToCardsVS::ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return DoesPlatformSupportLumenGI(Parameters.Platform); } IMPLEMENT_GLOBAL_SHADER(FRasterizeToCardsVS,"/Engine/Private/Lumen/LumenSceneLighting.usf","RasterizeToCardsVS",SF_Vertex); class FLumenCardCombineLightingCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FLumenCardCombineLightingCS); SHADER_USE_PARAMETER_STRUCT(FLumenCardCombineLightingCS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) RDG_BUFFER_ACCESS(IndirectArgsBuffer, ERHIAccess::IndirectArgs) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FLumenCardScene, LumenCardScene) SHADER_PARAMETER(float, DiffuseColorBoost) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, AlbedoAtlas) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, OpacityAtlas) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, EmissiveAtlas) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, DirectLightingAtlas) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, IndirectLightingAtlas) SHADER_PARAMETER_SAMPLER(SamplerState, BilinearClampedSampler) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, CardTiles) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, RWFinalLightingAtlas) SHADER_PARAMETER(FVector2f, IndirectLightingAtlasHalfTexelSize) SHADER_PARAMETER(FVector3f, TargetFormatQuantizationError) END_SHADER_PARAMETER_STRUCT() using FPermutationDomain = TShaderPermutationDomain<>; static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return DoesPlatformSupportLumenGI(Parameters.Platform); } }; IMPLEMENT_GLOBAL_SHADER(FLumenCardCombineLightingCS, "/Engine/Private/Lumen/LumenSceneLighting.usf", "CombineLumenSceneLightingCS", SF_Compute); void Lumen::CombineLumenSceneLighting( FScene* Scene, const FViewInfo& View, FRDGBuilder& GraphBuilder, const FLumenSceneFrameTemporaries& FrameTemporaries, const FLumenCardUpdateContext& CardUpdateContext, const FLumenCardTileUpdateContext& CardTileUpdateContext, ERDGPassFlags ComputePassFlags) { LLM_SCOPE_BYTAG(Lumen); FLumenSceneData& LumenSceneData = *Scene->GetLumenSceneData(View); FLumenCardCombineLightingCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->IndirectArgsBuffer = CardTileUpdateContext.DispatchCardTilesIndirectArgs; PassParameters->View = View.ViewUniformBuffer; PassParameters->LumenCardScene = FrameTemporaries.LumenCardSceneUniformBuffer; PassParameters->DiffuseColorBoost = 1.0f / FMath::Max(View.FinalPostProcessSettings.LumenDiffuseColorBoost, 1.0f); PassParameters->AlbedoAtlas = FrameTemporaries.AlbedoAtlas; PassParameters->OpacityAtlas = FrameTemporaries.OpacityAtlas; PassParameters->EmissiveAtlas = FrameTemporaries.EmissiveAtlas; PassParameters->DirectLightingAtlas = FrameTemporaries.DirectLightingAtlas; PassParameters->IndirectLightingAtlas = FrameTemporaries.IndirectLightingAtlas; PassParameters->BilinearClampedSampler = TStaticSamplerState::GetRHI(); PassParameters->CardTiles = GraphBuilder.CreateSRV(CardTileUpdateContext.CardTiles); PassParameters->RWFinalLightingAtlas = GraphBuilder.CreateUAV(FrameTemporaries.FinalLightingAtlas); const FIntPoint IndirectLightingAtlasSize = LumenSceneData.GetRadiosityAtlasSize(); PassParameters->IndirectLightingAtlasHalfTexelSize = FVector2f(0.5f / IndirectLightingAtlasSize.X, 0.5f / IndirectLightingAtlasSize.Y); PassParameters->TargetFormatQuantizationError = Lumen::GetLightingQuantizationError(); auto ComputeShader = View.ShaderMap->GetShader(); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("CombineLighting CS"), ComputePassFlags, ComputeShader, PassParameters, CardTileUpdateContext.DispatchCardTilesIndirectArgs, (uint32)ELumenDispatchCardTilesIndirectArgsOffset::OneGroupPerCardTile); } bool LumenSceneLighting::UseAsyncCompute(const FViewFamilyInfo& ViewFamily) { return Lumen::UseAsyncCompute(ViewFamily) && CVarLumenSceneLightingAsyncCompute.GetValueOnRenderThread() != 0; } DECLARE_GPU_STAT(LumenSceneLighting); void FDeferredShadingSceneRenderer::RenderLumenSceneLighting( FRDGBuilder& GraphBuilder, const FLumenSceneFrameTemporaries& FrameTemporaries, const FLumenDirectLightingTaskData* DirectLightingTaskData) { LLM_SCOPE_BYTAG(Lumen); TRACE_CPUPROFILER_EVENT_SCOPE(FDeferredShadingSceneRenderer::RenderLumenSceneLighting); FLumenSceneData& LumenSceneData = *Scene->GetLumenSceneData(Views[0]); bool bAnyLumenActive = false; for (const FViewInfo& View : Views) { const FPerViewPipelineState& ViewPipelineState = GetViewPipelineState(View); bAnyLumenActive = bAnyLumenActive || ViewPipelineState.DiffuseIndirectMethod == EDiffuseIndirectMethod::Lumen; } if (bAnyLumenActive) { TRACE_CPUPROFILER_EVENT_SCOPE(RenderLumenSceneLighting); QUICK_SCOPE_CYCLE_COUNTER(RenderLumenSceneLighting); RDG_EVENT_SCOPE_STAT(GraphBuilder, LumenSceneLighting, "LumenSceneLighting%s", LumenCardRenderer.bPropagateGlobalLightingChange ? TEXT(" PROPAGATE GLOBAL CHANGE!") : TEXT("")); RDG_GPU_STAT_SCOPE(GraphBuilder, LumenSceneLighting); const ERDGPassFlags ComputePassFlags = LumenSceneLighting::UseAsyncCompute(ViewFamily) ? ERDGPassFlags::AsyncCompute : ERDGPassFlags::Compute; LumenSceneData.IncrementSurfaceCacheUpdateFrameIndex(); if (LumenSceneData.bDebugClearAllCachedState) { AddClearRenderTargetPass(GraphBuilder, FrameTemporaries.DirectLightingAtlas); AddClearRenderTargetPass(GraphBuilder, FrameTemporaries.IndirectLightingAtlas); AddClearRenderTargetPass(GraphBuilder, FrameTemporaries.RadiosityNumFramesAccumulatedAtlas); AddClearRenderTargetPass(GraphBuilder, FrameTemporaries.FinalLightingAtlas); if (FrameTemporaries.DiffuseLightingAndSecondMomentHistoryAtlas) { AddClearRenderTargetPass(GraphBuilder, FrameTemporaries.DiffuseLightingAndSecondMomentHistoryAtlas); } if (FrameTemporaries.NumFramesAccumulatedHistoryAtlas) { AddClearRenderTargetPass(GraphBuilder, FrameTemporaries.NumFramesAccumulatedHistoryAtlas); } } LumenRadiosity::FFrameTemporaries RadiosityFrameTemporaries; LumenRadiosity::InitFrameTemporaries(GraphBuilder, LumenSceneData, ViewFamily, Views, RadiosityFrameTemporaries); FLumenCardUpdateContext DirectLightingCardUpdateContext; FLumenCardUpdateContext IndirectLightingCardUpdateContext; Lumen::BuildCardUpdateContext( GraphBuilder, LumenSceneData, Views, FrameTemporaries, RadiosityFrameTemporaries.bIndirectLightingHistoryValid, DirectLightingCardUpdateContext, IndirectLightingCardUpdateContext, ComputePassFlags); // Pointing cards debug data if (GetLumenLightingStatMode() > 2) { FLumenSceneFrameTemporaries* NonCstFrameTemporaries = const_cast(&FrameTemporaries); NonCstFrameTemporaries->DebugData = TraceLumenHardwareRayTracedDebug(GraphBuilder, Scene, Views[0], 0 /*ViewIndex*/, FrameTemporaries, ComputePassFlags); } RenderDirectLightingForLumenScene( GraphBuilder, FrameTemporaries, DirectLightingTaskData, DirectLightingCardUpdateContext, ComputePassFlags); RenderRadiosityForLumenScene( GraphBuilder, FrameTemporaries, RadiosityFrameTemporaries, IndirectLightingCardUpdateContext, ComputePassFlags); LumenSceneData.bFinalLightingAtlasContentsValid = true; GraphBuilder.FlushSetupQueue(); } } class FClearCardUpdateContextCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FClearCardUpdateContextCS); SHADER_USE_PARAMETER_STRUCT(FClearCardUpdateContextCS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWDirectLightingCardPageIndexAllocator) SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWIndirectLightingCardPageIndexAllocator) SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWMaxUpdateBucket) SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWCardPageTileAllocator) SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWPriorityHistogram) 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(FClearCardUpdateContextCS, "/Engine/Private/Lumen/LumenSceneLighting.usf", "ClearCardUpdateContextCS", SF_Compute); BEGIN_SHADER_PARAMETER_STRUCT(FLumenSurfaceCacheUpdateParameters, ) SHADER_PARAMETER(float, MaxDistanceFromFrustumToPrioritize) SHADER_PARAMETER(uint32, NumCameraOrigins) SHADER_PARAMETER_ARRAY(FVector4f, WorldCameraOrigins, [LUMEN_MAX_VIEWS]) SHADER_PARAMETER_ARRAY(FVector4f, CameraFrustumPlanes, [LUMEN_MAX_VIEWS * LUMEN_MAX_CAMERA_PLANES]) SHADER_PARAMETER_ARRAY(FVector4f, PreViewTranslationHigh, [LUMEN_MAX_VIEWS]) SHADER_PARAMETER_ARRAY(FVector4f, PreViewTranslationLow, [LUMEN_MAX_VIEWS]) END_SHADER_PARAMETER_STRUCT() class FBuildPageUpdatePriorityHistogramCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FBuildPageUpdatePriorityHistogramCS); SHADER_USE_PARAMETER_STRUCT(FBuildPageUpdatePriorityHistogramCS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWPriorityHistogram) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FLumenCardScene, LumenCardScene) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, CardPageLastUsedBuffer) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, CardPageHighResLastUsedBuffer) SHADER_PARAMETER_STRUCT_INCLUDE(FLumenSurfaceCacheUpdateParameters, SurfaceCacheUpdateParameters) SHADER_PARAMETER(uint32, SurfaceCacheUpdateFrameIndex) SHADER_PARAMETER(uint32, FreezeUpdateFrame) SHADER_PARAMETER(uint32, CardPageNum) SHADER_PARAMETER(float, FirstClipmapWorldExtentRcp) SHADER_PARAMETER(float, DirectLightingUpdateFactor) SHADER_PARAMETER(float, IndirectLightingUpdateFactor) END_SHADER_PARAMETER_STRUCT() class FSurfaceCacheFeedback : SHADER_PERMUTATION_BOOL("SURFACE_CACHE_FEEDBACK"); 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 64; } }; IMPLEMENT_GLOBAL_SHADER(FBuildPageUpdatePriorityHistogramCS, "/Engine/Private/Lumen/LumenSceneLighting.usf", "BuildPageUpdatePriorityHistogramCS", SF_Compute); class FSelectMaxUpdateBucketCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FSelectMaxUpdateBucketCS); SHADER_USE_PARAMETER_STRUCT(FSelectMaxUpdateBucketCS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWMaxUpdateBucket) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, PriorityHistogram) SHADER_PARAMETER(uint32, MaxDirectLightingTilesToUpdate) SHADER_PARAMETER(uint32, MaxIndirectLightingTilesToUpdate) SHADER_PARAMETER(uint32, SurfaceCacheUpdateFrameIndex) SHADER_PARAMETER(uint32, FreezeUpdateFrame) 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(FSelectMaxUpdateBucketCS, "/Engine/Private/Lumen/LumenSceneLighting.usf", "SelectMaxUpdateBucketCS", SF_Compute); class FBuildCardsUpdateListCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FBuildCardsUpdateListCS); SHADER_USE_PARAMETER_STRUCT(FBuildCardsUpdateListCS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWDirectLightingCardPageIndexAllocator) SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWDirectLightingCardPageIndexData) SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWIndirectLightingCardPageIndexAllocator) SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWIndirectLightingCardPageIndexData) SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWCardPageTileAllocator) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, MaxUpdateBucket) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, LumenCardDataBuffer) SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWLumenCardPageDataBuffer) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, CardPageLastUsedBuffer) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, CardPageHighResLastUsedBuffer) SHADER_PARAMETER_STRUCT_INCLUDE(FLumenSurfaceCacheUpdateParameters, SurfaceCacheUpdateParameters) SHADER_PARAMETER(uint32, SurfaceCacheUpdateFrameIndex) SHADER_PARAMETER(uint32, FreezeUpdateFrame) SHADER_PARAMETER(uint32, CardPageNum) SHADER_PARAMETER(float, FirstClipmapWorldExtentRcp) SHADER_PARAMETER(uint32, MaxDirectLightingTilesToUpdate) SHADER_PARAMETER(uint32, MaxIndirectLightingTilesToUpdate) SHADER_PARAMETER(float, DirectLightingUpdateFactor) SHADER_PARAMETER(float, IndirectLightingUpdateFactor) SHADER_PARAMETER(int32, IndirectLightingHistoryValid) END_SHADER_PARAMETER_STRUCT() class FSurfaceCacheFeedback : SHADER_PERMUTATION_BOOL("SURFACE_CACHE_FEEDBACK"); 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.SetDefine(TEXT("USE_LUMEN_CARD_DATA_BUFFER"), 1); OutEnvironment.SetDefine(TEXT("USE_RW_LUMEN_CARD_PAGE_DATA_BUFFER"), 1); } static int32 GetGroupSize() { return 64; } }; IMPLEMENT_GLOBAL_SHADER(FBuildCardsUpdateListCS, "/Engine/Private/Lumen/LumenSceneLighting.usf", "BuildCardsUpdateListCS", SF_Compute); class FSetCardPageIndexIndirectArgsCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FSetCardPageIndexIndirectArgsCS); SHADER_USE_PARAMETER_STRUCT(FSetCardPageIndexIndirectArgsCS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, RWDirectLightingDrawCardPageIndicesIndirectArgs) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, RWDirectLightingDispatchCardPageIndicesIndirectArgs) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, RWIndirectLightingDrawCardPageIndicesIndirectArgs) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, RWIndirectLightingDispatchCardPageIndicesIndirectArgs) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, DirectLightingCardPageIndexAllocator) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, IndirectLightingCardPageIndexAllocator) SHADER_PARAMETER(uint32, VertexCountPerInstanceIndirect) 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(FSetCardPageIndexIndirectArgsCS, "/Engine/Private/Lumen/LumenSceneLighting.usf", "SetCardPageIndexIndirectArgsCS", SF_Compute); class FLumenSceneLightingStatsCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FLumenSceneLightingStatsCS) SHADER_USE_PARAMETER_STRUCT(FLumenSceneLightingStatsCS, FGlobalShader) BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_INCLUDE(ShaderPrint::FShaderParameters, ShaderPrintUniformBuffer) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FLumenCardScene, LumenCardScene) SHADER_PARAMETER(uint32, CardPageNum) SHADER_PARAMETER(uint32, LightingStatMode) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, DirectLightingCardPageIndexAllocator) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, IndirectLightingCardPageIndexAllocator) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, PriorityHistogram) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, MaxUpdateBucket) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, CardPageTileAllocator) END_SHADER_PARAMETER_STRUCT() public: 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"), 1); OutEnvironment.SetDefine(TEXT("SHADER_STATS"), 1); } }; IMPLEMENT_GLOBAL_SHADER(FLumenSceneLightingStatsCS, "/Engine/Private/Lumen/LumenSceneLightingDebug.usf", "LumenSceneLightingStatsCS", SF_Compute); void Lumen::BuildCardUpdateContext( FRDGBuilder& GraphBuilder, const FLumenSceneData& LumenSceneData, const TArray& Views, const FLumenSceneFrameTemporaries& FrameTemporaries, bool bIndirectLightingHistoryValid, FLumenCardUpdateContext& DirectLightingCardUpdateContext, FLumenCardUpdateContext& IndirectLightingCardUpdateContext, ERDGPassFlags ComputePassFlags) { RDG_EVENT_SCOPE(GraphBuilder, "BuildCardUpdateContext"); float LumenSceneLightingUpdateSpeed = 0.0f; for (const FViewInfo& View : Views) { LumenSceneLightingUpdateSpeed = FMath::Max(LumenSceneLightingUpdateSpeed, FMath::Clamp(View.FinalPostProcessSettings.LumenSceneLightingUpdateSpeed, .5f, 16.0f)); } FRDGBufferSRV* CardPageLastUsedBufferSRV = nullptr; FRDGBufferSRV* CardPageHighResLastUsedBufferSRV = nullptr; const bool bUseFeedback = FrameTemporaries.CardPageLastUsedBufferSRV && FrameTemporaries.CardPageHighResLastUsedBufferSRV && LumenSceneLighting::UseFeedback(*Views[0].Family); if (bUseFeedback) { CardPageLastUsedBufferSRV = FrameTemporaries.CardPageLastUsedBufferSRV; CardPageHighResLastUsedBufferSRV = FrameTemporaries.CardPageHighResLastUsedBufferSRV; } const int32 NumCardPages = LumenSceneData.GetNumCardPages(); const uint32 UpdateFrameIndex = LumenSceneData.GetSurfaceCacheUpdateFrameIndex(); const uint32 FreezeUpdateFrame = Lumen::IsSurfaceCacheUpdateFrameFrozen() ? 1 : 0; const float FirstClipmapWorldExtentRcp = 1.0f / Lumen::GetGlobalDFClipmapExtent(0); SetLightingUpdateAtlasSize(LumenSceneData.GetPhysicalAtlasSize(), FMath::RoundToInt(GLumenDirectLightingUpdateFactor / LumenSceneLightingUpdateSpeed), DirectLightingCardUpdateContext); SetLightingUpdateAtlasSize(LumenSceneData.GetPhysicalAtlasSize(), FMath::RoundToInt(GLumenRadiosityUpdateFactor / LumenSceneLightingUpdateSpeed), IndirectLightingCardUpdateContext); DirectLightingCardUpdateContext.CardPageIndexAllocator = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32), 1), TEXT("Lumen.DirectLightingCardPageIndexAllocator")); DirectLightingCardUpdateContext.CardPageIndexData = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32), FMath::RoundUpToPowerOfTwo(NumCardPages)), TEXT("Lumen.DirectLightingCardPageIndexData")); DirectLightingCardUpdateContext.DrawCardPageIndicesIndirectArgs = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateIndirectDesc(1), TEXT("Lumen.DirectLighting.DrawCardPageIndicesIndirectArgs")); DirectLightingCardUpdateContext.DispatchCardPageIndicesIndirectArgs = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateIndirectDesc(FLumenCardUpdateContext::MAX), TEXT("Lumen.DirectLighting.DispatchCardPageIndicesIndirectArgs")); IndirectLightingCardUpdateContext.CardPageIndexAllocator = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32), 1), TEXT("Lumen.IndirectLightingCardPageIndexAllocator")); IndirectLightingCardUpdateContext.CardPageIndexData = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32), FMath::RoundUpToPowerOfTwo(NumCardPages)), TEXT("Lumen.IndirectLightingCardPageIndexData")); IndirectLightingCardUpdateContext.DrawCardPageIndicesIndirectArgs = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateIndirectDesc(1), TEXT("Lumen.IndirectLighting.DrawCardPageIndicesIndirectArgs")); IndirectLightingCardUpdateContext.DispatchCardPageIndicesIndirectArgs = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateIndirectDesc(FLumenCardUpdateContext::MAX), TEXT("Lumen.IndirectLighting.DispatchCardPageIndicesIndirectArgs")); FRDGBufferRef PriorityHistogram = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32), LumenCardUpdateContext::CARD_UPDATE_CONTEXT_MAX * LumenCardUpdateContext::PRIORITY_HISTOGRAM_SIZE), TEXT("Lumen.PriorityHistogram")); FRDGBufferRef MaxUpdateBucket = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32), LumenCardUpdateContext::CARD_UPDATE_CONTEXT_MAX * LumenCardUpdateContext::MAX_UPDATE_BUCKET_STRIDE), TEXT("Lumen.MaxUpdateBucket")); FRDGBufferRef CardPageTileAllocator = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32), LumenCardUpdateContext::CARD_UPDATE_CONTEXT_MAX * LumenCardUpdateContext::CARD_PAGE_TILE_ALLOCATOR_STRIDE), TEXT("Lumen.CardPageTileAllocator")); FRDGBufferUAV* MaxUpdateBucketUAV = GraphBuilder.CreateUAV(MaxUpdateBucket); FRDGBufferSRV* MaxUpdateBucketSRV = GraphBuilder.CreateSRV(MaxUpdateBucket); FRDGBufferUAV* DirectCardPageIndexAllocatorUAV = GraphBuilder.CreateUAV(DirectLightingCardUpdateContext.CardPageIndexAllocator); FRDGBufferSRV* DirectCardPageIndexAllocatorSRV = GraphBuilder.CreateSRV(DirectLightingCardUpdateContext.CardPageIndexAllocator); FRDGBufferUAV* CardPageTileAllocatorUAV = GraphBuilder.CreateUAV(CardPageTileAllocator); FRDGBufferSRV* CardPageTileAllocatorSRV = GraphBuilder.CreateSRV(CardPageTileAllocator); FRDGBufferUAV* IndirectCardPageIndexAllocatorUAV = GraphBuilder.CreateUAV(IndirectLightingCardUpdateContext.CardPageIndexAllocator); FRDGBufferSRV* IndirectCardPageIndexAllocatorSRV = GraphBuilder.CreateSRV(IndirectLightingCardUpdateContext.CardPageIndexAllocator); FRDGBufferUAV* PriorityHistogramUAV = GraphBuilder.CreateUAV(PriorityHistogram); FRDGBufferSRV* PriorityHistogramSRV = GraphBuilder.CreateSRV(PriorityHistogram); // Batch clear all resources required for the subsequent card context update pass { FClearCardUpdateContextCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->RWDirectLightingCardPageIndexAllocator = DirectCardPageIndexAllocatorUAV; PassParameters->RWIndirectLightingCardPageIndexAllocator = IndirectCardPageIndexAllocatorUAV; PassParameters->RWMaxUpdateBucket = MaxUpdateBucketUAV; PassParameters->RWCardPageTileAllocator = CardPageTileAllocatorUAV; PassParameters->RWPriorityHistogram = PriorityHistogramUAV; auto ComputeShader = Views[0].ShaderMap->GetShader(); const FIntVector GroupSize(FMath::DivideAndRoundUp(LumenCardUpdateContext::CARD_UPDATE_CONTEXT_MAX * LumenCardUpdateContext::PRIORITY_HISTOGRAM_SIZE, FClearCardUpdateContextCS::GetGroupSize()), 1, 1); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("ClearCardUpdateContext"), ComputePassFlags, ComputeShader, PassParameters, GroupSize); } FLumenSurfaceCacheUpdateParameters SurfaceCacheUpdateParameters; { const int32 NumViewOrigins = FrameTemporaries.ViewOrigins.Num(); check(NumViewOrigins <= SurfaceCacheUpdateParameters.WorldCameraOrigins.Num()); SurfaceCacheUpdateParameters.NumCameraOrigins = NumViewOrigins; SurfaceCacheUpdateParameters.MaxDistanceFromFrustumToPrioritize = CVarLumenSceneLightingMaxDistanceFromFrustumToPrioritize.GetValueOnRenderThread(); for (int32 OriginIndex = 0; OriginIndex < NumViewOrigins; ++OriginIndex) { FLumenViewOrigin const& LumenViewOrigin = FrameTemporaries.ViewOrigins[OriginIndex]; SurfaceCacheUpdateParameters.WorldCameraOrigins[OriginIndex] = LumenViewOrigin.WorldCameraOrigin; SurfaceCacheUpdateParameters.PreViewTranslationHigh[OriginIndex] = LumenViewOrigin.PreViewTranslationDF.High; SurfaceCacheUpdateParameters.PreViewTranslationLow[OriginIndex] = LumenViewOrigin.PreViewTranslationDF.Low; FPlane4f FrustumPlanes[LUMEN_MAX_CAMERA_PLANES]; LumenViewOrigin.ViewToClip.GetFrustumNearPlane(FrustumPlanes[0]); LumenViewOrigin.ViewToClip.GetFrustumLeftPlane(FrustumPlanes[1]); LumenViewOrigin.ViewToClip.GetFrustumRightPlane(FrustumPlanes[2]); LumenViewOrigin.ViewToClip.GetFrustumTopPlane(FrustumPlanes[3]); LumenViewOrigin.ViewToClip.GetFrustumBottomPlane(FrustumPlanes[4]); static_assert(LUMEN_MAX_CAMERA_PLANES == 5, "Update code above for the new number of frustum planes"); for (uint32 PlaneIndex = 0; PlaneIndex < LUMEN_MAX_CAMERA_PLANES; ++PlaneIndex) { SurfaceCacheUpdateParameters.CameraFrustumPlanes[PlaneIndex] = FVector4f(FrustumPlanes[PlaneIndex]); } } } // Prepare update priority histogram { FBuildPageUpdatePriorityHistogramCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->RWPriorityHistogram = PriorityHistogramUAV; PassParameters->LumenCardScene = FrameTemporaries.LumenCardSceneUniformBuffer; PassParameters->CardPageLastUsedBuffer = CardPageLastUsedBufferSRV; PassParameters->CardPageHighResLastUsedBuffer = CardPageHighResLastUsedBufferSRV; PassParameters->CardPageNum = NumCardPages; PassParameters->SurfaceCacheUpdateParameters = SurfaceCacheUpdateParameters; PassParameters->SurfaceCacheUpdateFrameIndex = UpdateFrameIndex; PassParameters->FreezeUpdateFrame = FreezeUpdateFrame; PassParameters->FirstClipmapWorldExtentRcp = FirstClipmapWorldExtentRcp; PassParameters->DirectLightingUpdateFactor = DirectLightingCardUpdateContext.UpdateFactor; PassParameters->IndirectLightingUpdateFactor = IndirectLightingCardUpdateContext.UpdateFactor; FBuildPageUpdatePriorityHistogramCS::FPermutationDomain PermutationVector; PermutationVector.Set(bUseFeedback); auto ComputeShader = Views[0].ShaderMap->GetShader(PermutationVector); const FIntVector GroupSize(FMath::DivideAndRoundUp(LumenSceneData.GetNumCardPages(), FBuildPageUpdatePriorityHistogramCS::GetGroupSize()), 1, 1); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("BuildPageUpdatePriorityHistogram"), ComputePassFlags, ComputeShader, PassParameters, GroupSize); } // Compute prefix sum and pick max bucket { FSelectMaxUpdateBucketCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->RWMaxUpdateBucket = MaxUpdateBucketUAV; PassParameters->PriorityHistogram = PriorityHistogramSRV; PassParameters->MaxDirectLightingTilesToUpdate = DirectLightingCardUpdateContext.MaxUpdateTiles; PassParameters->MaxIndirectLightingTilesToUpdate = IndirectLightingCardUpdateContext.MaxUpdateTiles; PassParameters->SurfaceCacheUpdateFrameIndex = UpdateFrameIndex; PassParameters->FreezeUpdateFrame = FreezeUpdateFrame; auto ComputeShader = Views[0].ShaderMap->GetShader(); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("Select max update bucket"), ComputePassFlags, ComputeShader, PassParameters, FIntVector(2, 1, 1)); } // Build list of tiles to update in this frame { FBuildCardsUpdateListCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->RWDirectLightingCardPageIndexAllocator = DirectCardPageIndexAllocatorUAV; PassParameters->RWDirectLightingCardPageIndexData = GraphBuilder.CreateUAV(DirectLightingCardUpdateContext.CardPageIndexData); PassParameters->RWIndirectLightingCardPageIndexAllocator = IndirectCardPageIndexAllocatorUAV; PassParameters->RWIndirectLightingCardPageIndexData = GraphBuilder.CreateUAV(IndirectLightingCardUpdateContext.CardPageIndexData); PassParameters->RWCardPageTileAllocator = CardPageTileAllocatorUAV; PassParameters->MaxUpdateBucket = MaxUpdateBucketSRV; PassParameters->LumenCardDataBuffer = FrameTemporaries.CardBufferSRV; PassParameters->RWLumenCardPageDataBuffer = FrameTemporaries.CardPageBufferUAV; PassParameters->CardPageLastUsedBuffer = CardPageLastUsedBufferSRV; PassParameters->CardPageHighResLastUsedBuffer = CardPageHighResLastUsedBufferSRV; PassParameters->SurfaceCacheUpdateParameters = SurfaceCacheUpdateParameters; PassParameters->CardPageNum = NumCardPages; PassParameters->SurfaceCacheUpdateFrameIndex = UpdateFrameIndex; PassParameters->FreezeUpdateFrame = FreezeUpdateFrame; PassParameters->FirstClipmapWorldExtentRcp = FirstClipmapWorldExtentRcp; PassParameters->IndirectLightingHistoryValid = bIndirectLightingHistoryValid ? 1 : 0; PassParameters->MaxDirectLightingTilesToUpdate = DirectLightingCardUpdateContext.MaxUpdateTiles; PassParameters->MaxIndirectLightingTilesToUpdate = IndirectLightingCardUpdateContext.MaxUpdateTiles; PassParameters->DirectLightingUpdateFactor = DirectLightingCardUpdateContext.UpdateFactor; PassParameters->IndirectLightingUpdateFactor = IndirectLightingCardUpdateContext.UpdateFactor; FBuildCardsUpdateListCS::FPermutationDomain PermutationVector; PermutationVector.Set(bUseFeedback); auto ComputeShader = Views[0].ShaderMap->GetShader(PermutationVector); const FIntVector GroupSize(FMath::DivideAndRoundUp(LumenSceneData.GetNumCardPages(), FBuildCardsUpdateListCS::GetGroupSize()), 1, 1); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("Build cards update list"), ComputePassFlags, ComputeShader, PassParameters, GroupSize); } // Setup indirect args { FSetCardPageIndexIndirectArgsCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->RWDirectLightingDrawCardPageIndicesIndirectArgs = GraphBuilder.CreateUAV(DirectLightingCardUpdateContext.DrawCardPageIndicesIndirectArgs); PassParameters->RWDirectLightingDispatchCardPageIndicesIndirectArgs = GraphBuilder.CreateUAV(DirectLightingCardUpdateContext.DispatchCardPageIndicesIndirectArgs); PassParameters->RWIndirectLightingDrawCardPageIndicesIndirectArgs = GraphBuilder.CreateUAV(IndirectLightingCardUpdateContext.DrawCardPageIndicesIndirectArgs); PassParameters->RWIndirectLightingDispatchCardPageIndicesIndirectArgs = GraphBuilder.CreateUAV(IndirectLightingCardUpdateContext.DispatchCardPageIndicesIndirectArgs); PassParameters->DirectLightingCardPageIndexAllocator = DirectCardPageIndexAllocatorSRV; PassParameters->IndirectLightingCardPageIndexAllocator = IndirectCardPageIndexAllocatorSRV; PassParameters->VertexCountPerInstanceIndirect = GRHISupportsRectTopology ? 3 : 6; auto ComputeShader = Views[0].ShaderMap->GetShader(); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("SetCardPageIndexIndirectArgs"), ComputePassFlags, ComputeShader, PassParameters, FIntVector(1, 1, 1)); } const uint32 StatMode = GetLumenLightingStatMode(); if (StatMode == 1 || StatMode == 2) { ShaderPrint::SetEnabled(true); FLumenSceneLightingStatsCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); ShaderPrint::SetParameters(GraphBuilder, Views[0].ShaderPrintData, PassParameters->ShaderPrintUniformBuffer); PassParameters->LumenCardScene = FrameTemporaries.LumenCardSceneUniformBuffer; PassParameters->DirectLightingCardPageIndexAllocator = DirectCardPageIndexAllocatorSRV; PassParameters->IndirectLightingCardPageIndexAllocator = IndirectCardPageIndexAllocatorSRV; PassParameters->PriorityHistogram = PriorityHistogramSRV; PassParameters->MaxUpdateBucket = MaxUpdateBucketSRV; PassParameters->CardPageTileAllocator = GraphBuilder.CreateSRV(CardPageTileAllocator); PassParameters->CardPageNum = LumenSceneData.GetNumCardPages(); PassParameters->LightingStatMode = StatMode; auto ComputeShader = Views[0].ShaderMap->GetShader(); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("SceneLightingStats"), ComputePassFlags, ComputeShader, PassParameters, FIntVector(1, 1, 1)); } }