// Copyright Epic Games, Inc. All Rights Reserved. #include "NaniteComposition.h" #include "Rendering/NaniteResources.h" #include "Rendering/NaniteStreamingManager.h" #include "ScenePrivate.h" #include "SystemTextures.h" #include "PixelShaderUtils.h" #include "PostProcess/SceneRenderTargets.h" BEGIN_SHADER_PARAMETER_STRUCT(FDummyDepthDecompressParameters, ) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SceneDepth) END_SHADER_PARAMETER_STRUCT() int32 GNaniteResummarizeHTile = 1; static FAutoConsoleVariableRef CVarNaniteResummarizeHTile( TEXT("r.Nanite.ResummarizeHTile"), GNaniteResummarizeHTile, TEXT("") ); int32 GNaniteDecompressDepth = 0; static FAutoConsoleVariableRef CVarNaniteDecompressDepth( TEXT("r.Nanite.DecompressDepth"), GNaniteDecompressDepth, TEXT("") ); int32 GNaniteCustomDepthExportMethod = 1; static FAutoConsoleVariableRef CVarNaniteCustomDepthExportMethod( TEXT("r.Nanite.CustomDepth.ExportMethod"), GNaniteCustomDepthExportMethod, TEXT("0 - Export depth/stencil into separate targets via PS\n") TEXT("1 - Export depth/stencil direct to target via CS (requires HTILE support)\n") ); #if WITH_EDITORONLY_DATA extern int32 GNaniteIsolateInvalidCoarseMesh; #endif class FNaniteMarkStencilPS : public FNaniteGlobalShader { DECLARE_GLOBAL_SHADER(FNaniteMarkStencilPS); SHADER_USE_PARAMETER_STRUCT(FNaniteMarkStencilPS, FNaniteGlobalShader); static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return DoesPlatformSupportNanite(Parameters.Platform); } BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, VisBuffer64) RENDER_TARGET_BINDING_SLOTS() END_SHADER_PARAMETER_STRUCT() }; IMPLEMENT_GLOBAL_SHADER(FNaniteMarkStencilPS, "/Engine/Private/Nanite/NaniteExportGBuffer.usf", "MarkStencilPS", SF_Pixel); class FEmitSceneDepthPS : public FNaniteGlobalShader { DECLARE_GLOBAL_SHADER(FEmitSceneDepthPS); SHADER_USE_PARAMETER_STRUCT(FEmitSceneDepthPS, FNaniteGlobalShader); class FVelocityExportDim : SHADER_PERMUTATION_BOOL("VELOCITY_EXPORT"); class FShadingMaskExportDim : SHADER_PERMUTATION_BOOL("SHADING_MASK_EXPORT"); class FSkinningDim : SHADER_PERMUTATION_BOOL("USE_SKINNING"); using FPermutationDomain = TShaderPermutationDomain; static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { const FPermutationDomain PermutationVector(Parameters.PermutationId); if (PermutationVector.Get()) { if (!NaniteSkinnedMeshesSupported()) { return false; } if (!PermutationVector.Get()) { // Skinning is only needed when velocity will be exported return false; } } return DoesPlatformSupportNanite(Parameters.Platform); } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FNaniteGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetRenderTargetOutputFormat(0, PF_R32_UINT); } BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_INCLUDE(FViewShaderParameters, View) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneUniformParameters, Scene) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, InViews) SHADER_PARAMETER_RDG_BUFFER_SRV(ByteAddressBuffer, VisibleClustersSWHW) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, RasterBinMeta) SHADER_PARAMETER(FIntVector4, PageConstants) SHADER_PARAMETER_RDG_BUFFER_SRV(ByteAddressBuffer, ClusterPageData) SHADER_PARAMETER_RDG_BUFFER_SRV(ByteAddressBuffer, HierarchyBuffer) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, VisBuffer64) SHADER_PARAMETER(uint32, MeshPassIndex) SHADER_PARAMETER(uint32, RegularMaterialRasterBinCount) RENDER_TARGET_BINDING_SLOTS() END_SHADER_PARAMETER_STRUCT() }; IMPLEMENT_GLOBAL_SHADER(FEmitSceneDepthPS, "/Engine/Private/Nanite/NaniteExportGBuffer.usf", "EmitSceneDepthPS", SF_Pixel); class FEmitSceneStencilPS : public FNaniteGlobalShader { DECLARE_GLOBAL_SHADER(FEmitSceneStencilPS); SHADER_USE_PARAMETER_STRUCT(FEmitSceneStencilPS, FNaniteGlobalShader); static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return DoesPlatformSupportNanite(Parameters.Platform); } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FNaniteGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("SHADING_MASK_LOAD"), 1); } BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneUniformParameters, Scene) SHADER_PARAMETER_RDG_BUFFER_SRV(ByteAddressBuffer, VisibleClustersSWHW) SHADER_PARAMETER(FIntVector4, PageConstants) SHADER_PARAMETER_RDG_BUFFER_SRV(ByteAddressBuffer, ClusterPageData) SHADER_PARAMETER_RDG_BUFFER_SRV(ByteAddressBuffer, HierarchyBuffer) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, ShadingMask) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, VisBuffer64) RENDER_TARGET_BINDING_SLOTS() END_SHADER_PARAMETER_STRUCT() }; IMPLEMENT_GLOBAL_SHADER(FEmitSceneStencilPS, "/Engine/Private/Nanite/NaniteExportGBuffer.usf", "EmitSceneStencilPS", SF_Pixel); class FEmitCustomDepthStencilPS : public FNaniteGlobalShader { DECLARE_GLOBAL_SHADER(FEmitCustomDepthStencilPS); SHADER_USE_PARAMETER_STRUCT(FEmitCustomDepthStencilPS, FNaniteGlobalShader); class FWriteCustomStencilDim : SHADER_PERMUTATION_BOOL("WRITE_CUSTOM_STENCIL"); using FPermutationDomain = TShaderPermutationDomain; static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return DoesPlatformSupportNanite(Parameters.Platform); } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FNaniteGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); const FPermutationDomain PermutationVector(Parameters.PermutationId); if (PermutationVector.Get()) { OutEnvironment.SetRenderTargetOutputFormat(0, PF_R16G16_UINT); } } BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneUniformParameters, Scene) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, InViews) SHADER_PARAMETER_RDG_BUFFER_SRV(ByteAddressBuffer, VisibleClustersSWHW) SHADER_PARAMETER(FIntVector4, PageConstants) SHADER_PARAMETER_RDG_BUFFER_SRV(ByteAddressBuffer, ClusterPageData) SHADER_PARAMETER_RDG_BUFFER_SRV(ByteAddressBuffer, HierarchyBuffer) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, VisBuffer64) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, CustomDepth) SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, CustomStencil) RENDER_TARGET_BINDING_SLOTS() END_SHADER_PARAMETER_STRUCT() }; IMPLEMENT_GLOBAL_SHADER(FEmitCustomDepthStencilPS, "/Engine/Private/Nanite/NaniteExportGBuffer.usf", "EmitCustomDepthStencilPS", SF_Pixel); class FDepthExportCS : public FNaniteGlobalShader { DECLARE_GLOBAL_SHADER(FDepthExportCS); SHADER_USE_PARAMETER_STRUCT(FDepthExportCS, FNaniteGlobalShader); class FVelocityExportDim : SHADER_PERMUTATION_BOOL("VELOCITY_EXPORT"); class FShadingMaskExportDim : SHADER_PERMUTATION_BOOL("SHADING_MASK_EXPORT"); class FSkinningDim : SHADER_PERMUTATION_BOOL("USE_SKINNING"); using FPermutationDomain = TShaderPermutationDomain; static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { const FPermutationDomain PermutationVector(Parameters.PermutationId); if (PermutationVector.Get()) { if (!NaniteSkinnedMeshesSupported()) { return false; } if (!PermutationVector.Get()) { // Skinning is only needed when velocity will be exported return false; } } return DoesPlatformSupportNanite(Parameters.Platform); } BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_INCLUDE(FViewShaderParameters, View) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneUniformParameters, Scene) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, InViews) SHADER_PARAMETER_RDG_BUFFER_SRV(ByteAddressBuffer, VisibleClustersSWHW) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, RasterBinMeta) SHADER_PARAMETER(FIntVector4, PageConstants) SHADER_PARAMETER_RDG_BUFFER_SRV(ByteAddressBuffer, ClusterPageData) SHADER_PARAMETER_RDG_BUFFER_SRV(ByteAddressBuffer, HierarchyBuffer) SHADER_PARAMETER(FIntVector4, DepthExportConfig) SHADER_PARAMETER(FUint32Vector4, ViewRect) SHADER_PARAMETER(uint32, bWriteCustomStencil) SHADER_PARAMETER(uint32, MeshPassIndex) SHADER_PARAMETER(uint32, RegularMaterialRasterBinCount) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, VisBuffer64) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, Velocity) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, ShadingMask) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTextureMetadata, SceneHTile) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, SceneDepth) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, SceneStencil) END_SHADER_PARAMETER_STRUCT() }; IMPLEMENT_GLOBAL_SHADER(FDepthExportCS, "/Engine/Private/Nanite/NaniteDepthExport.usf", "DepthExport", SF_Compute); BEGIN_SHADER_PARAMETER_STRUCT(FNaniteMarkStencilRectsParameters, ) SHADER_PARAMETER_STRUCT_INCLUDE(FPixelShaderUtils::FRasterizeToRectsVS::FParameters, VS) SHADER_PARAMETER_STRUCT_INCLUDE(FNaniteMarkStencilPS::FParameters, PS) END_SHADER_PARAMETER_STRUCT() BEGIN_SHADER_PARAMETER_STRUCT(FNaniteEmitDepthRectsParameters, ) SHADER_PARAMETER_STRUCT_INCLUDE(FPixelShaderUtils::FRasterizeToRectsVS::FParameters, VS) SHADER_PARAMETER_STRUCT_INCLUDE(FEmitSceneDepthPS::FParameters, PS) END_SHADER_PARAMETER_STRUCT() namespace Nanite { void EmitDepthTargets( FRDGBuilder& GraphBuilder, const FScene& Scene, const FViewInfo& View, bool bDrawSceneViewsInOneNanitePass, FRasterResults& RasterResults, FRDGTextureRef SceneDepth, FRDGTextureRef VelocityBuffer, FRDGTextureRef FirstStageDepthBuffer ) { LLM_SCOPE_BYTAG(Nanite); RDG_EVENT_SCOPE(GraphBuilder, "Nanite::EmitDepthTargets"); FRDGTextureRef VisBuffer64 = RasterResults.VisBuffer64; #if WITH_EDITORONLY_DATA // Hide all Nanite meshes when the isolate invalid coarse mesh batch debug mode is active. if (GNaniteIsolateInvalidCoarseMesh != 0) { const FRDGSystemTextures& SystemTextures = FRDGSystemTextures::Get(GraphBuilder); VisBuffer64 = SystemTextures.Black; } #endif const FSceneTexturesConfig& Config = View.GetSceneTexturesConfig(); const EShaderPlatform ShaderPlatform = View.GetShaderPlatform(); const FIntPoint SceneTexturesExtent = Config.Extent; const FClearValueBinding DefaultDepthStencil = Config.DepthClearValue; float DefaultDepth = 0.0f; uint32 DefaultStencil = 0; DefaultDepthStencil.GetDepthStencil(DefaultDepth, DefaultStencil); const uint32 StencilDecalMask = GET_STENCIL_BIT_MASK(RECEIVE_DECAL, 1); const bool bEmitVelocity = VelocityBuffer != nullptr; const bool bClearVelocity = bEmitVelocity && !HasBeenProduced(VelocityBuffer); FRDGTextureDesc ShadingMaskDesc = FRDGTextureDesc::Create2D( SceneTexturesExtent, PF_R32_UINT, FClearValueBinding::Transparent, TexCreate_RenderTargetable | TexCreate_ShaderResource | TexCreate_UAV); RasterResults.ShadingMask = GraphBuilder.CreateTexture(ShadingMaskDesc, TEXT("Nanite.ShadingMask")); RasterResults.ClearTileArgs = nullptr; RasterResults.ClearTileBuffer = nullptr; if (UseComputeDepthExport()) { // NOTE: We intentionally skip calling AddClearRenderTargetPass on the ShadingMask here, since we will explicitly // write all pixels during the export depth pass below // Emit depth, stencil, mask and velocity if (GNaniteDecompressDepth != 0) { // Force depth decompression so the depth shader only processes decompressed surfaces FDummyDepthDecompressParameters* DecompressParams = GraphBuilder.AllocParameters(); DecompressParams->SceneDepth = SceneDepth; GraphBuilder.AddPass( RDG_EVENT_NAME("NaniteDepthDecompress"), DecompressParams, ERDGPassFlags::Copy | ERDGPassFlags::NeverCull, [](FRDGAsyncTask, FRHICommandList&) {} ); } const FIntRect ViewRect = bDrawSceneViewsInOneNanitePass ? View.GetFamilyViewRect() : View.ViewRect; const int32 kHTileSize = 8; checkf((ViewRect.Min.X % kHTileSize) == 0 && (ViewRect.Min.Y % kHTileSize) == 0, TEXT("Viewport rect must be %d-pixel aligned."), kHTileSize); const FIntVector DispatchDim = FComputeShaderUtils::GetGroupCount(ViewRect.Size(), kHTileSize); const uint32 PlatformConfig = RHIGetHTilePlatformConfig(SceneTexturesExtent.X, SceneTexturesExtent.Y); FRDGTextureUAVRef SceneDepthUAV = GraphBuilder.CreateUAV(FRDGTextureUAVDesc::CreateForMetaData(SceneDepth, ERDGTextureMetaDataAccess::CompressedSurface)); FRDGTextureUAVRef SceneStencilUAV = GraphBuilder.CreateUAV(FRDGTextureUAVDesc::CreateForMetaData(SceneDepth, ERDGTextureMetaDataAccess::Stencil)); FRDGTextureUAVRef SceneHTileUAV = GraphBuilder.CreateUAV(FRDGTextureUAVDesc::CreateForMetaData(SceneDepth, ERDGTextureMetaDataAccess::HTile)); FRDGTextureUAVRef VelocityUAV = bEmitVelocity ? GraphBuilder.CreateUAV(VelocityBuffer) : nullptr; FRDGTextureUAVRef ShadingMaskUAV = GraphBuilder.CreateUAV(RasterResults.ShadingMask); FDepthExportCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->View = View.GetShaderParameters(); PassParameters->Scene = View.GetSceneUniforms().GetBuffer(GraphBuilder); PassParameters->InViews = GraphBuilder.CreateSRV(RasterResults.ViewsBuffer); PassParameters->VisibleClustersSWHW = GraphBuilder.CreateSRV(RasterResults.VisibleClustersSWHW); PassParameters->RasterBinMeta = GraphBuilder.CreateSRV(RasterResults.RasterBinMeta); PassParameters->PageConstants = RasterResults.PageConstants; PassParameters->ClusterPageData = Nanite::GStreamingManager.GetClusterPageDataSRV(GraphBuilder); PassParameters->HierarchyBuffer = Nanite::GStreamingManager.GetHierarchySRV(GraphBuilder); PassParameters->DepthExportConfig = FIntVector4(PlatformConfig, SceneTexturesExtent.X, StencilDecalMask, Nanite::FGlobalResources::GetMaxVisibleClusters()); PassParameters->ViewRect = FUint32Vector4((uint32)ViewRect.Min.X, (uint32)ViewRect.Min.Y, (uint32)ViewRect.Max.X, (uint32)ViewRect.Max.Y); PassParameters->bWriteCustomStencil = false; PassParameters->MeshPassIndex = ENaniteMeshPass::BasePass; PassParameters->RegularMaterialRasterBinCount = Scene.NaniteRasterPipelines[ENaniteMeshPass::BasePass].GetRegularBinCount(); PassParameters->VisBuffer64 = VisBuffer64; PassParameters->Velocity = VelocityUAV; PassParameters->ShadingMask = ShadingMaskUAV; PassParameters->SceneHTile = SceneHTileUAV; PassParameters->SceneDepth = SceneDepthUAV; PassParameters->SceneStencil = SceneStencilUAV; FDepthExportCS::FPermutationDomain PermutationVectorCS; PermutationVectorCS.Set(bEmitVelocity); PermutationVectorCS.Set(true); PermutationVectorCS.Set(bEmitVelocity && NaniteSkinnedMeshesSupported()); auto ComputeShader = View.ShaderMap->GetShader(PermutationVectorCS); FComputeShaderUtils::AddPass(GraphBuilder, RDG_EVENT_NAME("DepthExport"), ComputeShader, PassParameters, DispatchDim); } else { // Can't use ERenderTargetLoadAction::EClear to clear here because it needs to be the same for all render targets. AddClearRenderTargetPass(GraphBuilder, RasterResults.ShadingMask); if (bClearVelocity) { AddClearRenderTargetPass(GraphBuilder, VelocityBuffer); } // Emit scene depth buffer, mask and velocity { FEmitSceneDepthPS::FPermutationDomain PermutationVectorPS; PermutationVectorPS.Set(bEmitVelocity); PermutationVectorPS.Set(true); PermutationVectorPS.Set(bEmitVelocity && NaniteSkinnedMeshesSupported()); auto PixelShader = View.ShaderMap->GetShader(PermutationVectorPS); auto* PassParameters = GraphBuilder.AllocParameters(); PassParameters->View = View.GetShaderParameters(); PassParameters->Scene = View.GetSceneUniforms().GetBuffer(GraphBuilder); PassParameters->InViews = GraphBuilder.CreateSRV(RasterResults.ViewsBuffer); PassParameters->VisibleClustersSWHW = GraphBuilder.CreateSRV(RasterResults.VisibleClustersSWHW); PassParameters->RasterBinMeta = GraphBuilder.CreateSRV(RasterResults.RasterBinMeta); PassParameters->PageConstants = RasterResults.PageConstants; PassParameters->VisBuffer64 = VisBuffer64; PassParameters->ClusterPageData = Nanite::GStreamingManager.GetClusterPageDataSRV(GraphBuilder); PassParameters->HierarchyBuffer = Nanite::GStreamingManager.GetHierarchySRV(GraphBuilder); PassParameters->MeshPassIndex = ENaniteMeshPass::BasePass; PassParameters->RegularMaterialRasterBinCount = Scene.NaniteRasterPipelines[ENaniteMeshPass::BasePass].GetRegularBinCount(); PassParameters->RenderTargets[0] = FRenderTargetBinding(RasterResults.ShadingMask, ERenderTargetLoadAction::ELoad); PassParameters->RenderTargets[1] = bEmitVelocity ? FRenderTargetBinding(VelocityBuffer, ERenderTargetLoadAction::ELoad) : FRenderTargetBinding(); PassParameters->RenderTargets.DepthStencil = FDepthStencilBinding(SceneDepth, ERenderTargetLoadAction::ELoad, FExclusiveDepthStencil::DepthWrite_StencilWrite); FPixelShaderUtils::AddFullscreenPass( GraphBuilder, View.ShaderMap, RDG_EVENT_NAME("Emit Scene Depth/Resolve/Velocity"), PixelShader, PassParameters, bDrawSceneViewsInOneNanitePass ? View.GetFamilyViewRect() : View.ViewRect, TStaticBlendState<>::GetRHI(), TStaticRasterizerState<>::GetRHI(), TStaticDepthStencilState::GetRHI() ); } // Emit scene stencil { auto PixelShader = View.ShaderMap->GetShader(); auto* PassParameters = GraphBuilder.AllocParameters(); PassParameters->View = View.ViewUniformBuffer; PassParameters->Scene = View.GetSceneUniforms().GetBuffer(GraphBuilder); PassParameters->VisibleClustersSWHW = GraphBuilder.CreateSRV(RasterResults.VisibleClustersSWHW); PassParameters->PageConstants = RasterResults.PageConstants; PassParameters->ClusterPageData = Nanite::GStreamingManager.GetClusterPageDataSRV(GraphBuilder); PassParameters->HierarchyBuffer = Nanite::GStreamingManager.GetHierarchySRV(GraphBuilder); PassParameters->ShadingMask = RasterResults.ShadingMask; PassParameters->VisBuffer64 = VisBuffer64; PassParameters->RenderTargets.DepthStencil = FDepthStencilBinding ( SceneDepth, ERenderTargetLoadAction::ELoad, FExclusiveDepthStencil::DepthWrite_StencilWrite ); FPixelShaderUtils::AddFullscreenPass( GraphBuilder, View.ShaderMap, RDG_EVENT_NAME("Emit Scene Stencil"), PixelShader, PassParameters, View.ViewRect, TStaticBlendState<>::GetRHI(), TStaticRasterizerState<>::GetRHI(), TStaticDepthStencilState::GetRHI(), StencilDecalMask | GET_STENCIL_BIT_MASK(DISTANCE_FIELD_REPRESENTATION, 1) ); } if (GRHISupportsResummarizeHTile && GNaniteResummarizeHTile != 0) { // Resummarize HTile meta data if the RHI supports it AddResummarizeHTilePass(GraphBuilder, SceneDepth); } } // Also emit Nanite to first stage depth buffer, only if necessary for this frame. // It is not perfect, nanite cluster will have been HZB culled against Niagara opaque meshes that are not in the FirstStage depth buffer, so there will be holes. // Still an improvement for Niagara opaque particles colliding with depth buffer, and validated by artists. // Only using the PS path right now until we can optimise for platforms supporting write through UAV. if (FirstStageDepthBuffer) { FEmitSceneDepthPS::FPermutationDomain PermutationVectorPS; PermutationVectorPS.Set(false); PermutationVectorPS.Set(false); PermutationVectorPS.Set(false); auto PixelShader = View.ShaderMap->GetShader(PermutationVectorPS); auto* PassParameters = GraphBuilder.AllocParameters(); PassParameters->View = View.GetShaderParameters(); PassParameters->Scene = View.GetSceneUniforms().GetBuffer(GraphBuilder); PassParameters->InViews = GraphBuilder.CreateSRV(RasterResults.ViewsBuffer); PassParameters->VisibleClustersSWHW = GraphBuilder.CreateSRV(RasterResults.VisibleClustersSWHW); PassParameters->RasterBinMeta = GraphBuilder.CreateSRV(RasterResults.RasterBinMeta); PassParameters->PageConstants = RasterResults.PageConstants; PassParameters->VisBuffer64 = VisBuffer64; PassParameters->ClusterPageData = Nanite::GStreamingManager.GetClusterPageDataSRV(GraphBuilder); PassParameters->HierarchyBuffer = Nanite::GStreamingManager.GetHierarchySRV(GraphBuilder); PassParameters->MeshPassIndex = ENaniteMeshPass::BasePass; PassParameters->RegularMaterialRasterBinCount = Scene.NaniteRasterPipelines[ENaniteMeshPass::BasePass].GetRegularBinCount(); PassParameters->RenderTargets.DepthStencil = FDepthStencilBinding(FirstStageDepthBuffer, ERenderTargetLoadAction::ELoad, FExclusiveDepthStencil::DepthWrite_StencilNop); FPixelShaderUtils::AddFullscreenPass( GraphBuilder, View.ShaderMap, RDG_EVENT_NAME("Emit Scene First Stage Depth"), PixelShader, PassParameters, bDrawSceneViewsInOneNanitePass ? View.GetFamilyViewRect() : View.ViewRect, TStaticBlendState<>::GetRHI(), TStaticRasterizerState<>::GetRHI(), TStaticDepthStencilState::GetRHI() ); if (GRHISupportsResummarizeHTile && GNaniteResummarizeHTile != 0) { // Resummarize HTile meta data if the RHI supports it AddResummarizeHTilePass(GraphBuilder, FirstStageDepthBuffer); } } } FCustomDepthContext InitCustomDepthStencilContext( FRDGBuilder& GraphBuilder, const FCustomDepthTextures& CustomDepthTextures, bool bWriteCustomStencil ) { enum ECustomDepthExportMethod { DepthExportSeparatePS, // Emit depth & stencil from PS (Stencil separated and written to RT0) DepthExportCS // Emit depth & stencil from CS with HTILE (requires RHI support) }; check(CustomDepthTextures.IsValid()); FCustomDepthContext Output; Output.bComputeExport = UseComputeDepthExport() && GNaniteCustomDepthExportMethod == DepthExportCS; if (Output.bComputeExport) { if (!CustomDepthTextures.Depth->HasBeenProduced()) { AddClearDepthStencilPass(GraphBuilder, CustomDepthTextures.Depth, CustomDepthTextures.DepthAction, CustomDepthTextures.StencilAction); } Output.InputDepth = CustomDepthTextures.Depth; Output.InputStencilSRV = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::CreateWithPixelFormat(CustomDepthTextures.Depth, PF_X24_G8)); // We can output directly to the depth target using compute Output.DepthTarget = CustomDepthTextures.Depth; Output.StencilTarget = bWriteCustomStencil ? CustomDepthTextures.Depth : nullptr; } else { if (CustomDepthTextures.Depth->HasBeenProduced()) { Output.InputDepth = CustomDepthTextures.Depth; Output.InputStencilSRV = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::CreateWithPixelFormat(CustomDepthTextures.Depth, PF_X24_G8)); } else { // Custom depth buffer hasn't been produced (because no non-Nanite has been rendered to it), so just composite Nanite // custom depth with blank 1x1 textures const FRDGSystemTextures& SystemTextures = FRDGSystemTextures::Get(GraphBuilder); Output.InputDepth = SystemTextures.DepthDummy; Output.InputStencilSRV = SystemTextures.StencilDummySRV; } // Since we cannot output the stencil ref from the pixel shader, we'll combine Nanite and non-Nanite custom depth/stencil // into new, separate targets. Note that stencil test using custom stencil from this point will require tests to be // performed manually in the pixel shader (see PostProcess materials, for example). const FIntPoint CustomDepthExtent = CustomDepthTextures.Depth->Desc.Extent; FRDGTextureDesc OutCustomDepthDesc = FRDGTextureDesc::Create2D( CustomDepthExtent, PF_DepthStencil, FClearValueBinding::DepthFar, TexCreate_DepthStencilTargetable | TexCreate_ShaderResource); Output.DepthTarget = GraphBuilder.CreateTexture(OutCustomDepthDesc, TEXT("CombinedCustomDepth")); if (bWriteCustomStencil) { FRDGTextureDesc OutCustomStencilDesc = FRDGTextureDesc::Create2D( CustomDepthExtent, PF_R16G16_UINT, //PF_R8G8_UINT, FClearValueBinding::Transparent, TexCreate_RenderTargetable | TexCreate_ShaderResource); Output.StencilTarget = GraphBuilder.CreateTexture(OutCustomStencilDesc, TEXT("CombinedCustomStencil")); } } return Output; } void EmitCustomDepthStencilTargets( FRDGBuilder& GraphBuilder, const FScene& Scene, const FViewInfo& View, bool bDrawSceneViewsInOneNanitePass, const FIntVector4& PageConstants, FRDGBufferRef VisibleClustersSWHW, FRDGBufferRef ViewsBuffer, FRDGTextureRef VisBuffer64, const FCustomDepthContext& CustomDepthContext ) { LLM_SCOPE_BYTAG(Nanite); RDG_EVENT_SCOPE(GraphBuilder, "Nanite::EmitCustomDepthStencilTargets"); FRDGTextureRef CustomDepth = CustomDepthContext.InputDepth; FRDGTextureSRVRef CustomStencilSRV = CustomDepthContext.InputStencilSRV; const bool bWriteCustomStencil = CustomDepthContext.StencilTarget != nullptr; if (CustomDepthContext.bComputeExport) { // Emit custom depth and stencil from a CS that can handle HTILE if (GNaniteDecompressDepth != 0) { // Force depth decompression so the depth shader only processes decompressed surfaces FDummyDepthDecompressParameters* DecompressParams = GraphBuilder.AllocParameters(); DecompressParams->SceneDepth = CustomDepth; GraphBuilder.AddPass( RDG_EVENT_NAME("NaniteCustomDepthDecompress"), DecompressParams, ERDGPassFlags::Copy | ERDGPassFlags::NeverCull, [](FRDGAsyncTask, FRHICommandList&) {} ); } const FIntRect ViewRect = bDrawSceneViewsInOneNanitePass ? View.GetFamilyViewRect() : View.ViewRect; const int32 kHTileSize = 8; checkf((ViewRect.Min.X % kHTileSize) == 0 && (ViewRect.Min.Y % kHTileSize) == 0, TEXT("Viewport rect must be %d-pixel aligned."), kHTileSize); // Export depth { const FIntPoint CustomDepthExtent = CustomDepth->Desc.Extent; const FIntVector DispatchDim = FComputeShaderUtils::GetGroupCount(ViewRect.Size(), kHTileSize); const uint32 PlatformConfig = RHIGetHTilePlatformConfig(CustomDepthExtent.X, CustomDepthExtent.Y); FRDGTextureUAVRef CustomDepthUAV = GraphBuilder.CreateUAV(FRDGTextureUAVDesc::CreateForMetaData(CustomDepth, ERDGTextureMetaDataAccess::CompressedSurface)); FRDGTextureUAVRef CustomStencilUAV = GraphBuilder.CreateUAV(FRDGTextureUAVDesc::CreateForMetaData(CustomDepth, ERDGTextureMetaDataAccess::Stencil)); FRDGTextureUAVRef CustomHTileUAV = GraphBuilder.CreateUAV(FRDGTextureUAVDesc::CreateForMetaData(CustomDepth, ERDGTextureMetaDataAccess::HTile)); FDepthExportCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->View = View.GetShaderParameters(); PassParameters->Scene = View.GetSceneUniforms().GetBuffer(GraphBuilder); PassParameters->InViews = GraphBuilder.CreateSRV(ViewsBuffer); PassParameters->VisibleClustersSWHW = GraphBuilder.CreateSRV(VisibleClustersSWHW); PassParameters->PageConstants = PageConstants; PassParameters->ClusterPageData = Nanite::GStreamingManager.GetClusterPageDataSRV(GraphBuilder); PassParameters->HierarchyBuffer = Nanite::GStreamingManager.GetHierarchySRV(GraphBuilder); PassParameters->DepthExportConfig = FIntVector4(PlatformConfig, CustomDepthExtent.X, 0, Nanite::FGlobalResources::GetMaxVisibleClusters()); PassParameters->ViewRect = FUint32Vector4((uint32)ViewRect.Min.X, (uint32)ViewRect.Min.Y, (uint32)ViewRect.Max.X, (uint32)ViewRect.Max.Y); PassParameters->bWriteCustomStencil = bWriteCustomStencil; PassParameters->MeshPassIndex = ENaniteMeshPass::BasePass; PassParameters->VisBuffer64 = VisBuffer64; PassParameters->Velocity = nullptr; PassParameters->ShadingMask = nullptr; PassParameters->SceneHTile = CustomHTileUAV; PassParameters->SceneDepth = CustomDepthUAV; PassParameters->SceneStencil = CustomStencilUAV; FDepthExportCS::FPermutationDomain PermutationVectorCS; PermutationVectorCS.Set(false); PermutationVectorCS.Set(false); PermutationVectorCS.Set(false); auto ComputeShader = View.ShaderMap->GetShader(PermutationVectorCS); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("DepthExport"), ComputeShader, PassParameters, DispatchDim ); } } else // DepthExportSeparatePS { FRDGTextureRef OutCustomDepth = CustomDepthContext.DepthTarget; FRDGTextureRef OutCustomStencil = CustomDepthContext.StencilTarget; FEmitCustomDepthStencilPS::FPermutationDomain PermutationVectorPS; PermutationVectorPS.Set(bWriteCustomStencil); auto PixelShader = View.ShaderMap->GetShader(PermutationVectorPS); auto* PassParameters = GraphBuilder.AllocParameters(); // If we aren't emitting stencil, clear it so it's not garbage ERenderTargetLoadAction StencilLoadAction = OutCustomStencil ? ERenderTargetLoadAction::ENoAction : ERenderTargetLoadAction::EClear; PassParameters->View = View.ViewUniformBuffer; PassParameters->Scene = View.GetSceneUniforms().GetBuffer(GraphBuilder); PassParameters->InViews = GraphBuilder.CreateSRV(ViewsBuffer); PassParameters->VisibleClustersSWHW = GraphBuilder.CreateSRV(VisibleClustersSWHW); PassParameters->PageConstants = PageConstants; PassParameters->VisBuffer64 = VisBuffer64; PassParameters->ClusterPageData = Nanite::GStreamingManager.GetClusterPageDataSRV(GraphBuilder); PassParameters->HierarchyBuffer = Nanite::GStreamingManager.GetHierarchySRV(GraphBuilder); PassParameters->CustomDepth = CustomDepth; PassParameters->CustomStencil = CustomStencilSRV; PassParameters->RenderTargets[0] = OutCustomStencil ? FRenderTargetBinding(OutCustomStencil, ERenderTargetLoadAction::ENoAction) : FRenderTargetBinding(); PassParameters->RenderTargets.DepthStencil = FDepthStencilBinding(OutCustomDepth, ERenderTargetLoadAction::ENoAction, StencilLoadAction, FExclusiveDepthStencil::DepthWrite_StencilNop); FPixelShaderUtils::AddFullscreenPass( GraphBuilder, View.ShaderMap, OutCustomStencil ? RDG_EVENT_NAME("Emit Custom Depth/Stencil") : RDG_EVENT_NAME("Emit Custom Depth"), PixelShader, PassParameters, bDrawSceneViewsInOneNanitePass ? View.GetFamilyViewRect() : View.ViewRect, TStaticBlendState<>::GetRHI(), TStaticRasterizerState<>::GetRHI(), TStaticDepthStencilState::GetRHI() ); } } void FinalizeCustomDepthStencil( FRDGBuilder& GraphBuilder, const FCustomDepthContext& CustomDepthContext, FCustomDepthTextures& OutTextures ) { OutTextures.Depth = CustomDepthContext.DepthTarget; if (CustomDepthContext.StencilTarget) { if (CustomDepthContext.bComputeExport) { // we wrote straight to the depth/stencil buffer OutTextures.Stencil = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::CreateWithPixelFormat(CustomDepthContext.StencilTarget, PF_X24_G8)); } else { // separate stencil texture OutTextures.Stencil = GraphBuilder.CreateSRV(CustomDepthContext.StencilTarget); } } else { OutTextures.Stencil = CustomDepthContext.InputStencilSRV; } OutTextures.bSeparateStencilBuffer = !CustomDepthContext.bComputeExport; } void MarkSceneStencilRects( FRDGBuilder& GraphBuilder, const FRasterContext& RasterContext, FScene& Scene, FViewInfo* SharedView, FIntPoint ViewportSize, uint32 NumRects, FRDGBufferSRVRef RectMinMaxBufferSRV, FRDGTextureRef DepthAtlasTexture ) { FNaniteMarkStencilRectsParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->PS.View = SharedView->ViewUniformBuffer; PassParameters->PS.VisBuffer64 = RasterContext.VisBuffer64; PassParameters->PS.RenderTargets.DepthStencil = FDepthStencilBinding( DepthAtlasTexture, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ELoad, FExclusiveDepthStencil::DepthRead_StencilWrite ); auto PixelShader = SharedView->ShaderMap->GetShader(); FPixelShaderUtils::AddRasterizeToRectsPass(GraphBuilder, SharedView->ShaderMap, RDG_EVENT_NAME("Mark Stencil"), PixelShader, PassParameters, ViewportSize, RectMinMaxBufferSRV, NumRects, TStaticBlendState<>::GetRHI(), TStaticRasterizerState<>::GetRHI(), TStaticDepthStencilState::GetRHI(), STENCIL_SANDBOX_MASK ); } void EmitSceneDepthRects( FRDGBuilder& GraphBuilder, const FRasterContext& RasterContext, FScene& Scene, FViewInfo* SharedView, FIntPoint ViewportSize, uint32 NumRects, FRDGBufferSRVRef RectMinMaxBufferSRV, FRDGTextureRef DepthAtlasTexture ) { FNaniteEmitDepthRectsParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->PS.VisBuffer64 = RasterContext.VisBuffer64; PassParameters->PS.RenderTargets.DepthStencil = FDepthStencilBinding( DepthAtlasTexture, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ELoad, FExclusiveDepthStencil::DepthWrite_StencilRead ); FEmitSceneDepthPS::FPermutationDomain PermutationVectorPS; PermutationVectorPS.Set(false); PermutationVectorPS.Set(false); PermutationVectorPS.Set(false); auto PixelShader = SharedView->ShaderMap->GetShader(PermutationVectorPS); FPixelShaderUtils::AddRasterizeToRectsPass(GraphBuilder, SharedView->ShaderMap, RDG_EVENT_NAME("Emit Depth"), PixelShader, PassParameters, ViewportSize, RectMinMaxBufferSRV, NumRects, TStaticBlendState<>::GetRHI(), TStaticRasterizerState<>::GetRHI(), TStaticDepthStencilState::GetRHI(), STENCIL_SANDBOX_MASK ); } } // Nanite