// Copyright Epic Games, Inc. All Rights Reserved. /*============================================================================= LumenMeshSDFCulling.cpp =============================================================================*/ #include "RendererPrivate.h" #include "LightRendering.h" #include "ScenePrivate.h" #include "SceneUtils.h" #include "PipelineStateCache.h" #include "ShaderParameterStruct.h" #include "PixelShaderUtils.h" #include "DistanceFieldLightingShared.h" #include "LumenMeshCards.h" #include "LumenTracingUtils.h" int32 GMeshSDFAverageCulledCount = 512; FAutoConsoleVariableRef CVarMeshSDFAverageCulledCount( TEXT("r.Lumen.DiffuseIndirect.MeshSDF.AverageCulledCount"), GMeshSDFAverageCulledCount, TEXT(""), ECVF_Scalability | ECVF_RenderThreadSafe ); float GMeshSDFRadiusThreshold = 30; FAutoConsoleVariableRef CVarMeshSDFRadiusThreshold( TEXT("r.Lumen.DiffuseIndirect.MeshSDF.RadiusThreshold"), GMeshSDFRadiusThreshold, TEXT(""), ECVF_Scalability | ECVF_RenderThreadSafe ); float GMeshSDFNotCoveredExpandSurfaceScale = .6f; FAutoConsoleVariableRef CVarMeshSDFNotCoveredExpandSurfaceScale( TEXT("r.Lumen.DiffuseIndirect.MeshSDF.NotCoveredExpandSurfaceScale"), GMeshSDFNotCoveredExpandSurfaceScale, TEXT("Scales the surface expand used for Mesh SDFs that contain mostly two sided materials (foliage)"), ECVF_Scalability | ECVF_RenderThreadSafe ); float GMeshSDFNotCoveredMinStepScale = 32; FAutoConsoleVariableRef CVarMeshSDFNotCoveredMinStepScale( TEXT("r.Lumen.DiffuseIndirect.MeshSDF.NotCoveredMinStepScale"), GMeshSDFNotCoveredMinStepScale, TEXT("Scales the min step size to improve performance, for Mesh SDFs that contain mostly two sided materials (foliage)"), ECVF_Scalability | ECVF_RenderThreadSafe ); float GMeshSDFDitheredTransparencyStepThreshold = .1f; FAutoConsoleVariableRef CVarMeshSDFDitheredTransparencyStepThreshold( TEXT("r.Lumen.DiffuseIndirect.MeshSDF.DitheredTransparencyStepThreshold"), GMeshSDFDitheredTransparencyStepThreshold, TEXT("Per-step stochastic semi-transparency threshold, for tracing users that have dithered transparency enabled, for Mesh SDFs that contain mostly two sided materials (foliage)"), ECVF_Scalability | ECVF_RenderThreadSafe ); static TAutoConsoleVariable CVarLumenSceneHeightfieldCullForView( TEXT("r.LumenScene.Heightfield.CullForView"), 1, TEXT("Enables Heightfield culling (default = 1)"), ECVF_RenderThreadSafe ); static TAutoConsoleVariable CVarLumenSceneHeightfieldFroxelCulling( TEXT("r.LumenScene.Heightfield.FroxelCulling"), 1, TEXT("Enables Heightfield froxel view culling (default = 1)"), ECVF_RenderThreadSafe ); void SetupLumenMeshSDFTracingParameters( FRDGBuilder& GraphBuilder, const FScene* Scene, const FViewInfo& View, FLumenMeshSDFTracingParameters& OutParameters) { const FDistanceFieldSceneData& DistanceFieldSceneData = Scene->DistanceFieldSceneData; OutParameters.DistanceFieldObjectBuffers = DistanceField::SetupObjectBufferParameters(GraphBuilder, DistanceFieldSceneData); OutParameters.DistanceFieldAtlas = DistanceField::SetupAtlasParameters(GraphBuilder, DistanceFieldSceneData); OutParameters.MeshSDFNotCoveredExpandSurfaceScale = GMeshSDFNotCoveredExpandSurfaceScale; OutParameters.MeshSDFNotCoveredMinStepScale = GMeshSDFNotCoveredMinStepScale; OutParameters.MeshSDFDitheredTransparencyStepThreshold = GMeshSDFDitheredTransparencyStepThreshold; } uint32 CullMeshSDFObjectsForViewGroupSize = 64; class FCullMeshSDFObjectsForViewCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FCullMeshSDFObjectsForViewCS) SHADER_USE_PARAMETER_STRUCT(FCullMeshSDFObjectsForViewCS, FGlobalShader) BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, RWNumCulledObjects) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, RWObjectIndexBuffer) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, RWObjectIndirectArguments) SHADER_PARAMETER_STRUCT_INCLUDE(FDistanceFieldObjectBufferParameters, DistanceFieldObjectBuffers) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View) SHADER_PARAMETER(uint32, NumConvexHullPlanes) SHADER_PARAMETER_ARRAY(FVector4f, ViewFrustumConvexHull, [6]) SHADER_PARAMETER(uint32, ObjectBoundingGeometryIndexCount) SHADER_PARAMETER(float, CardTraceEndDistanceFromCamera) SHADER_PARAMETER(float, MaxMeshSDFInfluenceRadius) SHADER_PARAMETER(float, MeshSDFRadiusThreshold) 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"), CullMeshSDFObjectsForViewGroupSize); } }; IMPLEMENT_GLOBAL_SHADER(FCullMeshSDFObjectsForViewCS, "/Engine/Private/Lumen/LumenMeshSDFCulling.usf", "CullMeshSDFObjectsForViewCS", SF_Compute); class FCombineObjectIndexBuffersCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FCombineObjectIndexBuffersCS); SHADER_USE_PARAMETER_STRUCT(FCombineObjectIndexBuffersCS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer, MeshSDFIndexBuffer) SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer, HeightfieldIndexBuffer) SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer, NumCulledMeshSDFObjects) SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer, NumCulledHeightfieldObjects) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, RWCombinedObjectIndexBuffer) 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"), CullMeshSDFObjectsForViewGroupSize); } }; IMPLEMENT_GLOBAL_SHADER(FCombineObjectIndexBuffersCS, "/Engine/Private/Lumen/LumenMeshSDFCulling.usf", "CombineObjectIndexBuffersCS", SF_Compute); class FMeshSDFObjectCullVS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FMeshSDFObjectCullVS); SHADER_USE_PARAMETER_STRUCT(FMeshSDFObjectCullVS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer, ObjectIndexBuffer) // SDF parameters SHADER_PARAMETER_STRUCT_INCLUDE(FDistanceFieldObjectBufferParameters, DistanceFieldObjectBuffers) // Heightfield parameters SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FLumenCardScene, LumenCardScene) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View) SHADER_PARAMETER(float, ConservativeRadiusScale) SHADER_PARAMETER(float, MaxMeshSDFInfluenceRadius) END_SHADER_PARAMETER_STRUCT() class FCullMeshTypeSDF : SHADER_PERMUTATION_BOOL("CULL_MESH_SDF"); class FCullMeshTypeHeightfield : SHADER_PERMUTATION_BOOL("CULL_MESH_HEIGHTFIELD"); using FPermutationDomain = TShaderPermutationDomain; static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return DoesPlatformSupportLumenGI(Parameters.Platform); } }; IMPLEMENT_GLOBAL_SHADER(FMeshSDFObjectCullVS, "/Engine/Private/Lumen/LumenMeshSDFCulling.usf", "MeshSDFObjectCullVS", SF_Vertex); class FMeshSDFObjectCullPS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FMeshSDFObjectCullPS); SHADER_USE_PARAMETER_STRUCT(FMeshSDFObjectCullPS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, RWNumGridCulledMeshSDFObjects) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, RWNumGridCulledHeightfieldObjects) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, RWNumCulledObjectsToCompact) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, RWCulledObjectsToCompactArray) SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer, GridCulledMeshSDFObjectStartOffsetArray) // SDF parameters SHADER_PARAMETER_STRUCT_INCLUDE(FDistanceFieldObjectBufferParameters, DFObjectBufferParameters) SHADER_PARAMETER_STRUCT_INCLUDE(FDistanceFieldAtlasParameters, DistanceFieldAtlas) // Heightfield parameters SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FLumenCardScene, LumenCardScene) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View) SHADER_PARAMETER(float, MaxMeshSDFInfluenceRadius) SHADER_PARAMETER(FVector3f, CardGridZParams) SHADER_PARAMETER(uint32, CardGridPixelSizeShift) SHADER_PARAMETER(FIntVector, CullGridSize) SHADER_PARAMETER(float, CardTraceEndDistanceFromCamera) SHADER_PARAMETER(uint32, MaxNumberOfCulledObjects) SHADER_PARAMETER(float, HZBMipLevel) SHADER_PARAMETER_STRUCT_INCLUDE(FHZBParameters, HZBParameters) END_SHADER_PARAMETER_STRUCT() class FCullToFroxelGrid : SHADER_PERMUTATION_BOOL("CULL_TO_FROXEL_GRID"); class FCullMeshTypeSDF : SHADER_PERMUTATION_BOOL("CULL_MESH_SDF"); class FCullMeshTypeHeightfield : SHADER_PERMUTATION_BOOL("CULL_MESH_HEIGHTFIELD"); class FOffsetDataStructure : SHADER_PERMUTATION_INT("OFFSET_DATA_STRUCT", 3); using FPermutationDomain = TShaderPermutationDomain; static FPermutationDomain RemapPermutation(FPermutationDomain PermutationVector) { // FOffsetDataStructure is only used for mesh SDFs if (!PermutationVector.Get()) { PermutationVector.Set(0); } return PermutationVector; } static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { FPermutationDomain PermutationVector(Parameters.PermutationId); if (RemapPermutation(PermutationVector) != PermutationVector) { return false; } return DoesPlatformSupportLumenGI(Parameters.Platform); } }; IMPLEMENT_GLOBAL_SHADER(FMeshSDFObjectCullPS, "/Engine/Private/Lumen/LumenMeshSDFCulling.usf", "MeshSDFObjectCullPS", SF_Pixel); BEGIN_SHADER_PARAMETER_STRUCT(FMeshSDFObjectCull, ) SHADER_PARAMETER_STRUCT_INCLUDE(FMeshSDFObjectCullVS::FParameters, VS) SHADER_PARAMETER_STRUCT_INCLUDE(FMeshSDFObjectCullPS::FParameters, PS) RDG_BUFFER_ACCESS(MeshSDFIndirectArgs, ERHIAccess::IndirectArgs) RENDER_TARGET_BINDING_SLOTS() END_SHADER_PARAMETER_STRUCT() class FCompactCulledObjectsCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FCompactCulledObjectsCS); SHADER_USE_PARAMETER_STRUCT(FCompactCulledObjectsCS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) // Mesh SDF SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer, GridCulledMeshSDFObjectStartOffsetArray) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, RWNumGridCulledMeshSDFObjects) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, RWGridCulledMeshSDFObjectIndicesArray) // Heightfield SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer, GridCulledHeightfieldObjectStartOffsetArray) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, RWNumGridCulledHeightfieldObjects) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, RWGridCulledHeightfieldObjectIndicesArray) // Type-agnostic data SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer, NumCulledObjectsToCompact) SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer, CulledObjectsToCompactArray) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View) RDG_BUFFER_ACCESS(CompactCulledObjectsIndirectArguments, ERHIAccess::IndirectArgs) SHADER_PARAMETER(uint32, MaxNumberOfCulledObjects) END_SHADER_PARAMETER_STRUCT() class FCullMeshTypeSDF : SHADER_PERMUTATION_BOOL("CULL_MESH_SDF"); class FCullMeshTypeHeightfield : SHADER_PERMUTATION_BOOL("CULL_MESH_HEIGHTFIELD"); using FPermutationDomain = TShaderPermutationDomain; static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return DoesPlatformSupportLumenGI(Parameters.Platform); } static int32 GetGroupSize() { return 64; } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE"), GetGroupSize()); } }; IMPLEMENT_GLOBAL_SHADER(FCompactCulledObjectsCS, "/Engine/Private/Lumen/LumenMeshSDFCulling.usf", "CompactCulledObjectsCS", SF_Compute); uint32 ComputeCulledMeshSDFObjectsStartOffsetGroupSize = 64; class FComputeCulledObjectsStartOffsetCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FComputeCulledObjectsStartOffsetCS) SHADER_USE_PARAMETER_STRUCT(FComputeCulledObjectsStartOffsetCS, FGlobalShader) BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) // Mesh SDF SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer, NumGridCulledMeshSDFObjects) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, RWGridCulledMeshSDFObjectStartOffsetArray) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, RWCulledMeshSDFObjectAllocator) // Heightfield SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer, NumGridCulledHeightfieldObjects) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, RWGridCulledHeightfieldObjectStartOffsetArray) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, RWCulledHeightfieldObjectAllocator) // Type-agnostic SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, RWCompactCulledObjectsIndirectArguments) SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer, NumCulledObjectsToCompact) SHADER_PARAMETER(uint32, NumCullGridCells) 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"), ComputeCulledMeshSDFObjectsStartOffsetGroupSize); } }; IMPLEMENT_GLOBAL_SHADER(FComputeCulledObjectsStartOffsetCS, "/Engine/Private/Lumen/LumenMeshSDFCulling.usf", "ComputeCulledObjectsStartOffsetCS", SF_Compute); class FObjectCullingContext { public: uint32 NumCullGridCells = 0; uint32 MaxNumberOfCulledObjects = 0; // View culled object data FRDGBufferRef NumMeshSDFCulledObjects = nullptr; FRDGBufferRef MeshSDFObjectIndexBuffer = nullptr; FRDGBufferRef NumHeightfieldCulledObjects = nullptr; FRDGBufferRef HeightfieldObjectIndexBuffer = nullptr; // Froxel-culled object data FRDGBufferRef NumGridCulledMeshSDFObjects = nullptr; FRDGBufferRef GridCulledMeshSDFObjectStartOffsetArray = nullptr; FRDGBufferRef GridCulledMeshSDFObjectIndicesArray = nullptr; FRDGBufferRef NumGridCulledHeightfieldObjects = nullptr; FRDGBufferRef GridCulledHeightfieldObjectStartOffsetArray = nullptr; FRDGBufferRef GridCulledHeightfieldObjectIndicesArray = nullptr; // Intermediary buffers FRDGBufferRef ObjectIndirectArguments = nullptr; FRDGBufferRef NumCulledObjectsToCompact = nullptr; FRDGBufferRef CulledObjectsToCompactArray = nullptr; }; void InitObjectCullingContext( FRDGBuilder& GraphBuilder, uint32 NumCullGridCells, FObjectCullingContext& Context) { Context.NumCullGridCells = NumCullGridCells; Context.MaxNumberOfCulledObjects = NumCullGridCells * GMeshSDFAverageCulledCount; Context.NumGridCulledMeshSDFObjects = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), NumCullGridCells), TEXT("Lumen.NumGridCulledMeshSDFObjects")); Context.NumGridCulledHeightfieldObjects = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), NumCullGridCells), TEXT("Lumen.NumGridCulledHeightfieldObjects")); Context.GridCulledMeshSDFObjectIndicesArray = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), Context.MaxNumberOfCulledObjects), TEXT("Lumen.GridCulledMeshSDFObjectIndicesArray")); Context.GridCulledHeightfieldObjectIndicesArray = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), Context.MaxNumberOfCulledObjects), TEXT("Lumen.GridCulledHeightfieldObjectIndicesArray")); Context.NumCulledObjectsToCompact = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), 1), TEXT("Lumen.NumCulledObjectsToCompact")); Context.CulledObjectsToCompactArray = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), 2 * Context.MaxNumberOfCulledObjects), TEXT("Lumen.CulledObjectsToCompactArray")); AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(Context.NumGridCulledMeshSDFObjects, PF_R32_UINT), 0); AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(Context.NumGridCulledHeightfieldObjects, PF_R32_UINT), 0); AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(Context.NumCulledObjectsToCompact, PF_R32_UINT), 0); } void FillGridParameters( FRDGBuilder& GraphBuilder, const FScene* Scene, const FViewInfo& View, const FObjectCullingContext* Context, FLumenMeshSDFGridParameters& OutGridParameters) { const FDistanceFieldSceneData& DistanceFieldSceneData = Scene->DistanceFieldSceneData; SetupLumenMeshSDFTracingParameters(GraphBuilder, Scene, View, OutGridParameters.TracingParameters); if (Context) { bool bCullMeshSDFObjects = DistanceFieldSceneData.NumObjectsInBuffer > 0; if (bCullMeshSDFObjects) { // Froxel-culled data OutGridParameters.NumGridCulledMeshSDFObjects = GraphBuilder.CreateSRV(Context->NumGridCulledMeshSDFObjects, PF_R32_UINT); OutGridParameters.GridCulledMeshSDFObjectStartOffsetArray = GraphBuilder.CreateSRV(Context->GridCulledMeshSDFObjectStartOffsetArray, PF_R32_UINT); OutGridParameters.GridCulledMeshSDFObjectIndicesArray = GraphBuilder.CreateSRV(Context->GridCulledMeshSDFObjectIndicesArray, PF_R32_UINT); } bool bCullHeightfieldObjects = Lumen::UseHeightfieldTracing(*View.Family, *Scene->GetLumenSceneData(View)); if (bCullHeightfieldObjects) { // View-culled heightfield objects OutGridParameters.NumCulledHeightfieldObjects = GraphBuilder.CreateSRV(Context->NumHeightfieldCulledObjects, PF_R32_UINT); OutGridParameters.CulledHeightfieldObjectIndexBuffer = GraphBuilder.CreateSRV(Context->HeightfieldObjectIndexBuffer, PF_R32_UINT); // Froxel-culled heightfield objects are optionally set, depending on the method if (Context->NumGridCulledHeightfieldObjects) { OutGridParameters.NumGridCulledHeightfieldObjects = GraphBuilder.CreateSRV(Context->NumGridCulledHeightfieldObjects, PF_R32_UINT); OutGridParameters.GridCulledHeightfieldObjectStartOffsetArray = GraphBuilder.CreateSRV(Context->GridCulledHeightfieldObjectStartOffsetArray, PF_R32_UINT); OutGridParameters.GridCulledHeightfieldObjectIndicesArray = GraphBuilder.CreateSRV(Context->GridCulledHeightfieldObjectIndicesArray, PF_R32_UINT); } } } else { OutGridParameters.NumGridCulledMeshSDFObjects = nullptr; OutGridParameters.GridCulledMeshSDFObjectStartOffsetArray = nullptr; OutGridParameters.GridCulledMeshSDFObjectIndicesArray = nullptr; OutGridParameters.NumGridCulledHeightfieldObjects = nullptr; OutGridParameters.GridCulledHeightfieldObjectStartOffsetArray = nullptr; OutGridParameters.GridCulledHeightfieldObjectIndicesArray = nullptr; OutGridParameters.NumCulledHeightfieldObjects = nullptr; OutGridParameters.CulledHeightfieldObjectIndexBuffer = nullptr; } } class FCullHeightfieldObjectsForViewCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FCullHeightfieldObjectsForViewCS) SHADER_USE_PARAMETER_STRUCT(FCullHeightfieldObjectsForViewCS, FGlobalShader) BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FLumenCardScene, LumenCardScene) SHADER_PARAMETER(float, CardTraceEndDistanceFromCamera) SHADER_PARAMETER(float, MaxMeshSDFInfluenceRadius) SHADER_PARAMETER(int, MaxNumObjects) SHADER_PARAMETER(int, bShouldCull) SHADER_PARAMETER(uint32, ObjectBoundingGeometryIndexCount) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, RWNumCulledObjects) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, RWCulledObjectIndexBuffer) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, RWObjectIndirectArguments) 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"), CullMeshSDFObjectsForViewGroupSize); } }; IMPLEMENT_GLOBAL_SHADER(FCullHeightfieldObjectsForViewCS, "/Engine/Private/Lumen/LumenMeshSDFCulling.usf", "CullHeightfieldObjectsForViewCS", SF_Compute); void CombineObjectIndexBuffers( FRDGBuilder& GraphBuilder, const FScene* Scene, const FViewInfo& View, bool bCullMeshSDFObjects, bool bCullHeightfieldObjects, FObjectCullingContext& Context, FRDGBufferRef& CombinedObjectIndexBuffer ) { const FDistanceFieldSceneData& DistanceFieldSceneData = Scene->DistanceFieldSceneData; const FLumenSceneData& LumenSceneData = *Scene->GetLumenSceneData(View); if (bCullMeshSDFObjects && bCullHeightfieldObjects) { uint32 NumDistanceFields = DistanceFieldSceneData.NumObjectsInBuffer; uint32 NumHeightfields = LumenSceneData.Heightfields.Num(); uint32 MaxNumObjects = FMath::RoundUpToPowerOfTwo(NumDistanceFields + NumHeightfields); CombinedObjectIndexBuffer = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), MaxNumObjects), TEXT("Lumen.CombinedObjectIndexBuffer")); FCombineObjectIndexBuffersCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); { PassParameters->MeshSDFIndexBuffer = GraphBuilder.CreateSRV(Context.MeshSDFObjectIndexBuffer, PF_R32_UINT); PassParameters->HeightfieldIndexBuffer = GraphBuilder.CreateSRV(Context.HeightfieldObjectIndexBuffer, PF_R32_UINT); PassParameters->NumCulledMeshSDFObjects = GraphBuilder.CreateSRV(Context.NumMeshSDFCulledObjects, PF_R32_UINT); PassParameters->NumCulledHeightfieldObjects = GraphBuilder.CreateSRV(Context.NumHeightfieldCulledObjects, PF_R32_UINT); PassParameters->RWCombinedObjectIndexBuffer = GraphBuilder.CreateUAV(CombinedObjectIndexBuffer, PF_R32_UINT); } auto ComputeShader = View.ShaderMap->GetShader(); const int32 GroupSize = FMath::DivideAndRoundUp(NumDistanceFields + NumHeightfields, CullMeshSDFObjectsForViewGroupSize); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("CombineObjectIndexBuffers"), ComputeShader, PassParameters, FIntVector(GroupSize, 1, 1)); } else if (bCullHeightfieldObjects) { CombinedObjectIndexBuffer = Context.HeightfieldObjectIndexBuffer; } else //if (bCullMeshSDFObjects) { CombinedObjectIndexBuffer = Context.MeshSDFObjectIndexBuffer; } } void CullHeightfieldObjectsForView( FRDGBuilder& GraphBuilder, const FScene* Scene, const FViewInfo& View, const FLumenSceneFrameTemporaries& FrameTemporaries, float MaxMeshSDFInfluenceRadius, float CardTraceEndDistanceFromCamera, FRDGBufferRef& NumHeightfieldCulledObjects, FRDGBufferRef& HeightfieldObjectIndexBuffer, FRDGBufferRef& HeightfieldObjectIndirectArguments) { const FLumenSceneData& LumenSceneData = *Scene->GetLumenSceneData(View); // We don't want any heightfield overhead if there are no heightfields in the scene check(Lumen::UseHeightfieldTracing(*View.Family, LumenSceneData)); uint32 NumHeightfields = LumenSceneData.Heightfields.Num(); uint32 MaxNumHeightfields = FMath::RoundUpToPowerOfTwo(LumenSceneData.Heightfields.Num()); NumHeightfieldCulledObjects = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), 2), TEXT("Lumen.NumCulledHeightfieldObjects")); HeightfieldObjectIndexBuffer = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), MaxNumHeightfields), TEXT("Lumen.CulledHeightfieldObjectIndices")); AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(NumHeightfieldCulledObjects, PF_R32_UINT), 0); FCullHeightfieldObjectsForViewCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); { PassParameters->View = View.ViewUniformBuffer; PassParameters->LumenCardScene = FrameTemporaries.LumenCardSceneUniformBuffer; PassParameters->CardTraceEndDistanceFromCamera = CardTraceEndDistanceFromCamera; PassParameters->MaxMeshSDFInfluenceRadius = MaxMeshSDFInfluenceRadius; PassParameters->MaxNumObjects = NumHeightfields; PassParameters->bShouldCull = CVarLumenSceneHeightfieldCullForView.GetValueOnRenderThread() != 0; PassParameters->ObjectBoundingGeometryIndexCount = StencilingGeometry::GLowPolyStencilSphereIndexBuffer.GetIndexCount(); PassParameters->RWNumCulledObjects = GraphBuilder.CreateUAV(NumHeightfieldCulledObjects, PF_R32_UINT); PassParameters->RWCulledObjectIndexBuffer = GraphBuilder.CreateUAV(HeightfieldObjectIndexBuffer, PF_R32_UINT); PassParameters->RWObjectIndirectArguments = GraphBuilder.CreateUAV(HeightfieldObjectIndirectArguments, PF_R32_UINT); } auto ComputeShader = View.ShaderMap->GetShader(); const int32 GroupSize = FMath::DivideAndRoundUp(NumHeightfields, CullMeshSDFObjectsForViewGroupSize); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("CullHeightfieldsForView"), ComputeShader, PassParameters, FIntVector(GroupSize, 1, 1)); } void CullMeshSDFObjectsForView( FRDGBuilder& GraphBuilder, const FScene* Scene, const FViewInfo& View, float MaxMeshSDFInfluenceRadius, float CardTraceEndDistanceFromCamera, FObjectCullingContext& Context) { const FLumenSceneData& LumenSceneData = *Scene->GetLumenSceneData(View); const FDistanceFieldSceneData& DistanceFieldSceneData = Scene->DistanceFieldSceneData; int32 MaxSDFMeshObjects = FMath::RoundUpToPowerOfTwo(DistanceFieldSceneData.NumObjectsInBuffer); MaxSDFMeshObjects = FMath::DivideAndRoundUp(MaxSDFMeshObjects, 128) * 128; Context.NumMeshSDFCulledObjects = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), 1), TEXT("Lumen.NumMeshSDFCulledObjects")); Context.MeshSDFObjectIndexBuffer = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), MaxSDFMeshObjects), TEXT("Lumen.MeshSDFObjectIndexBuffer")); AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(Context.NumMeshSDFCulledObjects, PF_R32_UINT), 0); { FCullMeshSDFObjectsForViewCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->RWNumCulledObjects = GraphBuilder.CreateUAV(Context.NumMeshSDFCulledObjects, PF_R32_UINT); PassParameters->RWObjectIndexBuffer = GraphBuilder.CreateUAV(Context.MeshSDFObjectIndexBuffer, PF_R32_UINT); PassParameters->RWObjectIndirectArguments = GraphBuilder.CreateUAV(Context.ObjectIndirectArguments, PF_R32_UINT); PassParameters->DistanceFieldObjectBuffers = DistanceField::SetupObjectBufferParameters(GraphBuilder, DistanceFieldSceneData); PassParameters->View = View.ViewUniformBuffer; PassParameters->NumConvexHullPlanes = View.ViewFrustum.Planes.Num(); for (int32 i = 0; i < View.ViewFrustum.Planes.Num(); i++) { PassParameters->ViewFrustumConvexHull[i] = FVector4f((FVector3f)View.ViewFrustum.Planes[i], View.ViewFrustum.Planes[i].W); } PassParameters->ObjectBoundingGeometryIndexCount = StencilingGeometry::GLowPolyStencilSphereIndexBuffer.GetIndexCount(); PassParameters->CardTraceEndDistanceFromCamera = CardTraceEndDistanceFromCamera; PassParameters->MaxMeshSDFInfluenceRadius = MaxMeshSDFInfluenceRadius; PassParameters->MeshSDFRadiusThreshold = GMeshSDFRadiusThreshold / FMath::Clamp(View.FinalPostProcessSettings.LumenSceneDetail, .01f, 100.0f); auto ComputeShader = View.ShaderMap->GetShader(); const int32 GroupSize = FMath::DivideAndRoundUp(DistanceFieldSceneData.NumObjectsInBuffer, CullMeshSDFObjectsForViewGroupSize); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("CullMeshSDFObjectsForView"), ComputeShader, PassParameters, FIntVector(GroupSize, 1, 1)); } } // Compact list of {ObjectIndex, GridCellIndex} into a continuous array void CompactCulledObjectArray( FRDGBuilder& GraphBuilder, const FScene* Scene, const FViewInfo& View, FObjectCullingContext& Context, ERDGPassFlags ComputePassFlags) { Context.GridCulledMeshSDFObjectStartOffsetArray = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), Context.NumCullGridCells), TEXT("Lumen.GridCulledMeshSDFObjectStartOffsetArray")); Context.GridCulledHeightfieldObjectStartOffsetArray = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), Context.NumCullGridCells), TEXT("Lumen.GridCulledHeightfieldObjectStartOffsetArray")); FRDGBufferRef CulledMeshSDFObjectAllocator = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), 1), TEXT("Lumen.CulledMeshSDFObjectAllocator")); FRDGBufferRef CulledHeightfieldObjectAllocator = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), 1), TEXT("Lumen.CulledHeightfieldObjectAllocator")); FRDGBufferRef CompactCulledObjectsIndirectArguments = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateIndirectDesc(1), TEXT("Lumen.CompactCulledObjectsIndirectArguments")); AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(CulledMeshSDFObjectAllocator, PF_R32_UINT), 0, ComputePassFlags); AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(CulledHeightfieldObjectAllocator, PF_R32_UINT), 0, ComputePassFlags); { FComputeCulledObjectsStartOffsetCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); { // Mesh SDF PassParameters->NumGridCulledMeshSDFObjects = GraphBuilder.CreateSRV(Context.NumGridCulledMeshSDFObjects, PF_R32_UINT); PassParameters->RWGridCulledMeshSDFObjectStartOffsetArray = GraphBuilder.CreateUAV(Context.GridCulledMeshSDFObjectStartOffsetArray, PF_R32_UINT); PassParameters->RWCulledMeshSDFObjectAllocator = GraphBuilder.CreateUAV(CulledMeshSDFObjectAllocator, PF_R32_UINT); // Heightfield PassParameters->NumGridCulledHeightfieldObjects = GraphBuilder.CreateSRV(Context.NumGridCulledHeightfieldObjects, PF_R32_UINT); PassParameters->RWGridCulledHeightfieldObjectStartOffsetArray = GraphBuilder.CreateUAV(Context.GridCulledHeightfieldObjectStartOffsetArray, PF_R32_UINT); PassParameters->RWCulledHeightfieldObjectAllocator = GraphBuilder.CreateUAV(CulledHeightfieldObjectAllocator, PF_R32_UINT); // Type-agnostic PassParameters->RWCompactCulledObjectsIndirectArguments = GraphBuilder.CreateUAV(CompactCulledObjectsIndirectArguments, PF_R32_UINT); PassParameters->NumCulledObjectsToCompact = GraphBuilder.CreateSRV(Context.NumCulledObjectsToCompact, PF_R32_UINT); PassParameters->NumCullGridCells = Context.NumCullGridCells; } auto ComputeShader = View.ShaderMap->GetShader(); int32 GroupSize = FMath::DivideAndRoundUp(Context.NumCullGridCells, ComputeCulledMeshSDFObjectsStartOffsetGroupSize); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("ComputeCulledObjectsStartOffsetCS"), ComputePassFlags, ComputeShader, PassParameters, FIntVector(GroupSize, 1, 1)); } FRDGBufferUAVRef NumGridCulledMeshSDFObjectsUAV = GraphBuilder.CreateUAV(Context.NumGridCulledMeshSDFObjects, PF_R32_UINT); FRDGBufferUAVRef NumGridCulledHeightfieldObjectsUAV = GraphBuilder.CreateUAV(Context.NumGridCulledHeightfieldObjects, PF_R32_UINT); AddClearUAVPass(GraphBuilder, NumGridCulledMeshSDFObjectsUAV, 0, ComputePassFlags); AddClearUAVPass(GraphBuilder, NumGridCulledHeightfieldObjectsUAV, 0, ComputePassFlags); { FCompactCulledObjectsCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); { // Mesh SDF PassParameters->GridCulledMeshSDFObjectStartOffsetArray = GraphBuilder.CreateSRV(Context.GridCulledMeshSDFObjectStartOffsetArray, PF_R32_UINT); PassParameters->RWNumGridCulledMeshSDFObjects = NumGridCulledMeshSDFObjectsUAV; PassParameters->RWGridCulledMeshSDFObjectIndicesArray = GraphBuilder.CreateUAV(Context.GridCulledMeshSDFObjectIndicesArray, PF_R32_UINT); // Heightfield PassParameters->GridCulledHeightfieldObjectStartOffsetArray = GraphBuilder.CreateSRV(Context.GridCulledHeightfieldObjectStartOffsetArray, PF_R32_UINT); PassParameters->RWNumGridCulledHeightfieldObjects = NumGridCulledHeightfieldObjectsUAV; PassParameters->RWGridCulledHeightfieldObjectIndicesArray = GraphBuilder.CreateUAV(Context.GridCulledHeightfieldObjectIndicesArray, PF_R32_UINT); // Type-agnostic PassParameters->NumCulledObjectsToCompact = GraphBuilder.CreateSRV(Context.NumCulledObjectsToCompact, PF_R32_UINT); PassParameters->CulledObjectsToCompactArray = GraphBuilder.CreateSRV(Context.CulledObjectsToCompactArray, PF_R32_UINT); PassParameters->View = View.ViewUniformBuffer; PassParameters->CompactCulledObjectsIndirectArguments = CompactCulledObjectsIndirectArguments; PassParameters->MaxNumberOfCulledObjects = Context.MaxNumberOfCulledObjects; } FCompactCulledObjectsCS::FPermutationDomain PermutationVector; PermutationVector.Set< FCompactCulledObjectsCS::FCullMeshTypeSDF >(Scene->DistanceFieldSceneData.NumObjectsInBuffer > 0); PermutationVector.Set< FCompactCulledObjectsCS::FCullMeshTypeHeightfield >(Lumen::UseHeightfieldTracing(*View.Family, *Scene->GetLumenSceneData(View))); auto ComputeShader = View.ShaderMap->GetShader< FCompactCulledObjectsCS >(PermutationVector); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("CompactCulledObjects"), ComputePassFlags, ComputeShader, PassParameters, CompactCulledObjectsIndirectArguments, 0); } } void CullObjectsToGrid( const FViewInfo& View, const FScene* Scene, const FLumenSceneFrameTemporaries& FrameTemporaries, float MaxMeshSDFInfluenceRadius, float CardTraceEndDistanceFromCamera, int32 GridPixelsPerCellXY, int32 GridSizeZ, FVector ZParams, FIntVector CullGridSize, FRDGBuilder& GraphBuilder, FRDGBufferRef ObjectIndexBuffer, FObjectCullingContext& Context) { const FDistanceFieldSceneData& DistanceFieldSceneData = Scene->DistanceFieldSceneData; // Scatter mesh SDF objects into a temporary array of {ObjectIndex, GridCellIndex} FMeshSDFObjectCull* PassParameters = GraphBuilder.AllocParameters(); { if (DistanceFieldSceneData.NumObjectsInBuffer > 0) { PassParameters->VS.DistanceFieldObjectBuffers = DistanceField::SetupObjectBufferParameters(GraphBuilder, DistanceFieldSceneData); } if (Lumen::UseHeightfieldTracing(*View.Family, *Scene->GetLumenSceneData(View))) { PassParameters->VS.LumenCardScene = FrameTemporaries.LumenCardSceneUniformBuffer; } PassParameters->VS.ObjectIndexBuffer = GraphBuilder.CreateSRV(ObjectIndexBuffer, PF_R32_UINT); PassParameters->VS.View = GetShaderBinding(View.ViewUniformBuffer); // Boost the effective radius so that the edges of the sphere approximation lie on the sphere, instead of the vertices const int32 NumRings = StencilingGeometry::GLowPolyStencilSphereVertexBuffer.GetNumRings(); const float RadiansPerRingSegment = PI / (float)NumRings; PassParameters->VS.ConservativeRadiusScale = 1.0f / FMath::Cos(RadiansPerRingSegment); PassParameters->VS.MaxMeshSDFInfluenceRadius = MaxMeshSDFInfluenceRadius; PassParameters->PS.RWNumGridCulledMeshSDFObjects = GraphBuilder.CreateUAV(Context.NumGridCulledMeshSDFObjects, PF_R32_UINT); PassParameters->PS.RWNumGridCulledHeightfieldObjects = GraphBuilder.CreateUAV(Context.NumGridCulledHeightfieldObjects, PF_R32_UINT); PassParameters->PS.RWNumCulledObjectsToCompact = GraphBuilder.CreateUAV(Context.NumCulledObjectsToCompact, PF_R32_UINT); PassParameters->PS.RWCulledObjectsToCompactArray = GraphBuilder.CreateUAV(Context.CulledObjectsToCompactArray, PF_R32_UINT); if (DistanceFieldSceneData.NumObjectsInBuffer > 0) { PassParameters->PS.DFObjectBufferParameters = DistanceField::SetupObjectBufferParameters(GraphBuilder, Scene->DistanceFieldSceneData); PassParameters->PS.DistanceFieldAtlas = DistanceField::SetupAtlasParameters(GraphBuilder, DistanceFieldSceneData); } if (Lumen::UseHeightfieldTracing(*View.Family, *Scene->GetLumenSceneData(View))) { PassParameters->PS.LumenCardScene = FrameTemporaries.LumenCardSceneUniformBuffer; } PassParameters->PS.View = GetShaderBinding(View.ViewUniformBuffer); PassParameters->PS.MaxMeshSDFInfluenceRadius = MaxMeshSDFInfluenceRadius; PassParameters->PS.CardGridZParams = (FVector3f)ZParams; // LWC_TODO: Precision Loss PassParameters->PS.CardGridPixelSizeShift = FMath::FloorLog2(GridPixelsPerCellXY); PassParameters->PS.CullGridSize = CullGridSize; PassParameters->PS.CardTraceEndDistanceFromCamera = CardTraceEndDistanceFromCamera; PassParameters->PS.MaxNumberOfCulledObjects = Context.MaxNumberOfCulledObjects; PassParameters->PS.HZBParameters = GetHZBParameters(GraphBuilder, View, EHZBType::All); PassParameters->PS.HZBMipLevel = FMath::Max((int32)FMath::FloorLog2(GridPixelsPerCellXY) - 1, 0.0f); PassParameters->MeshSDFIndirectArgs = Context.ObjectIndirectArguments; } FMeshSDFObjectCullVS::FPermutationDomain PermutationVectorVS; PermutationVectorVS.Set< FMeshSDFObjectCullVS::FCullMeshTypeSDF >(DistanceFieldSceneData.NumObjectsInBuffer > 0); PermutationVectorVS.Set< FMeshSDFObjectCullVS::FCullMeshTypeHeightfield >(Lumen::UseHeightfieldTracing(*View.Family, *Scene->GetLumenSceneData(View))); auto VertexShader = View.ShaderMap->GetShader< FMeshSDFObjectCullVS >(PermutationVectorVS); FMeshSDFObjectCullPS::FPermutationDomain PermutationVectorPS; PermutationVectorPS.Set< FMeshSDFObjectCullPS::FCullToFroxelGrid >(GridSizeZ > 1); PermutationVectorPS.Set< FMeshSDFObjectCullPS::FCullMeshTypeSDF >(DistanceFieldSceneData.NumObjectsInBuffer > 0); PermutationVectorPS.Set< FMeshSDFObjectCullPS::FCullMeshTypeHeightfield >(Lumen::UseHeightfieldTracing(*View.Family, *Scene->GetLumenSceneData(View))); extern int32 GDistanceFieldOffsetDataStructure; PermutationVectorPS.Set< FMeshSDFObjectCullPS::FOffsetDataStructure >(GDistanceFieldOffsetDataStructure); PermutationVectorPS = FMeshSDFObjectCullPS::RemapPermutation(PermutationVectorPS); auto PixelShader = View.ShaderMap->GetShader(PermutationVectorPS); ClearUnusedGraphResources(VertexShader, &PassParameters->VS); ClearUnusedGraphResources(PixelShader, &PassParameters->PS); const bool bReverseCulling = View.bReverseCulling; GraphBuilder.AddPass( RDG_EVENT_NAME("ScatterMeshSDFsToGrid"), PassParameters, ERDGPassFlags::Raster | ERDGPassFlags::SkipRenderPass, [CullGridSize, bReverseCulling, VertexShader, PixelShader, PassParameters](FRDGAsyncTask, FRHICommandList& RHICmdList) { FRHIRenderPassInfo RPInfo; RPInfo.ResolveRect.X1 = 0; RPInfo.ResolveRect.Y1 = 0; RPInfo.ResolveRect.X2 = CullGridSize.X; RPInfo.ResolveRect.Y2 = CullGridSize.Y; RHICmdList.BeginRenderPass(RPInfo, TEXT("ScatterMeshSDFsToGrid")); FGraphicsPipelineStateInitializer GraphicsPSOInit; RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit); RHICmdList.SetViewport(0, 0, 0.0f, CullGridSize.X, CullGridSize.Y, 1.0f); // Render backfaces since camera may intersect GraphicsPSOInit.RasterizerState = bReverseCulling ? TStaticRasterizerState::GetRHI() : TStaticRasterizerState::GetRHI(); GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState::GetRHI(); GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI(); GraphicsPSOInit.PrimitiveType = PT_TriangleList; GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GetVertexDeclarationFVector4(); GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader(); GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader(); SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0); SetShaderParameters(RHICmdList, VertexShader, VertexShader.GetVertexShader(), PassParameters->VS); SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), PassParameters->PS); RHICmdList.SetStreamSource(0, StencilingGeometry::GLowPolyStencilSphereVertexBuffer.VertexBufferRHI, 0); RHICmdList.DrawIndexedPrimitiveIndirect( StencilingGeometry::GLowPolyStencilSphereIndexBuffer.IndexBufferRHI, PassParameters->MeshSDFIndirectArgs->GetIndirectRHICallBuffer(), 0); RHICmdList.EndRenderPass(); }); } void CullMeshObjectsToViewGrid( const FViewInfo& View, const FScene* Scene, const FLumenSceneFrameTemporaries& FrameTemporaries, float MaxMeshSDFInfluenceRadius, float CardTraceEndDistanceFromCamera, int32 GridPixelsPerCellXY, int32 GridSizeZ, FVector ZParams, FRDGBuilder& GraphBuilder, FLumenMeshSDFGridParameters& OutGridParameters, ERDGPassFlags ComputePassFlags) { LLM_SCOPE_BYTAG(Lumen); const FDistanceFieldSceneData& DistanceFieldSceneData = Scene->DistanceFieldSceneData; const FIntPoint CardGridSizeXY = FIntPoint::DivideAndRoundUp(View.ViewRect.Size(), GridPixelsPerCellXY); const FIntVector CullGridSize(CardGridSizeXY.X, CardGridSizeXY.Y, GridSizeZ); const uint32 NumCullGridCells = CullGridSize.X * CullGridSize.Y * CullGridSize.Z; uint32 MaxCullGridCells; { // Allocate buffers using scene render targets size so we won't reallocate every frame with dynamic resolution const FIntPoint BufferSize = View.GetSceneTexturesConfig().Extent; const FIntPoint MaxCardGridSizeXY = FIntPoint::DivideAndRoundUp(BufferSize, GridPixelsPerCellXY); MaxCullGridCells = MaxCardGridSizeXY.X * MaxCardGridSizeXY.Y * GridSizeZ; ensure(MaxCullGridCells >= NumCullGridCells); } RDG_EVENT_SCOPE(GraphBuilder, "MeshSDFCulling %ux%ux%u cells", CullGridSize.X, CullGridSize.Y, CullGridSize.Z); FObjectCullingContext Context; InitObjectCullingContext( GraphBuilder, MaxCullGridCells, Context); Context.ObjectIndirectArguments = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateIndirectDesc(1), TEXT("Lumen.CulledObjectIndirectArguments")); AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(Context.ObjectIndirectArguments), 0); bool bCullMeshSDFObjects = DistanceFieldSceneData.NumObjectsInBuffer > 0; if (bCullMeshSDFObjects) { CullMeshSDFObjectsForView( GraphBuilder, Scene, View, MaxMeshSDFInfluenceRadius, CardTraceEndDistanceFromCamera, Context); } bool bCullHeightfieldObjects = Lumen::UseHeightfieldTracing(*View.Family, *Scene->GetLumenSceneData(View)); if (bCullHeightfieldObjects) { CullHeightfieldObjectsForView( GraphBuilder, Scene, View, FrameTemporaries, MaxMeshSDFInfluenceRadius, CardTraceEndDistanceFromCamera, Context.NumHeightfieldCulledObjects, Context.HeightfieldObjectIndexBuffer, Context.ObjectIndirectArguments); } FRDGBufferRef NumGridCulledHeightfieldObjects = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), Context.NumCullGridCells), TEXT("Lumen.NumGridCulledHeightfieldObjects")); AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(NumGridCulledHeightfieldObjects, PF_R32_UINT), 0); if (bCullMeshSDFObjects || bCullHeightfieldObjects) { FRDGBufferRef CombinedObjectIndexBuffer; CombineObjectIndexBuffers( GraphBuilder, Scene, View, bCullMeshSDFObjects, bCullHeightfieldObjects, Context, CombinedObjectIndexBuffer); CullObjectsToGrid( View, Scene, FrameTemporaries, MaxMeshSDFInfluenceRadius, CardTraceEndDistanceFromCamera, GridPixelsPerCellXY, GridSizeZ, ZParams, CullGridSize, GraphBuilder, CombinedObjectIndexBuffer, Context); } CompactCulledObjectArray( GraphBuilder, Scene, View, Context, ComputePassFlags); FillGridParameters( GraphBuilder, Scene, View, &Context, OutGridParameters); }