// Copyright Epic Games, Inc. All Rights Reserved. static TAutoConsoleVariable CVarLumenDirectLightingStochastic( TEXT("r.LumenScene.DirectLighting.Stochastic"), 0, TEXT("Whether to enable stochastic lighting for Lumen scene (experimental)."), ECVF_RenderThreadSafe); static TAutoConsoleVariable CVarLumenDirectLightingStochasticTemporal( TEXT("r.LumenScene.DirectLighting.Stochastic.Temporal"), 1, TEXT("Enable temporal filtering."), ECVF_Scalability | ECVF_RenderThreadSafe ); static TAutoConsoleVariable CVarLumenDirectLightingStochasticTemporalMaxFramesAccumulated( TEXT("r.LumenScene.DirectLighting.Stochastic.Temporal.MaxFramesAccumulated"), 12, TEXT("Max history length when accumulating frames. Lower values have less ghosting, but more noise."), ECVF_Scalability | ECVF_RenderThreadSafe ); static TAutoConsoleVariable CVarLumenDirectLightingStochasticTemporalNeighborhoodClampScale( TEXT("r.LumenScene.DirectLighting.Stochastic.Temporal.NeighborhoodClampScale"), 2.0f, TEXT("Scales how permissive is neighborhood clamp. Higher values cause more ghosting, but allow smoother temporal accumulation."), ECVF_Scalability | ECVF_RenderThreadSafe ); static TAutoConsoleVariable CVarLumenDirectLightingStochasticDebug( TEXT("r.LumenScene.DirectLighting.Stochastic.Debug"), 0, TEXT("Enable debug print for Lumen stochastic pipeline."), ECVF_Scalability | ECVF_RenderThreadSafe ); static TAutoConsoleVariable CVarLumenDirectLightingStochasticSamplePerTexel( TEXT("r.LumenScene.DirectLighting.Stochastic.SamplePerTexel"), 1, TEXT("Number of light sample per texel for Lumen direct lighting with stochastic selection."), ECVF_Scalability | ECVF_RenderThreadSafe ); static TAutoConsoleVariable CVarLumenDirectLightingStochasticMinWeight( TEXT("r.LumenScene.DirectLighting.Stochastic.MinWeight"), 0.001f, TEXT("Determines minimal sample influence on final texels. Used to skip samples which would have minimal impact to the final image even if light is fully visible."), ECVF_Scalability | ECVF_RenderThreadSafe ); static uint32 GetLumenStochasticNumSamplesPerTexel() { const int32 NumSamples = FMath::Clamp(CVarLumenDirectLightingStochasticSamplePerTexel.GetValueOnRenderThread(), 1, 4); return NumSamples > 2 ? 4 : NumSamples; } bool LumenSceneDirectLighting::UseStochasticLighting(const FSceneViewFamily& ViewFamily) { return CVarLumenDirectLightingStochastic.GetValueOnRenderThread() > 0 && Lumen::UseHardwareRayTracedDirectLighting(ViewFamily); } /////////////////////////////////////////////////////////////////////////////////////////////////// class FLumenSceneCompactLightOffsetCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FLumenSceneCompactLightOffsetCS) SHADER_USE_PARAMETER_STRUCT(FLumenSceneCompactLightOffsetCS, FGlobalShader) BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER(uint32, NumLights) SHADER_PARAMETER(uint32, NumStandaloneLights) SHADER_PARAMETER(uint32, NumSamplesPerPixel1d) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, CardTilePerLightCounters) SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWCardTilePerLightOffsets) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, RWCardTilePerLightArgs) 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); //ShaderPrint::ModifyCompilationEnvironment(Parameters.Platform, OutEnvironment); OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE"), GetGroupSize()); OutEnvironment.SetDefine(TEXT("SHADER_STANDALONE_COMPACT_OFFSET"), 1); OutEnvironment.CompilerFlags.Add(CFLAG_WaveOperations); } }; IMPLEMENT_GLOBAL_SHADER(FLumenSceneCompactLightOffsetCS, "/Engine/Private/Lumen/LumenSceneDirectLightingStochastic.usf", "MainCS", SF_Compute); /////////////////////////////////////////////////////////////////////////////////////////////////// class FLumenSceneCompactLightListCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FLumenSceneCompactLightListCS) SHADER_USE_PARAMETER_STRUCT(FLumenSceneCompactLightListCS, FGlobalShader) BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) RDG_BUFFER_ACCESS(IndirectArgs, ERHIAccess::IndirectArgs) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, UniqueLightIndices) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, UniqueLightCount) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, CardTilePerLightOffsets) SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWCardTilePerLightCounters) SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWCardTilePerLightDatas) 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_STANDALONE_COMPACT_LIST"), 1); OutEnvironment.CompilerFlags.Add(CFLAG_WaveOperations); } }; IMPLEMENT_GLOBAL_SHADER(FLumenSceneCompactLightListCS, "/Engine/Private/Lumen/LumenSceneDirectLightingStochastic.usf", "MainCS", SF_Compute); /////////////////////////////////////////////////////////////////////////////////////////////////// // Only used for light with non-atlased light function class FLumenSceneEvaluateStandaloneLightMaterialCS : public FMaterialShader { DECLARE_SHADER_TYPE(FLumenSceneEvaluateStandaloneLightMaterialCS, Material); FLumenSceneEvaluateStandaloneLightMaterialCS(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); } FLumenSceneEvaluateStandaloneLightMaterialCS() {} BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) RDG_BUFFER_ACCESS(IndirectArgs, ERHIAccess::IndirectArgs) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer) SHADER_PARAMETER(uint32, LightIndex) SHADER_PARAMETER(uint32, ViewIndex) SHADER_PARAMETER_ARRAY(FVector4f, PreViewTranslationHigh, [LUMEN_MAX_VIEWS]) SHADER_PARAMETER_ARRAY(FVector4f, PreViewTranslationLow, [LUMEN_MAX_VIEWS]) SHADER_PARAMETER(FVector2f, ViewExposure) SHADER_PARAMETER_STRUCT_INCLUDE(FLightCloudTransmittanceParameters, LightCloudTransmittanceParameters) SHADER_PARAMETER_STRUCT_INCLUDE(LumenSceneDirectLighting::FLightDataParameters, LumenLightData) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, CardTilePerLightCounters) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, CardTilePerLightOffsets) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, CardTilePerLightDatas) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, LumenSceneData) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2DArray, RWLightSamples) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2DArray, RWSampleDiffuseLighting) SHADER_PARAMETER_STRUCT_INCLUDE(FLightFunctionParameters, LightFunctionParameters) END_SHADER_PARAMETER_STRUCT() class FCloudTransmittance : SHADER_PERMUTATION_BOOL("USE_CLOUD_TRANSMITTANCE"); using FPermutationDomain = TShaderPermutationDomain; static int32 GetGroupSize() { return 8; } static bool ShouldCompilePermutation(const FMaterialShaderPermutationParameters& Parameters) { FPermutationDomain PermutationVector(Parameters.PermutationId); return Parameters.MaterialParameters.MaterialDomain == EMaterialDomain::MD_LightFunction && DoesPlatformSupportLumenGI(Parameters.Platform); } static void ModifyCompilationEnvironment(const FMaterialShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FMaterialShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE"), GetGroupSize()); OutEnvironment.SetDefine(TEXT("SHADER_STANDALONE_EVALUATE"), 1); OutEnvironment.SetDefine(TEXT("LIGHT_FUNCTION"), 1); OutEnvironment.SetDefine(TEXT("USE_IES_PROFILE"), 1); // To avoid extra permutation OutEnvironment.SetDefine(TEXT("USE_RECT_LIGHT"), 1); // To avoid extra permutation OutEnvironment.SetDefine(TEXT("USE_LIGHT_FUNCTION_ATLAS"), 0); OutEnvironment.SetDefine(TEXT("SUBSTRATE_INLINE_SHADING"), 1); OutEnvironment.CompilerFlags.Add(CFLAG_WaveOperations); } }; IMPLEMENT_MATERIAL_SHADER_TYPE(, FLumenSceneEvaluateStandaloneLightMaterialCS, TEXT("/Engine/Private/Lumen/LumenSceneDirectLightingStochastic.usf"), TEXT("MainCS"), SF_Compute); /////////////////////////////////////////////////////////////////////////////////////////////////// // Only used for direct light with cloud transmittance class FLumenSceneEvaluateStandaloneLightCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FLumenSceneEvaluateStandaloneLightCS) SHADER_USE_PARAMETER_STRUCT(FLumenSceneEvaluateStandaloneLightCS, FGlobalShader) class FCloudTransmittance : SHADER_PERMUTATION_BOOL("USE_CLOUD_TRANSMITTANCE"); using FPermutationDomain = TShaderPermutationDomain; BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) RDG_BUFFER_ACCESS(IndirectArgs, ERHIAccess::IndirectArgs) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer) SHADER_PARAMETER(uint32, LightIndex) SHADER_PARAMETER(uint32, ViewIndex) SHADER_PARAMETER_ARRAY(FVector4f, PreViewTranslationHigh, [LUMEN_MAX_VIEWS]) SHADER_PARAMETER_ARRAY(FVector4f, PreViewTranslationLow, [LUMEN_MAX_VIEWS]) SHADER_PARAMETER(FVector2f, ViewExposure) SHADER_PARAMETER_STRUCT_INCLUDE(FLightCloudTransmittanceParameters, LightCloudTransmittanceParameters) SHADER_PARAMETER_STRUCT_INCLUDE(LumenSceneDirectLighting::FLightDataParameters, LumenLightData) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, CardTilePerLightCounters) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, CardTilePerLightOffsets) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, CardTilePerLightDatas) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, LumenSceneData) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2DArray, RWLightSamples) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2DArray, RWSampleDiffuseLighting) END_SHADER_PARAMETER_STRUCT() 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_STANDALONE_EVALUATE"), 1); OutEnvironment.SetDefine(TEXT("LIGHT_FUNCTION"), 0); OutEnvironment.SetDefine(TEXT("USE_IES_PROFILE"), 0); // Directional light does not support IES profile OutEnvironment.SetDefine(TEXT("USE_RECT_LIGHT"), 0); // Directional light OutEnvironment.SetDefine(TEXT("USE_LIGHT_FUNCTION_ATLAS"), 0); // Directional light does not use light function atlas OutEnvironment.SetDefine(TEXT("SUBSTRATE_INLINE_SHADING"), 1); OutEnvironment.CompilerFlags.Add(CFLAG_WaveOperations); } }; IMPLEMENT_GLOBAL_SHADER(FLumenSceneEvaluateStandaloneLightCS, "/Engine/Private/Lumen/LumenSceneDirectLightingStochastic.usf", "MainCS", SF_Compute); /////////////////////////////////////////////////////////////////////////////////////////////////// BEGIN_SHADER_PARAMETER_STRUCT(FLumenSceneLightingStochasticParameters, ) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer) SHADER_PARAMETER_STRUCT_INCLUDE(ShaderPrint::FShaderParameters, ShaderPrintUniformBuffer) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(LightFunctionAtlas::FLightFunctionAtlasGlobalParameters, LightFunctionAtlas) SHADER_PARAMETER_STRUCT_REF(FBlueNoise, BlueNoise) SHADER_PARAMETER(uint32, NumSamplesPerPixel1d) SHADER_PARAMETER(uint32, StateFrameIndex) SHADER_PARAMETER(uint32, MaxCompositeTiles) SHADER_PARAMETER(float, SamplingMinWeight) SHADER_PARAMETER(float, TemporalMaxFramesAccumulated) SHADER_PARAMETER(float, TemporalNeighborhoodClampScale) SHADER_PARAMETER(int32, TemporalAdvanceFrame) SHADER_PARAMETER(int32, DebugLightId) SHADER_PARAMETER(uint32, DummyZeroForFixingShaderCompilerBug) SHADER_PARAMETER(uint32, NumLights) SHADER_PARAMETER(uint32, NumStandaloneLights) SHADER_PARAMETER(uint32, NumViews) SHADER_PARAMETER(float, DiffuseColorBoost) 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) END_SHADER_PARAMETER_STRUCT() /////////////////////////////////////////////////////////////////////////////////////////////////// class FLumenSceneGenerateLightSamplesCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FLumenSceneGenerateLightSamplesCS) SHADER_USE_PARAMETER_STRUCT(FLumenSceneGenerateLightSamplesCS, FGlobalShader) BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) RDG_BUFFER_ACCESS(IndirectArgs, ERHIAccess::IndirectArgs) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FLumenCardScene, LumenCardScene) SHADER_PARAMETER_STRUCT_INCLUDE(LumenSceneDirectLighting::FLightDataParameters, LumenLightData) SHADER_PARAMETER_STRUCT_INCLUDE(FLumenSceneLightingStochasticParameters, CommonParameters) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, RWSampleLuminanceSum) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2DArray, RWSampleDiffuseLighting) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, RWSceneData) SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWCardTilePerLightCounters) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2DArray, RWLightSamples) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, RWUniqueLightIndices) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, RWUniqueLightCount) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, TileAllocator) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, TileData) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, LumenSceneDebugData) END_SHADER_PARAMETER_STRUCT() class FIESProfile : SHADER_PERMUTATION_BOOL("USE_IES_PROFILE"); class FRectLight : SHADER_PERMUTATION_BOOL("USE_RECT_LIGHT"); class FLightFunctionAtlas : SHADER_PERMUTATION_BOOL("USE_LIGHT_FUNCTION_ATLAS"); class FNumSamplesPerPixel1d : SHADER_PERMUTATION_SPARSE_INT("NUM_SAMPLES_PER_PIXEL_1D", 1, 2, 4); using FPermutationDomain = TShaderPermutationDomain; static int32 GetGroupSize() { return 8; } static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return DoesPlatformSupportLumenGI(Parameters.Platform); } static EShaderPermutationPrecacheRequest ShouldPrecachePermutation(const FGlobalShaderPermutationParameters& Parameters) { FPermutationDomain PermutationVector(Parameters.PermutationId); return EShaderPermutationPrecacheRequest::Precached; } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); //ShaderPrint::ModifyCompilationEnvironment(Parameters.Platform, OutEnvironment); OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE"), GetGroupSize()); OutEnvironment.SetDefine(TEXT("SHADER_GENERATE_SAMPLE"), 1); OutEnvironment.CompilerFlags.Add(CFLAG_WaveOperations); } }; IMPLEMENT_GLOBAL_SHADER(FLumenSceneGenerateLightSamplesCS, "/Engine/Private/Lumen/LumenSceneDirectLightingStochastic.usf", "GenerateLightSamplesCS", SF_Compute); /////////////////////////////////////////////////////////////////////////////////////////////////// class FLumenSceneShadeLightSamplesCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FLumenSceneShadeLightSamplesCS); SHADER_USE_PARAMETER_STRUCT(FLumenSceneShadeLightSamplesCS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER(uint32, DummyZeroForFixingShaderCompilerBug) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, TileAllocator) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, TileData) SHADER_PARAMETER_RDG_TEXTURE(Texture2DArray, LightSamples) SHADER_PARAMETER_RDG_TEXTURE(Texture2DArray, SampleDiffuseLighting) 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(uint32, NumSamplesPerPixel1d) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, AlbedoAtlas) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, OpacityAtlas) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, EmissiveAtlas) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, IndirectLightingAtlas) SHADER_PARAMETER_SAMPLER(SamplerState, BilinearClampedSampler) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, LumenSceneDebugData) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, RWFinalLightingAtlas) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, RWDirectLightingAtlas) SHADER_PARAMETER(FVector2f, IndirectLightingAtlasHalfTexelSize) SHADER_PARAMETER_STRUCT_INCLUDE(ShaderPrint::FShaderParameters, ShaderPrintUniformBuffer) END_SHADER_PARAMETER_STRUCT() class FUseLightSamples : SHADER_PERMUTATION_BOOL("USE_LIGHT_SAMPLES"); 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_SHADING"), 1); } }; IMPLEMENT_GLOBAL_SHADER(FLumenSceneShadeLightSamplesCS, "/Engine/Private/Lumen/LumenSceneDirectLightingStochastic.usf", "ShadeLightSamplesCS", SF_Compute); /////////////////////////////////////////////////////////////////////////////////////////////////// class FLumenSceneCompactLightSampleTracesCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FLumenSceneCompactLightSampleTracesCS) SHADER_USE_PARAMETER_STRUCT(FLumenSceneCompactLightSampleTracesCS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWCompactedTraceTexelData) SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWCompactedTraceTexelAllocator) SHADER_PARAMETER_RDG_TEXTURE(Texture2DArray, LightSamples) SHADER_PARAMETER(FIntPoint, SampleViewSize) SHADER_PARAMETER(uint32, NumSamplesPerPixel1d) SHADER_PARAMETER_STRUCT_INCLUDE(ShaderPrint::FShaderParameters, ShaderPrintUniformBuffer) END_SHADER_PARAMETER_STRUCT() static int32 GetGroupSize() { return 16; // TODO, could we reduce that to 8, so that we can load tile directly? and dispatch indirect? } class FWaveOps : SHADER_PERMUTATION_BOOL("WAVE_OPS"); using FPermutationDomain = TShaderPermutationDomain; static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return DoesPlatformSupportLumenGI(Parameters.Platform); } FORCENOINLINE static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE"), GetGroupSize()); OutEnvironment.SetDefine(TEXT("SHADER_COMPACTION"), 1); FPermutationDomain PermutationVector(Parameters.PermutationId); if (PermutationVector.Get()) { OutEnvironment.CompilerFlags.Add(CFLAG_WaveOperations); } } }; IMPLEMENT_GLOBAL_SHADER(FLumenSceneCompactLightSampleTracesCS, "/Engine/Private/Lumen/LumenSceneDirectLightingStochastic.usf", "CompactLightSampleTracesCS", SF_Compute); /////////////////////////////////////////////////////////////////////////////////////////////////// class FLumenSceneDenoiserTemporalCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FLumenSceneDenoiserTemporalCS) SHADER_USE_PARAMETER_STRUCT(FLumenSceneDenoiserTemporalCS, FGlobalShader) BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) RDG_BUFFER_ACCESS(IndirectArgsBuffer, ERHIAccess::IndirectArgs) SHADER_PARAMETER_STRUCT_INCLUDE(FLumenSceneLightingStochasticParameters, CommonParameters) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FLumenCardScene, LumenCardScene) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SampleLuminanceSumTexture) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, DiffuseLightingAndSecondMomentHistoryTexture) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, NumFramesAccumulatedHistoryTexture) SHADER_PARAMETER(float, PrevSceneColorPreExposureCorrection) SHADER_PARAMETER(FVector2f, IndirectLightingAtlasHalfTexelSize) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, RWDiffuseLightingAndSecondMoment) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, RWNumFramesAccumulated) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, TileAllocator) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, TileData) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, AlbedoAtlas) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, OpacityAtlas) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, EmissiveAtlas) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, ResolvedDirectLightingAtlas) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, IndirectLightingAtlas) SHADER_PARAMETER_SAMPLER(SamplerState, BilinearClampedSampler) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, FinalLightingAtlas) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, RWFinalLightingAtlas) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, RWDirectLightingAtlas) END_SHADER_PARAMETER_STRUCT() class FValidHistory : SHADER_PERMUTATION_BOOL("VALID_HISTORY"); 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_TEMPORAL_DENOISER"), 1); OutEnvironment.CompilerFlags.Add(CFLAG_AllowTypedUAVLoads); OutEnvironment.CompilerFlags.Add(CFLAG_Wave32); } }; IMPLEMENT_GLOBAL_SHADER(FLumenSceneDenoiserTemporalCS, "/Engine/Private/Lumen/LumenSceneDirectLightingStochastic.usf", "DenoiserTemporalCS", SF_Compute); /////////////////////////////////////////////////////////////////////////////////////////////////// static void CompactLumenSceneLightsTraces( const FViewInfo& View, FRDGBuilder& GraphBuilder, FRDGTextureRef LightSamples, FRDGBufferRef CompactedTraceTexelData, FRDGBufferRef CompactedTraceTexelAllocator) { // Compact light sample traces before tracing FLumenSceneCompactLightSampleTracesCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->RWCompactedTraceTexelData = GraphBuilder.CreateUAV(CompactedTraceTexelData); PassParameters->RWCompactedTraceTexelAllocator = GraphBuilder.CreateUAV(CompactedTraceTexelAllocator); PassParameters->LightSamples = LightSamples; PassParameters->NumSamplesPerPixel1d = LightSamples->Desc.ArraySize; PassParameters->SampleViewSize = LightSamples->Desc.Extent; ShaderPrint::SetParameters(GraphBuilder, View.ShaderPrintData, PassParameters->ShaderPrintUniformBuffer); const bool bWaveOps = Lumen::UseWaveOps(View.GetShaderPlatform()) && GRHIMinimumWaveSize <= 32 && GRHIMaximumWaveSize >= 32; FLumenSceneCompactLightSampleTracesCS::FPermutationDomain PermutationVector; PermutationVector.Set(bWaveOps); auto ComputeShader = View.ShaderMap->GetShader(PermutationVector); FIntVector GroupCount = FComputeShaderUtils::GetGroupCount(LightSamples->Desc.Extent, FLumenSceneCompactLightSampleTracesCS::GetGroupSize()); GroupCount.Z = PassParameters->NumSamplesPerPixel1d; FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("CompactLightSampleTraces%s", bWaveOps ? TEXT("(WaveOps)") : TEXT("")), ComputeShader, PassParameters, GroupCount); } /////////////////////////////////////////////////////////////////////////////////////////////////// static void ComputeStochasticLighting( FRDGBuilder& GraphBuilder, FScene* Scene, const FViewInfo& View, const FLumenSceneFrameTemporaries& FrameTemporaries, const FLumenDirectLightingTaskData* LightingTaskData, const FLumenCardUpdateContext& CardUpdateContext, ERDGPassFlags ComputePassFlags, const LumenSceneDirectLighting::FLightDataParameters& LumenLightData) { FLumenSceneData& LumenSceneData = *Scene->GetLumenSceneData(View);// TODO Views[x]? const int32 NumViewOrigins = FrameTemporaries.ViewOrigins.Num(); const bool bDebug = CVarLumenDirectLightingStochasticDebug.GetValueOnRenderThread() > 0; const uint32 NumSamplesPerPixel1d = GetLumenStochasticNumSamplesPerTexel(); const FGlobalShaderMap* GlobalShaderMap = View.ShaderMap; const bool bTemporal = CVarLumenDirectLightingStochasticTemporal.GetValueOnRenderThread() > 0; const bool bUseLightFunctionAtlas = LightFunctionAtlas::IsEnabled(View, LightFunctionAtlas::ELightFunctionAtlasSystem::Lumen); FBlueNoise BlueNoise = GetBlueNoiseGlobalParameters(); TUniformBufferRef BlueNoiseUniformBuffer = CreateUniformBufferImmediate(BlueNoise, EUniformBufferUsage::UniformBuffer_SingleDraw); // Common parameters FLumenSceneLightingStochasticParameters CommonParameters; { CommonParameters.ViewUniformBuffer = View.ViewUniformBuffer; CommonParameters.BlueNoise = BlueNoiseUniformBuffer; CommonParameters.NumSamplesPerPixel1d = NumSamplesPerPixel1d; CommonParameters.StateFrameIndex = View.ViewState ? View.ViewState->GetFrameIndex() : 0; CommonParameters.MaxCompositeTiles = CardUpdateContext.MaxUpdateTiles; CommonParameters.SamplingMinWeight = FMath::Max(CVarLumenDirectLightingStochasticMinWeight.GetValueOnRenderThread(), 0.0f); CommonParameters.TemporalMaxFramesAccumulated = FMath::Max(CVarLumenDirectLightingStochasticTemporalMaxFramesAccumulated.GetValueOnRenderThread(), 0.0f); CommonParameters.TemporalNeighborhoodClampScale = CVarLumenDirectLightingStochasticTemporalNeighborhoodClampScale.GetValueOnRenderThread(); CommonParameters.TemporalAdvanceFrame = View.ViewState && !View.bStatePrevViewInfoIsReadOnly ? 1 : 0; CommonParameters.DebugLightId = INDEX_NONE; CommonParameters.DummyZeroForFixingShaderCompilerBug = 0; CommonParameters.NumLights = LightingTaskData->GatheredLights.Num(); CommonParameters.NumStandaloneLights = LightingTaskData->StandaloneLightIndices.Num(); CommonParameters.NumViews = NumViewOrigins; CommonParameters.DiffuseColorBoost = 1.0f / FMath::Max(View.FinalPostProcessSettings.LumenDiffuseColorBoost, 1.0f); if (bUseLightFunctionAtlas) { CommonParameters.LightFunctionAtlas = LightFunctionAtlas::BindGlobalParameters(GraphBuilder, View); } check(NumViewOrigins <= CommonParameters.FrustumTranslatedWorldToClip.Num()); for (int32 OriginIndex = 0; OriginIndex < NumViewOrigins; ++OriginIndex) { const FLumenViewOrigin& ViewOrigin = FrameTemporaries.ViewOrigins[OriginIndex]; CommonParameters.FrustumTranslatedWorldToClip[OriginIndex] = ViewOrigin.FrustumTranslatedWorldToClip; CommonParameters.PreViewTranslationHigh[OriginIndex] = ViewOrigin.PreViewTranslationDF.High; CommonParameters.PreViewTranslationLow[OriginIndex] = ViewOrigin.PreViewTranslationDF.Low; CommonParameters.ViewExposure[OriginIndex] = ViewOrigin.LastEyeAdaptationExposure; } if (true || bDebug) { ShaderPrint::SetEnabled(true); ShaderPrint::RequestSpaceForLines(1024); ShaderPrint::SetParameters(GraphBuilder, View.ShaderPrintData, CommonParameters.ShaderPrintUniformBuffer); } } const uint32 MaxLightTiles = CardUpdateContext.MaxUpdateTiles; const uint32 NumLights = LightingTaskData->GatheredLights.Num(); const uint32 NumStandaloneLights = LightingTaskData->StandaloneLightIndices.Num(); const uint32 NumLightsRoundedUp = FMath::RoundUpToPowerOfTwo(FMath::Max(LightingTaskData->GatheredLights.Num(), 1)) * NumViewOrigins; const uint32 MaxLightsPerTile = FMath::RoundUpToPowerOfTwo(FMath::Clamp(CVarLumenDirectLightingMaxLightsPerTile.GetValueOnRenderThread(), 1, 32)); const uint32 MaxCulledCardTiles = MaxLightsPerTile * MaxLightTiles; const bool bHasStandaloneLight = LightingTaskData->StandaloneLightIndices.Num() > 0; // 0. Splice card pages into tiles FLumenCardTileUpdateContext CardTileUpdateContext; { RDG_EVENT_SCOPE(GraphBuilder, "SpliceCardPageIntoToTiles"); Lumen::SpliceCardPagesIntoTiles(GraphBuilder, GlobalShaderMap, CardUpdateContext, FrameTemporaries.LumenCardSceneUniformBuffer, CardTileUpdateContext, ComputePassFlags); } // 0. Early out if no lights if (NumLights == 0) { RDG_EVENT_SCOPE(GraphBuilder, "Shading"); FLumenSceneShadeLightSamplesCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->DummyZeroForFixingShaderCompilerBug = 0; PassParameters->IndirectArgsBuffer = CardTileUpdateContext.DispatchCardTilesIndirectArgs; PassParameters->View = View.ViewUniformBuffer; PassParameters->LumenCardScene = FrameTemporaries.LumenCardSceneUniformBuffer; PassParameters->DiffuseColorBoost = 1.0f / FMath::Max(View.FinalPostProcessSettings.LumenDiffuseColorBoost, 1.0f); PassParameters->NumSamplesPerPixel1d = CommonParameters.NumSamplesPerPixel1d; PassParameters->AlbedoAtlas = FrameTemporaries.AlbedoAtlas; PassParameters->OpacityAtlas = FrameTemporaries.OpacityAtlas; PassParameters->EmissiveAtlas = FrameTemporaries.EmissiveAtlas; PassParameters->IndirectLightingAtlas = FrameTemporaries.IndirectLightingAtlas; PassParameters->BilinearClampedSampler = TStaticSamplerState::GetRHI(); PassParameters->RWFinalLightingAtlas = GraphBuilder.CreateUAV(FrameTemporaries.FinalLightingAtlas); PassParameters->RWDirectLightingAtlas = GraphBuilder.CreateUAV(FrameTemporaries.DirectLightingAtlas); const FIntPoint IndirectLightingAtlasSize = LumenSceneData.GetRadiosityAtlasSize(); PassParameters->IndirectLightingAtlasHalfTexelSize = FVector2f(0.5f / IndirectLightingAtlasSize.X, 0.5f / IndirectLightingAtlasSize.Y); PassParameters->TileAllocator = GraphBuilder.CreateSRV(CardTileUpdateContext.CardTileAllocator); PassParameters->TileData = GraphBuilder.CreateSRV(CardTileUpdateContext.CardTiles); ShaderPrint::SetParameters(GraphBuilder, View.ShaderPrintData, PassParameters->ShaderPrintUniformBuffer); FLumenSceneShadeLightSamplesCS::FPermutationDomain PermutationVector; PermutationVector.Set(false); auto ComputeShader = View.ShaderMap->GetShader(PermutationVector); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("CombineLighting CS"), ComputePassFlags, ComputeShader, PassParameters, CardTileUpdateContext.DispatchCardTilesIndirectArgs, (uint32)ELumenDispatchCardTilesIndirectArgsOffset::OneGroupPerCardTile); return; } { // Transient atlas for storing position and normal to avoid loading surface cache data const FIntPoint AtlasTileCount = FIntPoint(128u, FMath::DivideAndRoundUp(MaxLightTiles, 128u)); const FIntPoint AtlasResolution = AtlasTileCount * Lumen::CardTileSize; check(CardUpdateContext.MaxUpdateTiles <= uint32(AtlasTileCount.X * AtlasTileCount.Y)); // Transient texture for shading FRDGTextureRef LightSamples = GraphBuilder.CreateTexture( FRDGTextureDesc::Create2DArray(AtlasResolution, PF_R32_UINT, FClearValueBinding::Black, TexCreate_ShaderResource | TexCreate_UAV, CommonParameters.NumSamplesPerPixel1d), TEXT("LumenScene.DirectLighting.LightSamples")); FRDGTextureRef SampleLuminanceSum = GraphBuilder.CreateTexture( FRDGTextureDesc::Create2D(AtlasResolution, PF_R16F, FClearValueBinding::Black, TexCreate_ShaderResource | TexCreate_UAV), TEXT("LumenScene.DirectLighting.SampleLuminanceSum")); FRDGTextureRef SceneAlbedo = GraphBuilder.CreateTexture( FRDGTextureDesc::Create2D(AtlasResolution, PF_A2B10G10R10, FClearValueBinding::Black, TexCreate_ShaderResource | TexCreate_UAV), TEXT("LumenScene.DirectLighting.SceneAlbedo")); // Each texel can select a light, so there is at max 64 unique lights per 8x8 card (i.e., == AltasResolution) FRDGTextureRef UniqueLightIndices = GraphBuilder.CreateTexture( FRDGTextureDesc::Create2D(AtlasResolution, PF_R32_UINT, FClearValueBinding::Black, TexCreate_ShaderResource | TexCreate_UAV), TEXT("LumenScene.DirectLighting.UniqueLightIndices")); const FIntPoint LightCounterResolution(FMath::DivideAndRoundUp(AtlasResolution.X, int32(Lumen::CardTileSize)), FMath::DivideAndRoundUp(AtlasResolution.Y, int32(Lumen::CardTileSize))); FRDGTextureRef UniqueLightCount = GraphBuilder.CreateTexture( FRDGTextureDesc::Create2D(LightCounterResolution, PF_R32_UINT, FClearValueBinding::Black, TexCreate_ShaderResource | TexCreate_UAV), TEXT("LumenScene.DirectLighting.UniqueLightCount")); FRDGTextureRef SampleDiffuseLighting = GraphBuilder.CreateTexture( FRDGTextureDesc::Create2DArray(AtlasResolution, PF_FloatRGBA, FClearValueBinding::Black, TexCreate_ShaderResource | TexCreate_UAV, CommonParameters.NumSamplesPerPixel1d), TEXT("LumenScene.DirectLighting.SampleDiffuseLighting")); // When using temporal filtering, allocate an intermediate storage for direct lighting for spatially filtering neighborhood FRDGTextureRef ResolvedDirectLightingAtlas = FrameTemporaries.DirectLightingAtlas; if (bTemporal) { ResolvedDirectLightingAtlas = GraphBuilder.CreateTexture(FrameTemporaries.DirectLightingAtlas->Desc, TEXT("LumenScene.DirectLighting.TemporaryDirectLightingAtlas")); } // Store position, normal, and view index at the sample position to avoid the loading of the cards data during the tracing. FRDGTextureRef SceneData = GraphBuilder.CreateTexture( FRDGTextureDesc::Create2D(AtlasResolution, PF_A32B32G32R32F, FClearValueBinding::Black, TexCreate_ShaderResource | TexCreate_UAV), TEXT("LumenScene.DirectLighting.SceneData")); FRDGBufferRef CompactedLightSampleData = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32), AtlasResolution.X * AtlasResolution.Y), TEXT("LumenScene.DirectLighting.CompactedLightSampleData")); FRDGBufferRef CompactedLightSampleAllocator = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32), 1), TEXT("LumenScene.DirectLighting.CompactedLightSampleAllocator")); FRDGTextureUAVRef SampleLuminanceSumUAV = GraphBuilder.CreateUAV(SampleLuminanceSum, ERDGUnorderedAccessViewFlags::SkipBarrier); FRDGBufferRef CardTilePerLightCounters = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(4u, NumLights), TEXT("LumenScene.DirectLighting.CardTilePerLightCounters")); FRDGBufferRef CardTilePerLightOffsets = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(4u, NumLights), TEXT("LumenScene.DirectLighting.CardTilePerLightOffsets")); FRDGBufferRef CardTilePerLightDatas = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(4u, MaxCulledCardTiles), TEXT("LumenScene.DirectLighting.CardTilePerLightDatas")); { AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(CompactedLightSampleAllocator), 0); if (bHasStandaloneLight) { AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(CardTilePerLightCounters), 0u); } AddClearUAVPass(GraphBuilder,GraphBuilder.CreateUAV(SampleLuminanceSum), 0.f); AddClearUAVPass(GraphBuilder,GraphBuilder.CreateUAV(LightSamples), 0u); // Needed as trace/sample compaction is dispatch on the entire resource, and we need which samples are valid or not // Only for debug AddClearUAVPass(GraphBuilder,GraphBuilder.CreateUAV(UniqueLightIndices), 0u); // Remove - Not needed just for debugging AddClearUAVPass(GraphBuilder,GraphBuilder.CreateUAV(UniqueLightCount), 0u); // Remove - Not needed just for debugging } // 1.1 Sample light { RDG_EVENT_SCOPE(GraphBuilder, "Generate Sample"); FLumenSceneGenerateLightSamplesCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->CommonParameters = CommonParameters; PassParameters->RWSampleLuminanceSum = SampleLuminanceSumUAV; PassParameters->RWLightSamples = GraphBuilder.CreateUAV(LightSamples); PassParameters->LumenLightData = LumenLightData; PassParameters->LumenCardScene = FrameTemporaries.LumenCardSceneUniformBuffer; PassParameters->TileAllocator = GraphBuilder.CreateSRV(CardTileUpdateContext.CardTileAllocator); PassParameters->TileData = GraphBuilder.CreateSRV(CardTileUpdateContext.CardTiles); PassParameters->IndirectArgs = CardTileUpdateContext.DispatchCardTilesIndirectArgs; PassParameters->RWUniqueLightIndices = GraphBuilder.CreateUAV(UniqueLightIndices); PassParameters->RWUniqueLightCount = GraphBuilder.CreateUAV(UniqueLightCount); PassParameters->RWSampleDiffuseLighting = GraphBuilder.CreateUAV(SampleDiffuseLighting); PassParameters->RWSceneData = GraphBuilder.CreateUAV(SceneData); PassParameters->LumenSceneDebugData = FrameTemporaries.DebugData; PassParameters->RWCardTilePerLightCounters = GraphBuilder.CreateUAV(CardTilePerLightCounters); FLumenSceneGenerateLightSamplesCS::FPermutationDomain PermutationVector; PermutationVector.Set(LightingTaskData->bHasIESLights); PermutationVector.Set(LightingTaskData->bHasRectLights); PermutationVector.Set(bUseLightFunctionAtlas); PermutationVector.Set(CommonParameters.NumSamplesPerPixel1d); auto ComputeShader = View.ShaderMap->GetShader(PermutationVector); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("GenerateSamples(SamplesPerTexel:%d)", CommonParameters.NumSamplesPerPixel1d), ComputeShader, PassParameters, CardTileUpdateContext.DispatchCardTilesIndirectArgs, 1u * sizeof(FRHIDispatchIndirectParameters)); // Dispatch 1 group per card tile } // 1.2 Evaluate lighting for standalone lights if (bHasStandaloneLight) { // Indirect args buffer of tiles for each standalong line + all tiles covered by standalone lights FRDGBufferRef CardTilePerLightArgs = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateIndirectDesc(NumLights + 1u),TEXT("LumenScene.DirectLighting.IndirectTileListArgsBuffer")); // Compute offset and Build indirect arts { RDG_EVENT_SCOPE(GraphBuilder, "Compact Offset & Args"); FLumenSceneCompactLightOffsetCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->NumLights = NumLights; PassParameters->NumStandaloneLights = NumStandaloneLights; PassParameters->NumSamplesPerPixel1d = NumSamplesPerPixel1d; PassParameters->CardTilePerLightCounters = GraphBuilder.CreateSRV(CardTilePerLightCounters); PassParameters->RWCardTilePerLightOffsets = GraphBuilder.CreateUAV(CardTilePerLightOffsets); PassParameters->RWCardTilePerLightArgs = GraphBuilder.CreateUAV(CardTilePerLightArgs); FLumenSceneCompactLightOffsetCS::FPermutationDomain PermutationVector; auto ComputeShader = View.ShaderMap->GetShader(PermutationVector); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("StandaloneLight::CompactOffset"), ComputeShader, PassParameters, FIntVector(1,1,1)); } // Compute list of tiles { RDG_EVENT_SCOPE(GraphBuilder, "Compact List"); FRDGBufferRef CardTilePerLightCountersForInsertion = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(4u, NumLights), TEXT("LumenScene.DirectLighting.CardTilePerLightCountersForInsertion")); FRDGBufferUAVRef RWCardTilePerLightCountersForInsertion = GraphBuilder.CreateUAV(CardTilePerLightCountersForInsertion); AddClearUAVPass(GraphBuilder, RWCardTilePerLightCountersForInsertion, 0u); FLumenSceneCompactLightListCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->IndirectArgs = CardTilePerLightArgs; PassParameters->CardTilePerLightOffsets = GraphBuilder.CreateSRV(CardTilePerLightOffsets); PassParameters->UniqueLightIndices = UniqueLightIndices; PassParameters->UniqueLightCount = UniqueLightCount; PassParameters->RWCardTilePerLightCounters = RWCardTilePerLightCountersForInsertion; PassParameters->RWCardTilePerLightDatas = GraphBuilder.CreateUAV(CardTilePerLightDatas); FLumenSceneCompactLightListCS::FPermutationDomain PermutationVector; auto ComputeShader = View.ShaderMap->GetShader(PermutationVector); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("StandaloneLight::CompactList"), ComputeShader, PassParameters, CardTilePerLightArgs, NumLights * sizeof(FRHIDispatchIndirectParameters)); } // Evaluate light FRDGTextureUAVRef LightSamplesUAVSkipBarrier = GraphBuilder.CreateUAV(LightSamples, ERDGUnorderedAccessViewFlags::SkipBarrier); FRDGTextureUAVRef SampleDiffuseLightingUAVSkipBarrier = GraphBuilder.CreateUAV(SampleDiffuseLighting, ERDGUnorderedAccessViewFlags::SkipBarrier); for (const int32 StandaloneLightIndex : LightingTaskData->StandaloneLightIndices) { const FLumenGatheredLight& Light = LightingTaskData->GatheredLights[StandaloneLightIndex]; const bool bMayUseCloudTransmittance = GLumenDirectLightingCloudTransmittance != 0 && Light.bMayCastCloudTransmittance; //check(Light.NeedsShadowMask()); // Two possible cases: // * Directional/Local Light with material light functions // * Directional light with cloud transmittance if (const FMaterialRenderProxy* LightFunctionMaterialProxy = Light.LightFunctionMaterialProxy) { FLumenSceneEvaluateStandaloneLightMaterialCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->IndirectArgs = CardTilePerLightArgs; PassParameters->LightIndex = StandaloneLightIndex; PassParameters->ViewIndex = 0; // TODO ViewIndex; PassParameters->CardTilePerLightCounters = GraphBuilder.CreateSRV(CardTilePerLightCounters); PassParameters->CardTilePerLightOffsets = GraphBuilder.CreateSRV(CardTilePerLightOffsets); PassParameters->CardTilePerLightDatas = GraphBuilder.CreateSRV(CardTilePerLightDatas); PassParameters->LumenSceneData = SceneData; PassParameters->RWLightSamples = LightSamplesUAVSkipBarrier; PassParameters->RWSampleDiffuseLighting = SampleDiffuseLightingUAVSkipBarrier; PassParameters->LumenLightData = LumenLightData; PassParameters->ViewUniformBuffer = View.ViewUniformBuffer; const bool bUseCloudTransmittance = SetupLightCloudTransmittanceParameters( GraphBuilder, Scene, View, bMayUseCloudTransmittance ? Light.LightSceneInfo : nullptr, PassParameters->LightCloudTransmittanceParameters); SetupLightFunctionParameters(View, Light.LightSceneInfo, 1.0f, PassParameters->LightFunctionParameters); 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; } FLumenSceneEvaluateStandaloneLightMaterialCS::FPermutationDomain PermutationVector; //PermutationVector.Set(Lumen::UseThreadGroupSize32()); PermutationVector.Set(bUseCloudTransmittance); const FMaterial& Material = LightFunctionMaterialProxy->GetMaterialWithFallback(Scene->GetFeatureLevel(), LightFunctionMaterialProxy); const FMaterialShaderMap* MaterialShaderMap = Material.GetRenderingThreadShaderMap(); TShaderRef ComputeShader = MaterialShaderMap->GetShader(PermutationVector); const uint32 DispatchIndirectArgOffset = StandaloneLightIndex * sizeof(FRHIDispatchIndirectParameters); ClearUnusedGraphResources(ComputeShader, PassParameters, { CardTilePerLightArgs }); GraphBuilder.AddPass( RDG_EVENT_NAME("StandaloneLight::Evaluate(LF,%s)", *Light.Name), PassParameters, ComputePassFlags, [PassParameters, ComputeShader, CardTilePerLightArgs, DispatchIndirectArgOffset, LightFunctionMaterialProxy, &Material, &View](FRDGAsyncTask, FRHIComputeCommandList& RHICmdList) { CardTilePerLightArgs->MarkResourceAsUsed(); FComputeShaderUtils::ValidateIndirectArgsBuffer(CardTilePerLightArgs, DispatchIndirectArgOffset); FRHIComputeShader* ShaderRHI = ComputeShader.GetComputeShader(); SetComputePipelineState(RHICmdList, ShaderRHI); SetShaderParameters(RHICmdList, ComputeShader, ShaderRHI, *PassParameters); ComputeShader->SetParameters(RHICmdList, ShaderRHI, LightFunctionMaterialProxy, Material, View); RHICmdList.DispatchIndirectComputeShader(CardTilePerLightArgs->GetIndirectRHICallBuffer(), DispatchIndirectArgOffset); UnsetShaderUAVs(RHICmdList, ComputeShader, ShaderRHI); }); } else { FLumenSceneEvaluateStandaloneLightCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->IndirectArgs = CardTilePerLightArgs; PassParameters->LightIndex = StandaloneLightIndex; PassParameters->ViewIndex = 0; // TODO ViewIndex; PassParameters->CardTilePerLightCounters = GraphBuilder.CreateSRV(CardTilePerLightCounters); PassParameters->CardTilePerLightOffsets = GraphBuilder.CreateSRV(CardTilePerLightOffsets); PassParameters->CardTilePerLightDatas = GraphBuilder.CreateSRV(CardTilePerLightDatas); PassParameters->LumenSceneData = SceneData; PassParameters->RWLightSamples = LightSamplesUAVSkipBarrier; PassParameters->RWSampleDiffuseLighting = SampleDiffuseLightingUAVSkipBarrier; PassParameters->LumenLightData = LumenLightData; PassParameters->ViewUniformBuffer = View.ViewUniformBuffer; const bool bUseCloudTransmittance = SetupLightCloudTransmittanceParameters( GraphBuilder, Scene, View, bMayUseCloudTransmittance ? Light.LightSceneInfo : nullptr, PassParameters->LightCloudTransmittanceParameters); 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; } FLumenSceneEvaluateStandaloneLightCS::FPermutationDomain PermutationVector; PermutationVector.Set(bUseCloudTransmittance); auto ComputeShader = View.ShaderMap->GetShader(PermutationVector); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("StandaloneLight::Evaluate(%s)", *Light.Name), ComputeShader, PassParameters, CardTilePerLightArgs, StandaloneLightIndex * sizeof(FRHIDispatchIndirectParameters)); } } } // 2. Trace compaction { RDG_EVENT_SCOPE(GraphBuilder, "Compact Traces"); CompactLumenSceneLightsTraces( View, GraphBuilder, LightSamples, CompactedLightSampleData, CompactedLightSampleAllocator); } // 3. HW Trace { RDG_EVENT_SCOPE(GraphBuilder, "HWRT Trace"); for (int32 OriginIndex = 0; OriginIndex < NumViewOrigins; ++OriginIndex) { const FViewInfo& LocalView = *FrameTemporaries.ViewOrigins[OriginIndex].ReferenceView; FLumenDirectLightingStochasticData StochasticData; StochasticData.CompactedLightSampleData = CompactedLightSampleData; StochasticData.CompactedLightSampleAllocator = CompactedLightSampleAllocator; StochasticData.LightSamples = LightSamples; StochasticData.SceneDataTexture = SceneData; TraceLumenHardwareRayTracedDirectLightingShadows( GraphBuilder, Scene, LocalView, OriginIndex, FrameTemporaries, StochasticData, LumenLightData, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, ComputePassFlags); } } // 4. Shading FRDGTextureRef ResolvedDiffuseLighting = nullptr; { RDG_EVENT_SCOPE(GraphBuilder, "Shading"); FLumenSceneShadeLightSamplesCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->DummyZeroForFixingShaderCompilerBug = 0; PassParameters->IndirectArgsBuffer = CardTileUpdateContext.DispatchCardTilesIndirectArgs; PassParameters->View = View.ViewUniformBuffer; PassParameters->LumenCardScene = FrameTemporaries.LumenCardSceneUniformBuffer; PassParameters->DiffuseColorBoost = 1.0f / FMath::Max(View.FinalPostProcessSettings.LumenDiffuseColorBoost, 1.0f); PassParameters->NumSamplesPerPixel1d = CommonParameters.NumSamplesPerPixel1d; PassParameters->AlbedoAtlas = FrameTemporaries.AlbedoAtlas; PassParameters->OpacityAtlas = FrameTemporaries.OpacityAtlas; PassParameters->EmissiveAtlas = FrameTemporaries.EmissiveAtlas; PassParameters->IndirectLightingAtlas = FrameTemporaries.IndirectLightingAtlas; PassParameters->BilinearClampedSampler = TStaticSamplerState::GetRHI(); PassParameters->RWFinalLightingAtlas = GraphBuilder.CreateUAV(FrameTemporaries.FinalLightingAtlas); PassParameters->RWDirectLightingAtlas = GraphBuilder.CreateUAV(ResolvedDirectLightingAtlas); const FIntPoint IndirectLightingAtlasSize = LumenSceneData.GetRadiosityAtlasSize(); PassParameters->IndirectLightingAtlasHalfTexelSize = FVector2f(0.5f / IndirectLightingAtlasSize.X, 0.5f / IndirectLightingAtlasSize.Y); PassParameters->TileAllocator = GraphBuilder.CreateSRV(CardTileUpdateContext.CardTileAllocator); PassParameters->TileData = GraphBuilder.CreateSRV(CardTileUpdateContext.CardTiles); PassParameters->LightSamples = LightSamples; PassParameters->SampleDiffuseLighting = SampleDiffuseLighting; PassParameters->LumenSceneDebugData = FrameTemporaries.DebugData; ShaderPrint::SetParameters(GraphBuilder, View.ShaderPrintData, PassParameters->ShaderPrintUniformBuffer); FLumenSceneShadeLightSamplesCS::FPermutationDomain PermutationVector; PermutationVector.Set(true); auto ComputeShader = View.ShaderMap->GetShader(PermutationVector); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("CombineLighting CS"), ComputePassFlags, ComputeShader, PassParameters, CardTileUpdateContext.DispatchCardTilesIndirectArgs, (uint32)ELumenDispatchCardTilesIndirectArgsOffset::OneGroupPerCardTile); } // 5. Temporal accumulation if (bTemporal) { RDG_EVENT_SCOPE(GraphBuilder, "Temporal Filtering"); const FIntPoint Resolution = FrameTemporaries.DirectLightingAtlas->Desc.Extent; FRDGTextureRef DiffuseLightingAndSecondMoment = GraphBuilder.CreateTexture( FRDGTextureDesc::Create2D(Resolution, PF_FloatRGBA, FClearValueBinding::Black, TexCreate_ShaderResource | TexCreate_UAV), TEXT("Lumen.SceneLighting.DiffuseLightingAndSecondMoment")); FRDGTextureRef NumFramesAccumulated = GraphBuilder.CreateTexture( FRDGTextureDesc::Create2D(Resolution, PF_G8, FClearValueBinding::Black, TexCreate_ShaderResource | TexCreate_UAV), TEXT("Lumen.SceneLighting.NumFramesAccumulated")); FLumenSceneDenoiserTemporalCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->IndirectArgsBuffer = CardTileUpdateContext.DispatchCardTilesIndirectArgs; PassParameters->CommonParameters = CommonParameters; PassParameters->LumenCardScene = FrameTemporaries.LumenCardSceneUniformBuffer; PassParameters->SampleLuminanceSumTexture = SampleLuminanceSum; PassParameters->ResolvedDirectLightingAtlas = ResolvedDirectLightingAtlas; PassParameters->DiffuseLightingAndSecondMomentHistoryTexture = FrameTemporaries.DiffuseLightingAndSecondMomentHistoryAtlas; // DiffuseLightingAndSecondMomentHistory; PassParameters->NumFramesAccumulatedHistoryTexture = FrameTemporaries.NumFramesAccumulatedHistoryAtlas;// NumFramesAccumulatedHistory; PassParameters->PrevSceneColorPreExposureCorrection = View.PreExposure / View.PrevViewInfo.SceneColorPreExposure; PassParameters->RWDiffuseLightingAndSecondMoment = GraphBuilder.CreateUAV(DiffuseLightingAndSecondMoment); PassParameters->RWNumFramesAccumulated = GraphBuilder.CreateUAV(NumFramesAccumulated); PassParameters->TileAllocator = GraphBuilder.CreateSRV(CardTileUpdateContext.CardTileAllocator); PassParameters->TileData = GraphBuilder.CreateSRV(CardTileUpdateContext.CardTiles); const FIntPoint IndirectLightingAtlasSize = LumenSceneData.GetRadiosityAtlasSize(); PassParameters->IndirectLightingAtlasHalfTexelSize = FVector2f(0.5f / IndirectLightingAtlasSize.X, 0.5f / IndirectLightingAtlasSize.Y); PassParameters->AlbedoAtlas = FrameTemporaries.AlbedoAtlas; PassParameters->OpacityAtlas = FrameTemporaries.OpacityAtlas; PassParameters->EmissiveAtlas = FrameTemporaries.EmissiveAtlas; PassParameters->IndirectLightingAtlas = FrameTemporaries.IndirectLightingAtlas; PassParameters->BilinearClampedSampler = TStaticSamplerState::GetRHI(); PassParameters->RWFinalLightingAtlas = GraphBuilder.CreateUAV(FrameTemporaries.FinalLightingAtlas); PassParameters->RWDirectLightingAtlas = GraphBuilder.CreateUAV(FrameTemporaries.DirectLightingAtlas); FLumenSceneDenoiserTemporalCS::FPermutationDomain PermutationVector; PermutationVector.Set(FrameTemporaries.DiffuseLightingAndSecondMomentHistoryAtlas != nullptr && bTemporal); auto ComputeShader = View.ShaderMap->GetShader(PermutationVector); const FIntVector GroupCount = FComputeShaderUtils::GetGroupCount(View.ViewRect.Size(), FLumenSceneDenoiserTemporalCS::GetGroupSize()); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("TemporalAccumulation"), ComputeShader, PassParameters, CardTileUpdateContext.DispatchCardTilesIndirectArgs, (uint32)ELumenDispatchCardTilesIndirectArgsOffset::OneGroupPerCardTile); FLumenSceneFrameTemporaries* NonCstFrameTemporaries = const_cast(&FrameTemporaries); if (DiffuseLightingAndSecondMoment && NumFramesAccumulated && bTemporal && NonCstFrameTemporaries) { NonCstFrameTemporaries->DiffuseLightingAndSecondMomentHistoryAtlas = DiffuseLightingAndSecondMoment; NonCstFrameTemporaries->NumFramesAccumulatedHistoryAtlas = NumFramesAccumulated; } else { NonCstFrameTemporaries->DiffuseLightingAndSecondMomentHistoryAtlas = nullptr; NonCstFrameTemporaries->NumFramesAccumulatedHistoryAtlas = nullptr; } } // Draw direct lighting stats & Lumen cards/tiles if (GetLumenLightingStatMode() == 3) { AddLumenSceneDirectLightingStatsPass( GraphBuilder, Scene, View, FrameTemporaries, LightingTaskData, CardUpdateContext, CardTileUpdateContext, CompactedLightSampleAllocator, ComputePassFlags); } } } ///////////////////////////////////////////////////////////////////////////////////////////////////