Files
UnrealEngineUWP/Engine/Source/Runtime/Renderer/Private/DeferredShadingRenderer.cpp
jeremy moore 925a8fe75c Fix some items rendering to a separate velocity pass when base velocity pass is used with r.SelectiveBasePassOutputs.
This logic took primitives with a material using static lighting and moved velocity writing to a second pass.
Seems like that is some old behavior and after asking around it isn't well known why we had it.
Anyway it's not good to pay the cost of that second pass, so removing that.
#preflight 628fd8f574630984fd4d4464

#ROBOMERGE-AUTHOR: jeremy.moore
#ROBOMERGE-SOURCE: CL 20392160 via CL 20392172 via CL 20392182
#ROBOMERGE-BOT: UE5 (Release-Engine-Staging -> Main) (v949-20362246)

[CL 20398501 by jeremy moore in ue5-main branch]
2022-05-27 16:44:41 -04:00

3508 lines
140 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
DeferredShadingRenderer.cpp: Top level rendering loop for deferred shading
=============================================================================*/
#include "DeferredShadingRenderer.h"
#include "VelocityRendering.h"
#include "SingleLayerWaterRendering.h"
#include "SkyAtmosphereRendering.h"
#include "VolumetricCloudRendering.h"
#include "VolumetricRenderTarget.h"
#include "ScenePrivate.h"
#include "SceneOcclusion.h"
#include "ScreenRendering.h"
#include "PostProcess/SceneFilterRendering.h"
#include "PostProcess/PostProcessSubsurface.h"
#include "PostProcess/PostProcessVisualizeCalibrationMaterial.h"
#include "PostProcess/TemporalAA.h"
#include "CompositionLighting/CompositionLighting.h"
#include "FXSystem.h"
#include "OneColorShader.h"
#include "CompositionLighting/PostProcessDeferredDecals.h"
#include "CompositionLighting/PostProcessAmbientOcclusion.h"
#include "DistanceFieldAmbientOcclusion.h"
#include "GlobalDistanceField.h"
#include "PostProcess/PostProcessing.h"
#include "DistanceFieldAtlas.h"
#include "EngineModule.h"
#include "SceneViewExtension.h"
#include "GPUSkinCache.h"
#include "PipelineStateCache.h"
#include "ClearQuad.h"
#include "RendererModule.h"
#include "VT/VirtualTextureFeedback.h"
#include "VT/VirtualTextureSystem.h"
#include "GPUScene.h"
#include "RayTracing/RayTracingMaterialHitShaders.h"
#include "RayTracing/RayTracingLighting.h"
#include "RayTracing/RayTracingDecals.h"
#include "RayTracing/RayTracingScene.h"
#include "RayTracingDynamicGeometryCollection.h"
#include "RayTracingSkinnedGeometry.h"
#include "SceneTextureParameters.h"
#include "ScreenSpaceDenoise.h"
#include "ScreenSpaceRayTracing.h"
#include "RayTracing/RaytracingOptions.h"
#include "RayTracingDefinitions.h"
#include "RayTracingInstance.h"
#include "ShaderPrint.h"
#include "GPUSortManager.h"
#include "HairStrands/HairStrandsRendering.h"
#include "HairStrands/HairStrandsData.h"
#include "PhysicsField/PhysicsFieldComponent.h"
#include "PhysicsFieldRendering.h"
#include "NaniteVisualizationData.h"
#include "Rendering/NaniteResources.h"
#include "Rendering/NaniteStreamingManager.h"
#include "Rendering/NaniteCoarseMeshStreamingManager.h"
#include "SceneTextureReductions.h"
#include "VirtualShadowMaps/VirtualShadowMapCacheManager.h"
#include "Strata/Strata.h"
#include "Lumen/Lumen.h"
#include "Experimental/Containers/SherwoodHashTable.h"
#include "RayTracingGeometryManager.h"
#include "InstanceCulling/InstanceCullingManager.h"
#include "ProfilingDebugging/CpuProfilerTrace.h"
#include "Engine/SubsurfaceProfile.h"
#include "SceneCaptureRendering.h"
#include "NaniteSceneProxy.h"
#include "RayTracing/RayTracingInstanceCulling.h"
#include "GPUMessaging.h"
#include "RectLightTextureManager.h"
#include "Lumen/LumenFrontLayerTranslucency.h"
extern int32 GNaniteShowStats;
static TAutoConsoleVariable<int32> CVarClearCoatNormal(
TEXT("r.ClearCoatNormal"),
0,
TEXT("0 to disable clear coat normal.\n")
TEXT(" 0: off\n")
TEXT(" 1: on"),
ECVF_ReadOnly);
static TAutoConsoleVariable<int32> CVarIrisNormal(
TEXT("r.IrisNormal"),
0,
TEXT("0 to disable iris normal.\n")
TEXT(" 0: off\n")
TEXT(" 1: on"),
ECVF_ReadOnly);
int32 GbEnableAsyncComputeTranslucencyLightingVolumeClear = 0; // @todo: disabled due to GPU crashes
static FAutoConsoleVariableRef CVarEnableAsyncComputeTranslucencyLightingVolumeClear(
TEXT("r.EnableAsyncComputeTranslucencyLightingVolumeClear"),
GbEnableAsyncComputeTranslucencyLightingVolumeClear,
TEXT("Whether to clear the translucency lighting volume using async compute.\n"),
ECVF_RenderThreadSafe | ECVF_Scalability
);
static int32 GRayTracing = 0;
static TAutoConsoleVariable<int32> CVarRayTracing(
TEXT("r.RayTracing"),
GRayTracing,
TEXT("0 to disable ray tracing.\n")
TEXT(" 0: off\n")
TEXT(" 1: on"),
ECVF_RenderThreadSafe | ECVF_ReadOnly);
int32 GRayTracingUseTextureLod = 0;
static TAutoConsoleVariable<int32> CVarRayTracingTextureLod(
TEXT("r.RayTracing.UseTextureLod"),
GRayTracingUseTextureLod,
TEXT("Enable automatic texture mip level selection in ray tracing material shaders.\n")
TEXT(" 0: highest resolution mip level is used for all texture (default).\n")
TEXT(" 1: texture LOD is approximated based on total ray length, output resolution and texel density at hit point (ray cone method)."),
ECVF_RenderThreadSafe | ECVF_ReadOnly);
static int32 GForceAllRayTracingEffects = -1;
static TAutoConsoleVariable<int32> CVarForceAllRayTracingEffects(
TEXT("r.RayTracing.ForceAllRayTracingEffects"),
GForceAllRayTracingEffects,
TEXT("Force all ray tracing effects ON/OFF.\n")
TEXT(" -1: Do not force (default) \n")
TEXT(" 0: All ray tracing effects disabled\n")
TEXT(" 1: All ray tracing effects enabled"),
ECVF_RenderThreadSafe);
static int32 GRayTracingAllowInline = 1;
static TAutoConsoleVariable<int32> CVarRayTracingAllowInline(
TEXT("r.RayTracing.AllowInline"),
GRayTracingAllowInline,
TEXT("Allow use of Inline Ray Tracing if supported (default=1)."),
ECVF_RenderThreadSafe);
static int32 GRayTracingAllowPipeline = 1;
static TAutoConsoleVariable<int32> CVarRayTracingAllowPipeline(
TEXT("r.RayTracing.AllowPipeline"),
GRayTracingAllowPipeline,
TEXT("Allow use of Ray Tracing pipelines if supported (default=1)."),
ECVF_RenderThreadSafe);
static int32 GRayTracingSceneCaptures = -1;
static FAutoConsoleVariableRef CVarRayTracingSceneCaptures(
TEXT("r.RayTracing.SceneCaptures"),
GRayTracingSceneCaptures,
TEXT("Enable ray tracing in scene captures.\n")
TEXT(" -1: Use scene capture settings (default) \n")
TEXT(" 0: off \n")
TEXT(" 1: on"),
ECVF_RenderThreadSafe);
static int32 GRayTracingExcludeDecals = 0;
static FAutoConsoleVariableRef CRayTracingExcludeDecals(
TEXT("r.RayTracing.ExcludeDecals"),
GRayTracingExcludeDecals,
TEXT("A toggle that modifies the inclusion of decals in the ray tracing BVH.\n")
TEXT(" 0: Decals included in the ray tracing BVH (default)\n")
TEXT(" 1: Decals excluded from the ray tracing BVH"),
ECVF_RenderThreadSafe);
static int32 GRayTracingExcludeTranslucent = 0;
static FAutoConsoleVariableRef CRayTracingExcludeTranslucent(
TEXT("r.RayTracing.ExcludeTranslucent"),
GRayTracingExcludeTranslucent,
TEXT("A toggle that modifies the inclusion of translucent objects in the ray tracing scene.\n")
TEXT(" 0: Translucent objects included in the ray tracing scene (default)\n")
TEXT(" 1: Translucent objects excluded from the ray tracing scene"),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarRayTracingAsyncBuild(
TEXT("r.RayTracing.AsyncBuild"),
0,
TEXT("Whether to build ray tracing acceleration structures on async compute queue.\n"),
ECVF_RenderThreadSafe
);
static int32 GRayTracingParallelMeshBatchSetup = 1;
static FAutoConsoleVariableRef CRayTracingParallelMeshBatchSetup(
TEXT("r.RayTracing.ParallelMeshBatchSetup"),
GRayTracingParallelMeshBatchSetup,
TEXT("Whether to setup ray tracing materials via parallel jobs."),
ECVF_RenderThreadSafe);
static int32 GRayTracingParallelMeshBatchSize = 1024;
static FAutoConsoleVariableRef CRayTracingParallelMeshBatchSize(
TEXT("r.RayTracing.ParallelMeshBatchSize"),
GRayTracingParallelMeshBatchSize,
TEXT("Batch size for ray tracing materials parallel jobs."),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<float> CVarRayTracingDynamicGeometryLastRenderTimeUpdateDistance(
TEXT("r.RayTracing.DynamicGeometryLastRenderTimeUpdateDistance"),
5000.0f,
TEXT("Dynamic geometries within this distance will have their LastRenderTime updated, so that visibility based ticking (like skeletal mesh) can work when the component is not directly visible in the view (but reflected)."));
static TAutoConsoleVariable<int32> CVarRayTracingAutoInstance(
TEXT("r.RayTracing.AutoInstance"),
1,
TEXT("Whether to auto instance static meshes\n"),
ECVF_RenderThreadSafe
);
static int32 GRayTracingDebugDisableTriangleCull = 0;
static FAutoConsoleVariableRef CVarRayTracingDebugDisableTriangleCull(
TEXT("r.RayTracing.DebugDisableTriangleCull"),
GRayTracingDebugDisableTriangleCull,
TEXT("Forces all ray tracing geometry instances to be double-sided by disabling back-face culling. This is useful for debugging and profiling. (default = 0)")
);
static int32 GRayTracingDebugForceOpaque = 0;
static FAutoConsoleVariableRef CVarRayTracingDebugForceOpaque(
TEXT("r.RayTracing.DebugForceOpaque"),
GRayTracingDebugForceOpaque,
TEXT("Forces all ray tracing geometry instances to be opaque, effectively disabling any-hit shaders. This is useful for debugging and profiling. (default = 0)")
);
static int32 GNumLODTasksToInline = 10;
FAutoConsoleVariableRef CVarNumLODTasksToInline(
TEXT("r.RayTracing.GatherWorldInstancingInlineThreshold"),
GNumLODTasksToInline,
TEXT(""),
ECVF_Scalability | ECVF_RenderThreadSafe);
#if !UE_BUILD_SHIPPING
static TAutoConsoleVariable<int32> CVarForceBlackVelocityBuffer(
TEXT("r.Test.ForceBlackVelocityBuffer"), 0,
TEXT("Force the velocity buffer to have no motion vector for debugging purpose."),
ECVF_RenderThreadSafe);
#endif
static TAutoConsoleVariable<int32> CVarNaniteViewMeshLODBiasEnable(
TEXT("r.Nanite.ViewMeshLODBias.Enable"), 1,
TEXT("Whether LOD offset to apply for rasterized Nanite meshes for the main viewport should be based off TSR's ScreenPercentage (Enabled by default)."),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<float> CVarNaniteViewMeshLODBiasOffset(
TEXT("r.Nanite.ViewMeshLODBias.Offset"), 0.0f,
TEXT("LOD offset to apply for rasterized Nanite meshes for the main viewport when using TSR (Default = 0)."),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<float> CVarNaniteViewMeshLODBiasMin(
TEXT("r.Nanite.ViewMeshLODBias.Min"), -2.0f,
TEXT("Minimum LOD offset for rasterizing Nanite meshes for the main viewport (Default = -2)."),
ECVF_RenderThreadSafe);
static int32 GNaniteProgrammableRasterPrimary = 1;
static FAutoConsoleVariableRef CNaniteProgrammableRasterPrimary(
TEXT("r.Nanite.ProgrammableRaster.Primary"),
GNaniteProgrammableRasterPrimary,
TEXT("A toggle that allows Nanite programmable raster in the primary pass.\n")
TEXT(" 0: Programmable raster is disabled\n")
TEXT(" 1: Programmable raster is enabled (default)"),
ECVF_RenderThreadSafe);
namespace Lumen
{
extern bool AnyLumenHardwareRayTracingPassEnabled();
}
namespace Nanite
{
extern bool IsStatFilterActive(const FString& FilterName);
extern void ListStatFilters(FSceneRenderer* SceneRenderer);
}
DECLARE_CYCLE_STAT(TEXT("InitViews Intentional Stall"), STAT_InitViews_Intentional_Stall, STATGROUP_InitViews);
DECLARE_CYCLE_STAT(TEXT("DeferredShadingSceneRenderer UpdateDownsampledDepthSurface"), STAT_FDeferredShadingSceneRenderer_UpdateDownsampledDepthSurface, STATGROUP_SceneRendering);
DECLARE_CYCLE_STAT(TEXT("DeferredShadingSceneRenderer Render Init"), STAT_FDeferredShadingSceneRenderer_Render_Init, STATGROUP_SceneRendering);
DECLARE_CYCLE_STAT(TEXT("DeferredShadingSceneRenderer FGlobalDynamicVertexBuffer Commit"), STAT_FDeferredShadingSceneRenderer_FGlobalDynamicVertexBuffer_Commit, STATGROUP_SceneRendering);
DECLARE_CYCLE_STAT(TEXT("DeferredShadingSceneRenderer FXSystem PreRender"), STAT_FDeferredShadingSceneRenderer_FXSystem_PreRender, STATGROUP_SceneRendering);
DECLARE_CYCLE_STAT(TEXT("DeferredShadingSceneRenderer AllocGBufferTargets"), STAT_FDeferredShadingSceneRenderer_AllocGBufferTargets, STATGROUP_SceneRendering);
DECLARE_CYCLE_STAT(TEXT("DeferredShadingSceneRenderer DBuffer"), STAT_FDeferredShadingSceneRenderer_DBuffer, STATGROUP_SceneRendering);
DECLARE_CYCLE_STAT(TEXT("DeferredShadingSceneRenderer ResolveDepth After Basepass"), STAT_FDeferredShadingSceneRenderer_ResolveDepth_After_Basepass, STATGROUP_SceneRendering);
DECLARE_CYCLE_STAT(TEXT("DeferredShadingSceneRenderer Resolve After Basepass"), STAT_FDeferredShadingSceneRenderer_Resolve_After_Basepass, STATGROUP_SceneRendering);
DECLARE_CYCLE_STAT(TEXT("DeferredShadingSceneRenderer FXSystem PostRenderOpaque"), STAT_FDeferredShadingSceneRenderer_FXSystem_PostRenderOpaque, STATGROUP_SceneRendering);
DECLARE_CYCLE_STAT(TEXT("DeferredShadingSceneRenderer AfterBasePass"), STAT_FDeferredShadingSceneRenderer_AfterBasePass, STATGROUP_SceneRendering);
DECLARE_CYCLE_STAT(TEXT("DeferredShadingSceneRenderer Lighting"), STAT_FDeferredShadingSceneRenderer_Lighting, STATGROUP_SceneRendering);
DECLARE_CYCLE_STAT(TEXT("DeferredShadingSceneRenderer RenderLightShaftOcclusion"), STAT_FDeferredShadingSceneRenderer_RenderLightShaftOcclusion, STATGROUP_SceneRendering);
DECLARE_CYCLE_STAT(TEXT("DeferredShadingSceneRenderer RenderAtmosphere"), STAT_FDeferredShadingSceneRenderer_RenderAtmosphere, STATGROUP_SceneRendering);
DECLARE_CYCLE_STAT(TEXT("DeferredShadingSceneRenderer RenderSkyAtmosphere"), STAT_FDeferredShadingSceneRenderer_RenderSkyAtmosphere, STATGROUP_SceneRendering);
DECLARE_CYCLE_STAT(TEXT("DeferredShadingSceneRenderer RenderFog"), STAT_FDeferredShadingSceneRenderer_RenderFog, STATGROUP_SceneRendering);
DECLARE_CYCLE_STAT(TEXT("DeferredShadingSceneRenderer RenderLightShaftBloom"), STAT_FDeferredShadingSceneRenderer_RenderLightShaftBloom, STATGROUP_SceneRendering);
DECLARE_CYCLE_STAT(TEXT("DeferredShadingSceneRenderer RenderFinish"), STAT_FDeferredShadingSceneRenderer_RenderFinish, STATGROUP_SceneRendering);
DECLARE_GPU_STAT(RayTracingScene);
DECLARE_GPU_STAT(RayTracingGeometry);
DECLARE_GPU_STAT(Postprocessing);
DECLARE_GPU_STAT(VisibilityCommands);
DECLARE_GPU_STAT(RenderDeferredLighting);
DECLARE_GPU_STAT(AllocateRendertargets);
DECLARE_GPU_STAT(FrameRenderFinish);
DECLARE_GPU_STAT(SortLights);
DECLARE_GPU_STAT(PostRenderOpsFX);
DECLARE_GPU_STAT(GPUSceneUpdate);
DECLARE_GPU_STAT_NAMED(Unaccounted, TEXT("[unaccounted]"));
DECLARE_GPU_DRAWCALL_STAT(WaterRendering);
DECLARE_GPU_STAT(HairRendering);
DEFINE_GPU_DRAWCALL_STAT(VirtualTextureUpdate);
DECLARE_GPU_STAT(UploadDynamicBuffers);
DECLARE_GPU_STAT(PostOpaqueExtensions);
CSV_DEFINE_CATEGORY(LightCount, true);
/*-----------------------------------------------------------------------------
Global Illumination Plugin Function Delegates
-----------------------------------------------------------------------------*/
static FGlobalIlluminationPluginDelegates::FAnyRayTracingPassEnabled GIPluginAnyRaytracingPassEnabledDelegate;
FGlobalIlluminationPluginDelegates::FAnyRayTracingPassEnabled& FGlobalIlluminationPluginDelegates::AnyRayTracingPassEnabled()
{
return GIPluginAnyRaytracingPassEnabledDelegate;
}
static FGlobalIlluminationPluginDelegates::FPrepareRayTracing GIPluginPrepareRayTracingDelegate;
FGlobalIlluminationPluginDelegates::FPrepareRayTracing& FGlobalIlluminationPluginDelegates::PrepareRayTracing()
{
return GIPluginPrepareRayTracingDelegate;
}
static FGlobalIlluminationPluginDelegates::FRenderDiffuseIndirectLight GIPluginRenderDiffuseIndirectLightDelegate;
FGlobalIlluminationPluginDelegates::FRenderDiffuseIndirectLight& FGlobalIlluminationPluginDelegates::RenderDiffuseIndirectLight()
{
return GIPluginRenderDiffuseIndirectLightDelegate;
}
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
static FGlobalIlluminationPluginDelegates::FRenderDiffuseIndirectVisualizations GIPluginRenderDiffuseIndirectVisualizationsDelegate;
FGlobalIlluminationPluginDelegates::FRenderDiffuseIndirectVisualizations& FGlobalIlluminationPluginDelegates::RenderDiffuseIndirectVisualizations()
{
return GIPluginRenderDiffuseIndirectVisualizationsDelegate;
}
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
const TCHAR* GetDepthPassReason(bool bDitheredLODTransitionsUseStencil, EShaderPlatform ShaderPlatform)
{
if (IsForwardShadingEnabled(ShaderPlatform))
{
return TEXT("(Forced by ForwardShading)");
}
bool bUseNanite = UseNanite(ShaderPlatform);
if (bUseNanite)
{
return TEXT("(Forced by Nanite)");
}
bool bDBufferAllowed = IsUsingDBuffers(ShaderPlatform);
if (bDBufferAllowed)
{
return TEXT("(Forced by DBuffer)");
}
if (bDitheredLODTransitionsUseStencil)
{
return TEXT("(Forced by StencilLODDither)");
}
return TEXT("");
}
/*-----------------------------------------------------------------------------
FDeferredShadingSceneRenderer
-----------------------------------------------------------------------------*/
FDeferredShadingSceneRenderer::FDeferredShadingSceneRenderer(TArrayView<const FSceneViewFamily*> InViewFamilies, FHitProxyConsumer* HitProxyConsumer)
: FSceneRenderer(InViewFamilies, HitProxyConsumer)
, DepthPass(GetDepthPassInfo(Scene))
, bAreLightsInLightGrid(false)
{
ViewPipelineStates.SetNum(AllFamilyViews.Num());
FamilyPipelineStates.SetNum(ViewFamilies.Num());
}
/**
* Renders the view family.
*/
DEFINE_STAT(STAT_CLM_PrePass);
DECLARE_CYCLE_STAT(TEXT("FXPreRender"), STAT_CLM_FXPreRender, STATGROUP_CommandListMarkers);
DECLARE_CYCLE_STAT(TEXT("AfterPrePass"), STAT_CLM_AfterPrePass, STATGROUP_CommandListMarkers);
DECLARE_CYCLE_STAT(TEXT("Lighting"), STAT_CLM_Lighting, STATGROUP_CommandListMarkers);
DECLARE_CYCLE_STAT(TEXT("AfterLighting"), STAT_CLM_AfterLighting, STATGROUP_CommandListMarkers);
DECLARE_CYCLE_STAT(TEXT("WaterPass"), STAT_CLM_WaterPass, STATGROUP_CommandListMarkers);
DECLARE_CYCLE_STAT(TEXT("Translucency"), STAT_CLM_Translucency, STATGROUP_CommandListMarkers);
DECLARE_CYCLE_STAT(TEXT("Distortion"), STAT_CLM_Distortion, STATGROUP_CommandListMarkers);
DECLARE_CYCLE_STAT(TEXT("AfterTranslucency"), STAT_CLM_AfterTranslucency, STATGROUP_CommandListMarkers);
DECLARE_CYCLE_STAT(TEXT("RenderDistanceFieldLighting"), STAT_CLM_RenderDistanceFieldLighting, STATGROUP_CommandListMarkers);
DECLARE_CYCLE_STAT(TEXT("LightShaftBloom"), STAT_CLM_LightShaftBloom, STATGROUP_CommandListMarkers);
DECLARE_CYCLE_STAT(TEXT("PostProcessing"), STAT_CLM_PostProcessing, STATGROUP_CommandListMarkers);
DECLARE_CYCLE_STAT(TEXT("Velocity"), STAT_CLM_Velocity, STATGROUP_CommandListMarkers);
DECLARE_CYCLE_STAT(TEXT("AfterVelocity"), STAT_CLM_AfterVelocity, STATGROUP_CommandListMarkers);
DECLARE_CYCLE_STAT(TEXT("TranslucentVelocity"), STAT_CLM_TranslucentVelocity, STATGROUP_CommandListMarkers);
DECLARE_CYCLE_STAT(TEXT("RenderFinish"), STAT_CLM_RenderFinish, STATGROUP_CommandListMarkers);
DECLARE_CYCLE_STAT(TEXT("AfterFrame"), STAT_CLM_AfterFrame, STATGROUP_CommandListMarkers);
DECLARE_CYCLE_STAT(TEXT("Wait RayTracing Add Mesh Batch"), STAT_WaitRayTracingAddMesh, STATGROUP_SceneRendering);
FGlobalDynamicIndexBuffer FDeferredShadingSceneRenderer::DynamicIndexBufferForInitViews;
FGlobalDynamicIndexBuffer FDeferredShadingSceneRenderer::DynamicIndexBufferForInitShadows;
FGlobalDynamicVertexBuffer FDeferredShadingSceneRenderer::DynamicVertexBufferForInitViews;
FGlobalDynamicVertexBuffer FDeferredShadingSceneRenderer::DynamicVertexBufferForInitShadows;
TGlobalResource<FGlobalDynamicReadBuffer> FDeferredShadingSceneRenderer::DynamicReadBufferForInitShadows;
TGlobalResource<FGlobalDynamicReadBuffer> FDeferredShadingSceneRenderer::DynamicReadBufferForInitViews;
/**
* Returns true if the depth Prepass needs to run
*/
bool FDeferredShadingSceneRenderer::ShouldRenderPrePass() const
{
return (DepthPass.EarlyZPassMode != DDM_None || DepthPass.bEarlyZPassMovable != 0);
}
bool FDeferredShadingSceneRenderer::RenderHzb(FRDGBuilder& GraphBuilder, FRDGTextureRef SceneDepthTexture)
{
RDG_GPU_STAT_SCOPE(GraphBuilder, HZB);
int32 FamilyIndex = GetViewFamilyIndexInScene(*ActiveViewFamily);
const FFamilyPipelineState& FamilyPipelineState = *FamilyPipelineStates[FamilyIndex];
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
FViewInfo& View = Views[ViewIndex];
RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask);
FSceneViewState* ViewState = View.ViewState;
const FPerViewPipelineState& ViewPipelineState = GetViewPipelineState(View);
if (ViewPipelineState.bClosestHZB || ViewPipelineState.bFurthestHZB)
{
RDG_EVENT_SCOPE(GraphBuilder, "BuildHZB(ViewId=%d)", ViewIndex);
FRDGTextureRef ClosestHZBTexture = nullptr;
FRDGTextureRef FurthestHZBTexture = nullptr;
BuildHZB(
GraphBuilder,
SceneDepthTexture,
/* VisBufferTexture = */ nullptr,
View.ViewRect,
View.GetFeatureLevel(),
View.GetShaderPlatform(),
TEXT("HZBClosest"),
/* OutClosestHZBTexture = */ ViewPipelineState.bClosestHZB ? &ClosestHZBTexture : nullptr,
TEXT("HZBFurthest"),
/* OutFurthestHZBTexture = */ &FurthestHZBTexture);
// Update the view.
{
View.HZBMipmap0Size = FurthestHZBTexture->Desc.Extent;
View.HZB = FurthestHZBTexture;
// Extract furthest HZB texture.
if (View.ViewState)
{
if (IsNaniteEnabled() || FInstanceCullingContext::IsOcclusionCullingEnabled())
{
GraphBuilder.QueueTextureExtraction(FurthestHZBTexture, &View.ViewState->PrevFrameViewInfo.HZB);
}
else
{
View.ViewState->PrevFrameViewInfo.HZB = nullptr;
}
}
// Extract closest HZB texture.
if (ViewPipelineState.bClosestHZB)
{
View.ClosestHZB = ClosestHZBTexture;
}
}
}
if (FamilyPipelineState.bHZBOcclusion && ViewState && ViewState->HZBOcclusionTests.GetNum() != 0)
{
check(ViewState->HZBOcclusionTests.IsValidFrame(ViewState->OcclusionFrameCounter));
ViewState->HZBOcclusionTests.Submit(GraphBuilder, View);
}
}
return FamilyPipelineState.bHZBOcclusion;
}
BEGIN_SHADER_PARAMETER_STRUCT(FRenderOpaqueFXPassParameters, )
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneTextureUniformParameters, SceneTextures)
END_SHADER_PARAMETER_STRUCT()
static void RenderOpaqueFX(
FRDGBuilder& GraphBuilder,
TArrayView<const FViewInfo> Views,
FFXSystemInterface* FXSystem,
TRDGUniformBufferRef<FSceneTextureUniformParameters> SceneTexturesUniformBuffer)
{
// Notify the FX system that opaque primitives have been rendered and we now have a valid depth buffer.
if (FXSystem && Views.Num() > 0)
{
RDG_GPU_STAT_SCOPE(GraphBuilder, PostRenderOpsFX);
RDG_CSV_STAT_EXCLUSIVE_SCOPE(GraphBuilder, RenderOpaqueFX);
const ERDGPassFlags UBPassFlags = ERDGPassFlags::Compute | ERDGPassFlags::Raster | ERDGPassFlags::SkipRenderPass | ERDGPassFlags::NeverCull;
// Add a pass which extracts the RHI handle from the scene textures UB and sends it to the FX system.
FRenderOpaqueFXPassParameters* ExtractUBPassParameters = GraphBuilder.AllocParameters<FRenderOpaqueFXPassParameters>();
ExtractUBPassParameters->SceneTextures = SceneTexturesUniformBuffer;
GraphBuilder.AddPass(RDG_EVENT_NAME("SetSceneTexturesUniformBuffer"), ExtractUBPassParameters, UBPassFlags, [ExtractUBPassParameters, FXSystem](FRHICommandListImmediate&)
{
FXSystem->SetSceneTexturesUniformBuffer(ExtractUBPassParameters->SceneTextures->GetRHIRef());
});
FXSystem->PostRenderOpaque(GraphBuilder, Views, true /*bAllowGPUParticleUpdate*/);
// Clear the scene textures UB pointer on the FX system. Use the same pass parameters to extend resource lifetimes.
GraphBuilder.AddPass(RDG_EVENT_NAME("UnsetSceneTexturesUniformBuffer"), ExtractUBPassParameters, UBPassFlags, [FXSystem](FRHICommandListImmediate&)
{
FXSystem->SetSceneTexturesUniformBuffer(nullptr);
});
if (FGPUSortManager* GPUSortManager = FXSystem->GetGPUSortManager())
{
GPUSortManager->OnPostRenderOpaque(GraphBuilder);
}
GraphBuilder.AddDispatchHint();
}
}
#if RHI_RAYTRACING
static void AddDebugRayTracingInstanceFlags(ERayTracingInstanceFlags& InOutFlags)
{
if (GRayTracingDebugForceOpaque)
{
InOutFlags |= ERayTracingInstanceFlags::ForceOpaque;
}
if (GRayTracingDebugDisableTriangleCull)
{
InOutFlags |= ERayTracingInstanceFlags::TriangleCullDisable;
}
}
bool FDeferredShadingSceneRenderer::GatherRayTracingWorldInstancesForView(FRDGBuilder& GraphBuilder, FViewInfo& View, FRayTracingScene& RayTracingScene)
{
if (!IsRayTracingEnabled())
{
return false;
}
bool bAnyRayTracingPassEnabled = false;
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
bAnyRayTracingPassEnabled |= AnyRayTracingPassEnabled(Scene, Views[ViewIndex]);
}
if (!bAnyRayTracingPassEnabled)
{
return false;
}
TRACE_CPUPROFILER_EVENT_SCOPE(FDeferredShadingSceneRenderer::GatherRayTracingWorldInstances);
SCOPE_CYCLE_COUNTER(STAT_GatherRayTracingWorldInstances);
ActiveViewFamily->RayTracingCollector.ClearViewMeshArrays();
FGPUScenePrimitiveCollector DummyDynamicPrimitiveCollector;
ActiveViewFamily->RayTracingCollector.AddViewMeshArrays(
&View,
&View.RayTracedDynamicMeshElements,
&View.SimpleElementCollector,
&DummyDynamicPrimitiveCollector,
ActiveViewFamily->GetFeatureLevel(),
&DynamicIndexBufferForInitViews,
&DynamicVertexBufferForInitViews,
&DynamicReadBufferForInitViews
);
View.DynamicRayTracingMeshCommandStorage.Reserve(Scene->Primitives.Num());
View.VisibleRayTracingMeshCommands.Reserve(Scene->Primitives.Num());
extern TSet<IPersistentViewUniformBufferExtension*> PersistentViewUniformBufferExtensions;
for (IPersistentViewUniformBufferExtension* Extension : PersistentViewUniformBufferExtensions)
{
Extension->BeginRenderView(&View);
}
View.RayTracingMeshResourceCollector = MakeUnique<FRayTracingMeshResourceCollector>(
Scene->GetFeatureLevel(),
&DynamicIndexBufferForInitViews,
&DynamicVertexBufferForInitViews,
&DynamicReadBufferForInitViews);
View.RayTracingCullingParameters.Init(View);
FRayTracingMaterialGatheringContext MaterialGatheringContext
{
Scene,
&View,
*ActiveViewFamily,
GraphBuilder,
*View.RayTracingMeshResourceCollector
};
const float CurrentWorldTime = View.Family->Time.GetWorldTimeSeconds();
struct FRelevantPrimitive
{
FRHIRayTracingGeometry* RayTracingGeometryRHI = nullptr;
TArrayView<const int32> CachedRayTracingMeshCommandIndices;
uint64 StateHash = 0;
int32 PrimitiveIndex = -1;
int8 LODIndex = -1;
uint8 InstanceMask = 0;
bool bStatic = false;
bool bAllSegmentsOpaque = true;
bool bAnySegmentsCastShadow = false;
bool bAnySegmentsDecal = false;
bool bTwoSided = false;
bool bIsSky = false;
bool bAllSegmentsTranslucent = true;
uint64 InstancingKey() const
{
uint64 Key = StateHash;
Key ^= uint64(InstanceMask) << 32;
Key ^= bAllSegmentsOpaque ? 0x1ull << 40 : 0x0;
Key ^= bAnySegmentsCastShadow ? 0x1ull << 41 : 0x0;
Key ^= bAnySegmentsDecal ? 0x1ull << 42 : 0x0;
Key ^= bTwoSided ? 0x1ull << 43 : 0x0;
Key ^= bIsSky ? 0x1ull << 44 : 0x0;
Key ^= bAllSegmentsTranslucent ? 0x1ull << 45 : 0x0;
return Key ^ reinterpret_cast<uint64>(RayTracingGeometryRHI);
}
};
// Unified array is used for static and dynamic primitives because we don't know ahead of time how many we'll have of each.
TArray<FRelevantPrimitive> RelevantPrimitives;
RelevantPrimitives.Reserve(Scene->PrimitiveSceneProxies.Num());
TArray<FPrimitiveSceneInfo*> DirtyCachedRayTracingPrimitives;
DirtyCachedRayTracingPrimitives.Reserve(Scene->PrimitiveSceneProxies.Num());
const bool bGameView = View.bIsGameView || View.Family->EngineShowFlags.Game;
int32 VisiblePrimitives = 0;
bool bPerformRayTracing = View.State != nullptr && !View.bIsReflectionCapture && View.bAllowRayTracing;
if (bPerformRayTracing)
{
TRACE_CPUPROFILER_EVENT_SCOPE(GatherRayTracingWorldInstances_RelevantPrimitives);
int32 BroadIndex = 0;
for (int PrimitiveIndex = 0; PrimitiveIndex < Scene->PrimitiveSceneProxies.Num(); PrimitiveIndex++)
{
while (PrimitiveIndex >= int(Scene->TypeOffsetTable[BroadIndex].Offset))
{
BroadIndex++;
}
// Skip before dereferencing SceneInfo
if (EnumHasAnyFlags(Scene->PrimitiveRayTracingFlags[PrimitiveIndex], ERayTracingPrimitiveFlags::UnsupportedProxyType))
{
//skip over unsupported SceneProxies (warning don't make IsRayTracingRelevant data dependent other than the vtable)
PrimitiveIndex = Scene->TypeOffsetTable[BroadIndex].Offset - 1;
continue;
}
// Get primitive visibility state from culling
if (!View.PrimitiveRayTracingVisibilityMap[PrimitiveIndex])
{
continue;
}
const FPrimitiveSceneInfo* SceneInfo = Scene->Primitives[PrimitiveIndex];
// #dxr_todo: ray tracing in scene captures should re-use the persistent RT scene. (UE-112448)
bool bShouldRayTraceSceneCapture = GRayTracingSceneCaptures > 0
|| (GRayTracingSceneCaptures == -1 && View.bSceneCaptureUsesRayTracing);
if (View.bIsSceneCapture && (!bShouldRayTraceSceneCapture || !SceneInfo->bIsVisibleInSceneCaptures))
{
continue;
}
if (!View.bIsSceneCapture && SceneInfo->bIsVisibleInSceneCapturesOnly)
{
continue;
}
// Some primitives should only be visible editor mode, however far field geometry
// and hidden shadow casters must still always be added to the RT scene.
if (bGameView && !SceneInfo->bDrawInGame && !SceneInfo->bRayTracingFarField)
{
continue;
}
// Marked visible and used after point, check if streaming then mark as used in the TLAS (so it can be streamed in)
if (EnumHasAnyFlags(Scene->PrimitiveRayTracingFlags[PrimitiveIndex], ERayTracingPrimitiveFlags::Streaming))
{
// Is the cached data dirty?
if (SceneInfo->bCachedRaytracingDataDirty)
{
DirtyCachedRayTracingPrimitives.Add(Scene->Primitives[PrimitiveIndex]);
}
check(SceneInfo->CoarseMeshStreamingHandle != INDEX_NONE);
RayTracingScene.UsedCoarseMeshStreamingHandles.Add(SceneInfo->CoarseMeshStreamingHandle);
}
VisiblePrimitives++;
//#dxr_todo UE-68621 The Raytracing code path does not support ShowFlags since data moved to the SceneInfo.
//Touching the SceneProxy to determine this would simply cost too much
static const auto RayTracingStaticMeshesCVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.RayTracing.Geometry.StaticMeshes"));
FRelevantPrimitive Item;
Item.PrimitiveIndex = PrimitiveIndex;
if (EnumHasAnyFlags(Scene->PrimitiveRayTracingFlags[PrimitiveIndex], ERayTracingPrimitiveFlags::StaticMesh)
&& View.Family->EngineShowFlags.StaticMeshes
&& RayTracingStaticMeshesCVar && RayTracingStaticMeshesCVar->GetValueOnRenderThread() > 0)
{
Item.bStatic = true;
RelevantPrimitives.Add(Item);
}
else if (View.Family->EngineShowFlags.SkeletalMeshes)
{
Item.bStatic = false;
RelevantPrimitives.Add(Item);
}
}
}
INC_DWORD_STAT_BY(STAT_VisibleRayTracingPrimitives, VisiblePrimitives);
FPrimitiveSceneInfo::UpdateCachedRaytracingData(Scene, DirtyCachedRayTracingPrimitives);
FGraphEventArray LODTaskList;
{
TRACE_CPUPROFILER_EVENT_SCOPE(GatherRayTracingWorldInstances_ComputeLOD);
static const auto ICVarStaticMeshLODDistanceScale = IConsoleManager::Get().FindConsoleVariable(TEXT("r.StaticMeshLODDistanceScale"));
const float LODScaleCVarValue = ICVarStaticMeshLODDistanceScale->GetFloat();
const int32 ForcedLODLevel = GetCVarForceLOD();
const uint32 NumTotalItems = RelevantPrimitives.Num();
const uint32 TargetItemsPerTask = 1024; // Granularity based on profiling Infiltrator scene
const uint32 NumTasks = FMath::Max(1u, FMath::DivideAndRoundUp(NumTotalItems, TargetItemsPerTask));
const uint32 ItemsPerTask = FMath::DivideAndRoundUp(NumTotalItems, NumTasks); // Evenly divide commands between tasks (avoiding potential short last task)
auto ComputeLOD =
[ &View,
Scene = this->Scene,
LODScaleCVarValue,
ForcedLODLevel
](FRelevantPrimitive* Items, uint32 NumItems)
{
TRACE_CPUPROFILER_EVENT_SCOPE(GatherRayTracingWorldInstances_ComputeLOD_Task);
for (uint32 i = 0; i < NumItems; ++i)
{
FRelevantPrimitive& RelevantPrimitive = Items[i];
if (!RelevantPrimitive.bStatic)
{
continue; // skip dynamic primitives
}
const int32 PrimitiveIndex = RelevantPrimitive.PrimitiveIndex;
const FPrimitiveSceneInfo* SceneInfo = Scene->Primitives[PrimitiveIndex];
int8 LODIndex = 0;
if (EnumHasAnyFlags(Scene->PrimitiveRayTracingFlags[PrimitiveIndex], ERayTracingPrimitiveFlags::ComputeLOD))
{
const FPrimitiveBounds& Bounds = Scene->PrimitiveBounds[PrimitiveIndex];
const FPrimitiveSceneInfo* RESTRICT PrimitiveSceneInfo = Scene->Primitives[PrimitiveIndex];
FLODMask LODToRender;
const int8 CurFirstLODIdx = PrimitiveSceneInfo->Proxy->GetCurrentFirstLODIdx_RenderThread();
check(CurFirstLODIdx >= 0);
float MeshScreenSizeSquared = 0;
float LODScale = LODScaleCVarValue * View.LODDistanceFactor;
LODToRender = ComputeLODForMeshes(SceneInfo->StaticMeshRelevances, View, Bounds.BoxSphereBounds.Origin, Bounds.BoxSphereBounds.SphereRadius, ForcedLODLevel, MeshScreenSizeSquared, CurFirstLODIdx, LODScale, true);
LODIndex = LODToRender.GetRayTracedLOD();
}
if (!EnumHasAllFlags(Scene->PrimitiveRayTracingFlags[PrimitiveIndex], ERayTracingPrimitiveFlags::CacheInstances))
{
FRHIRayTracingGeometry* RayTracingGeometryInstance = SceneInfo->GetStaticRayTracingGeometryInstance(LODIndex);
if (RayTracingGeometryInstance == nullptr)
{
continue;
}
// Sometimes LODIndex is out of range because it is clamped by ClampToFirstLOD, like the requested LOD is being streamed in and hasn't been available
// According to InitViews, we should hide the static mesh instance
if (SceneInfo->CachedRayTracingMeshCommandIndicesPerLOD.IsValidIndex(LODIndex))
{
RelevantPrimitive.LODIndex = LODIndex;
RelevantPrimitive.RayTracingGeometryRHI = SceneInfo->GetStaticRayTracingGeometryInstance(LODIndex);
RelevantPrimitive.CachedRayTracingMeshCommandIndices = SceneInfo->CachedRayTracingMeshCommandIndicesPerLOD[LODIndex];
RelevantPrimitive.StateHash = SceneInfo->CachedRayTracingMeshCommandsHashPerLOD[LODIndex];
for (int32 CommandIndex : RelevantPrimitive.CachedRayTracingMeshCommandIndices)
{
if (CommandIndex >= 0)
{
const FRayTracingMeshCommand& RayTracingMeshCommand = Scene->CachedRayTracingMeshCommands[CommandIndex];
RelevantPrimitive.InstanceMask |= RayTracingMeshCommand.InstanceMask;
RelevantPrimitive.bAllSegmentsOpaque &= RayTracingMeshCommand.bOpaque;
RelevantPrimitive.bAnySegmentsCastShadow |= RayTracingMeshCommand.bCastRayTracedShadows;
RelevantPrimitive.bAnySegmentsDecal |= RayTracingMeshCommand.bDecal;
RelevantPrimitive.bTwoSided |= RayTracingMeshCommand.bTwoSided;
RelevantPrimitive.bIsSky |= RayTracingMeshCommand.bIsSky;
RelevantPrimitive.bAllSegmentsTranslucent &= RayTracingMeshCommand.bIsTranslucent;
}
else
{
// CommandIndex == -1 indicates that the mesh batch has been filtered by FRayTracingMeshProcessor (like the shadow depth pass batch)
// Do nothing in this case
}
}
RelevantPrimitive.InstanceMask |= RelevantPrimitive.bAnySegmentsCastShadow ? RAY_TRACING_MASK_SHADOW : 0;
if (EnumHasAllFlags(Scene->PrimitiveRayTracingFlags[PrimitiveIndex], ERayTracingPrimitiveFlags::FarField))
{
RelevantPrimitive.InstanceMask = RAY_TRACING_MASK_FAR_FIELD;
}
}
}
}
};
if (NumTasks > (uint32)GNumLODTasksToInline)
{
SCOPED_NAMED_EVENT(DispatchParallelComputeLOD, FColor::Red);
LODTaskList.Reserve(NumTasks);
for (uint32 TaskIndex = 0; TaskIndex < NumTasks; ++TaskIndex)
{
const uint32 FirstTaskItemIndex = TaskIndex * ItemsPerTask;
LODTaskList.Add(FFunctionGraphTask::CreateAndDispatchWhenReady(
[ FirstTaskItemIndex,
Items = RelevantPrimitives.GetData() + FirstTaskItemIndex,
NumItems = FMath::Min(ItemsPerTask, NumTotalItems - FirstTaskItemIndex),
ComputeLOD
]()
{
ComputeLOD(Items, NumItems);
},
TStatId(), nullptr, ENamedThreads::AnyNormalThreadHiPriTask));
}
}
else
{
SCOPED_NAMED_EVENT(ComputeLOD, FColor::Magenta);
ComputeLOD(RelevantPrimitives.GetData(), NumTotalItems);
}
}
{
TRACE_CPUPROFILER_EVENT_SCOPE(GatherRayTracingWorldInstances_DynamicElements);
const bool bParallelMeshBatchSetup = GRayTracingParallelMeshBatchSetup && FApp::ShouldUseThreadingForPerformance();
const int64 SharedBufferGenerationID = Scene->GetRayTracingDynamicGeometryCollection()->BeginUpdate();
struct FRayTracingMeshBatchWorkItem
{
const FPrimitiveSceneProxy* SceneProxy = nullptr;
TArray<FMeshBatch> MeshBatchesOwned;
TArrayView<const FMeshBatch> MeshBatchesView;
uint32 InstanceIndex = 0;
TArrayView<const FMeshBatch> GetMeshBatches() const
{
if (MeshBatchesOwned.Num())
{
check(MeshBatchesView.Num() == 0);
return TArrayView<const FMeshBatch>(MeshBatchesOwned);
}
else
{
check(MeshBatchesOwned.Num() == 0);
return MeshBatchesView;
}
}
};
static constexpr uint32 MaxWorkItemsPerPage = 128; // Try to keep individual pages small to avoid slow-path memory allocations
struct FRayTracingMeshBatchTaskPage
{
FRayTracingMeshBatchWorkItem WorkItems[MaxWorkItemsPerPage];
uint32 NumWorkItems = 0;
FRayTracingMeshBatchTaskPage* Next = nullptr;
};
FRayTracingMeshBatchTaskPage* MeshBatchTaskHead = nullptr;
FRayTracingMeshBatchTaskPage* MeshBatchTaskPage = nullptr;
uint32 NumPendingMeshBatches = 0;
const uint32 RayTracingParallelMeshBatchSize = GRayTracingParallelMeshBatchSize;
auto KickRayTracingMeshBatchTask = [&View, &MeshBatchTaskHead, &MeshBatchTaskPage, &NumPendingMeshBatches, Scene = this->Scene]()
{
if (MeshBatchTaskHead)
{
FDynamicRayTracingMeshCommandStorage* TaskDynamicCommandStorage = new(FMemStack::Get()) FDynamicRayTracingMeshCommandStorage;
View.DynamicRayTracingMeshCommandStoragePerTask.Add(TaskDynamicCommandStorage);
FRayTracingMeshCommandOneFrameArray* TaskVisibleCommands = new(FMemStack::Get()) FRayTracingMeshCommandOneFrameArray;
TaskVisibleCommands->Reserve(NumPendingMeshBatches);
View.VisibleRayTracingMeshCommandsPerTask.Add(TaskVisibleCommands);
View.AddRayTracingMeshBatchTaskList.Add(FFunctionGraphTask::CreateAndDispatchWhenReady(
[TaskDataHead = MeshBatchTaskHead, &View, Scene, TaskDynamicCommandStorage, TaskVisibleCommands]()
{
FTaskTagScope TaskTagScope(ETaskTag::EParallelRenderingThread);
TRACE_CPUPROFILER_EVENT_SCOPE(RayTracingMeshBatchTask);
FRayTracingMeshBatchTaskPage* Page = TaskDataHead;
const int32 ExpectedMaxVisibieCommands = TaskVisibleCommands->Max();
while (Page)
{
for (uint32 ItemIndex = 0; ItemIndex < Page->NumWorkItems; ++ItemIndex)
{
const FRayTracingMeshBatchWorkItem& WorkItem = Page->WorkItems[ItemIndex];
TArrayView<const FMeshBatch> MeshBatches = WorkItem.GetMeshBatches();
for (int32 SegmentIndex = 0; SegmentIndex < MeshBatches.Num(); SegmentIndex++)
{
const FMeshBatch& MeshBatch = MeshBatches[SegmentIndex];
FDynamicRayTracingMeshCommandContext CommandContext(
*TaskDynamicCommandStorage, *TaskVisibleCommands,
SegmentIndex, WorkItem.InstanceIndex);
FMeshPassProcessorRenderState PassDrawRenderState(Scene->UniformBuffers.ViewUniformBuffer);
FRayTracingMeshProcessor RayTracingMeshProcessor(&CommandContext, Scene, &View, PassDrawRenderState, Scene->CachedRayTracingMeshCommandsMode);
RayTracingMeshProcessor.AddMeshBatch(MeshBatch, 1, WorkItem.SceneProxy);
}
}
FRayTracingMeshBatchTaskPage* NextPage = Page->Next;
Page->~FRayTracingMeshBatchTaskPage();
Page = NextPage;
}
check(ExpectedMaxVisibieCommands <= TaskVisibleCommands->Max());
}, TStatId(), nullptr, ENamedThreads::AnyThread));
}
MeshBatchTaskHead = nullptr;
MeshBatchTaskPage = nullptr;
NumPendingMeshBatches = 0;
};
// Local temporary array of instances used for GetDynamicRayTracingInstances()
TArray<FRayTracingInstance> TempRayTracingInstances;
for (const FRelevantPrimitive& RelevantPrimitive : RelevantPrimitives)
{
if (RelevantPrimitive.bStatic)
{
continue;
}
const int32 PrimitiveIndex = RelevantPrimitive.PrimitiveIndex;
FPrimitiveSceneInfo* SceneInfo = Scene->Primitives[PrimitiveIndex];
FPrimitiveSceneProxy* SceneProxy = Scene->PrimitiveSceneProxies[PrimitiveIndex];
TempRayTracingInstances.Reset();
MaterialGatheringContext.DynamicRayTracingGeometriesToUpdate.Reset();
SceneProxy->GetDynamicRayTracingInstances(MaterialGatheringContext, TempRayTracingInstances);
for (auto DynamicRayTracingGeometryUpdate : MaterialGatheringContext.DynamicRayTracingGeometriesToUpdate)
{
Scene->GetRayTracingDynamicGeometryCollection()->AddDynamicMeshBatchForGeometryUpdate(
Scene,
&View,
SceneProxy,
DynamicRayTracingGeometryUpdate,
PrimitiveIndex
);
}
if (TempRayTracingInstances.Num() > 0)
{
for (FRayTracingInstance& Instance : TempRayTracingInstances)
{
const FRayTracingGeometry* Geometry = Instance.Geometry;
if (!ensureMsgf(Geometry->DynamicGeometrySharedBufferGenerationID == FRayTracingGeometry::NonSharedVertexBuffers
|| Geometry->DynamicGeometrySharedBufferGenerationID == SharedBufferGenerationID,
TEXT("GenerationID %lld, but expected to be %lld or %lld. Geometry debug name: '%s'. ")
TEXT("When shared vertex buffers are used, the contents is expected to be written every frame. ")
TEXT("Possibly AddDynamicMeshBatchForGeometryUpdate() was not called for this geometry."),
Geometry->DynamicGeometrySharedBufferGenerationID, SharedBufferGenerationID, FRayTracingGeometry::NonSharedVertexBuffers,
*Geometry->Initializer.DebugName.ToString()))
{
continue;
}
// If geometry still has pending build request then add to list which requires a force build
if (Geometry->HasPendingBuildRequest())
{
RayTracingScene.GeometriesToBuild.Add(Geometry);
}
// Validate the material/segment counts
if (!ensureMsgf(Instance.GetMaterials().Num() == Geometry->Initializer.Segments.Num() ||
(Geometry->Initializer.Segments.Num() == 0 && Instance.GetMaterials().Num() == 1),
TEXT("Ray tracing material assignment validation failed for geometry '%s'. "
"Instance.GetMaterials().Num() = %d, Geometry->Initializer.Segments.Num() = %d, Instance.Mask = 0x%X."),
*Geometry->Initializer.DebugName.ToString(), Instance.GetMaterials().Num(),
Geometry->Initializer.Segments.Num(), Instance.Mask))
{
continue;
}
const uint32 InstanceIndex = RayTracingScene.Instances.Num();
FRayTracingGeometryInstance& RayTracingInstance = RayTracingScene.Instances.AddDefaulted_GetRef();
RayTracingInstance.GeometryRHI = Geometry->RayTracingGeometryRHI;
checkf(RayTracingInstance.GeometryRHI, TEXT("Ray tracing instance must have a valid geometry."));
RayTracingInstance.DefaultUserData = PrimitiveIndex;
RayTracingInstance.Mask = Instance.Mask;
RayTracingInstance.bApplyLocalBoundsTransform = Instance.bApplyLocalBoundsTransform;
if (Instance.bForceOpaque)
{
RayTracingInstance.Flags |= ERayTracingInstanceFlags::ForceOpaque;
}
if (Instance.bDoubleSided)
{
RayTracingInstance.Flags |= ERayTracingInstanceFlags::TriangleCullDisable;
}
AddDebugRayTracingInstanceFlags(RayTracingInstance.Flags);
if (Instance.InstanceGPUTransformsSRV.IsValid())
{
RayTracingInstance.NumTransforms = Instance.NumTransforms;
RayTracingInstance.GPUTransformsSRV = Instance.InstanceGPUTransformsSRV;
}
else
{
if (Instance.OwnsTransforms())
{
// Slow path: copy transforms to the owned storage
checkf(Instance.InstanceTransformsView.Num() == 0, TEXT("InstanceTransformsView is expected to be empty if using InstanceTransforms"));
TArrayView<FMatrix> SceneOwnedTransforms = RayTracingScene.Allocate<FMatrix>(Instance.InstanceTransforms.Num());
FMemory::Memcpy(SceneOwnedTransforms.GetData(), Instance.InstanceTransforms.GetData(), Instance.InstanceTransforms.Num() * sizeof(RayTracingInstance.Transforms[0]));
static_assert(TIsSame<decltype(SceneOwnedTransforms[0]), decltype(Instance.InstanceTransforms[0])>::Value, "Unexpected transform type");
RayTracingInstance.NumTransforms = SceneOwnedTransforms.Num();
RayTracingInstance.Transforms = SceneOwnedTransforms;
}
else
{
// Fast path: just reference persistently-allocated transforms and avoid a copy
checkf(Instance.InstanceTransforms.Num() == 0, TEXT("InstanceTransforms is expected to be empty if using InstanceTransformsView"));
RayTracingInstance.NumTransforms = Instance.InstanceTransformsView.Num();
RayTracingInstance.Transforms = Instance.InstanceTransformsView;
}
}
if (bParallelMeshBatchSetup)
{
if (NumPendingMeshBatches >= RayTracingParallelMeshBatchSize)
{
KickRayTracingMeshBatchTask();
}
if (MeshBatchTaskPage == nullptr || MeshBatchTaskPage->NumWorkItems == MaxWorkItemsPerPage)
{
FRayTracingMeshBatchTaskPage* NextPage = new(FMemStack::Get()) FRayTracingMeshBatchTaskPage;
if (MeshBatchTaskHead == nullptr)
{
MeshBatchTaskHead = NextPage;
}
if (MeshBatchTaskPage)
{
MeshBatchTaskPage->Next = NextPage;
}
MeshBatchTaskPage = NextPage;
}
FRayTracingMeshBatchWorkItem& WorkItem = MeshBatchTaskPage->WorkItems[MeshBatchTaskPage->NumWorkItems];
MeshBatchTaskPage->NumWorkItems++;
NumPendingMeshBatches += Instance.GetMaterials().Num();
if (Instance.OwnsMaterials())
{
Swap(WorkItem.MeshBatchesOwned, Instance.Materials);
}
else
{
WorkItem.MeshBatchesView = Instance.MaterialsView;
}
WorkItem.SceneProxy = SceneProxy;
WorkItem.InstanceIndex = InstanceIndex;
}
else
{
TArrayView<const FMeshBatch> InstanceMaterials = Instance.GetMaterials();
for (int32 SegmentIndex = 0; SegmentIndex < InstanceMaterials.Num(); SegmentIndex++)
{
const FMeshBatch& MeshBatch = InstanceMaterials[SegmentIndex];
FDynamicRayTracingMeshCommandContext CommandContext(View.DynamicRayTracingMeshCommandStorage, View.VisibleRayTracingMeshCommands, SegmentIndex, InstanceIndex);
FMeshPassProcessorRenderState PassDrawRenderState(Scene->UniformBuffers.ViewUniformBuffer);
FRayTracingMeshProcessor RayTracingMeshProcessor(&CommandContext, Scene, &View, PassDrawRenderState, Scene->CachedRayTracingMeshCommandsMode);
RayTracingMeshProcessor.AddMeshBatch(MeshBatch, 1, SceneProxy);
}
}
}
if (CVarRayTracingDynamicGeometryLastRenderTimeUpdateDistance.GetValueOnRenderThread() > 0.0f)
{
if (FVector::Distance(SceneProxy->GetActorPosition(), View.ViewMatrices.GetViewOrigin()) < CVarRayTracingDynamicGeometryLastRenderTimeUpdateDistance.GetValueOnRenderThread())
{
// Update LastRenderTime for components so that visibility based ticking (like skeletal meshes) can get updated
// We are only doing this for dynamic geometries now
SceneInfo->LastRenderTime = CurrentWorldTime;
SceneInfo->UpdateComponentLastRenderTime(CurrentWorldTime, /*bUpdateLastRenderTimeOnScreen=*/true);
SceneInfo->ConditionalUpdateUniformBuffer(GraphBuilder.RHICmdList);
}
}
}
}
KickRayTracingMeshBatchTask();
}
//
{
TRACE_CPUPROFILER_EVENT_SCOPE(GatherRayTracingWorldInstances_AddInstances);
const bool bAutoInstance = CVarRayTracingAutoInstance.GetValueOnRenderThread() != 0;
if (LODTaskList.Num() > 0)
{
SCOPED_NAMED_EVENT(WaitForParallelComputeLOD, FColor::Red);
TRACE_CPUPROFILER_EVENT_SCOPE(WaitForLODTasks);
FTaskGraphInterface::Get().WaitUntilTasksComplete(LODTaskList, ENamedThreads::GetRenderThread_Local());
}
struct FAutoInstanceBatch
{
int32 Index = INDEX_NONE;
// Copies the next InstanceSceneDataOffset and user data into the current batch, returns true if arrays were re-allocated.
bool Add(FRayTracingScene& RayTracingScene, uint32 InInstanceSceneDataOffset, uint32 InUserData)
{
// Adhoc TArray-like resize behavior, in lieu of support for using a custom FMemStackBase in TArray.
// Idea for future: if batch becomes large enough, we could actually split it into multiple instances to avoid memory waste.
const bool bNeedReallocation = Cursor == InstanceSceneDataOffsets.Num();
if (bNeedReallocation)
{
int32 PrevCount = InstanceSceneDataOffsets.Num();
int32 NextCount = FMath::Max(PrevCount * 2, 1);
TArrayView<uint32> NewInstanceSceneDataOffsets = RayTracingScene.Allocate<uint32>(NextCount);
if (PrevCount)
{
FMemory::Memcpy(NewInstanceSceneDataOffsets.GetData(), InstanceSceneDataOffsets.GetData(), InstanceSceneDataOffsets.GetTypeSize() * InstanceSceneDataOffsets.Num());
}
InstanceSceneDataOffsets = NewInstanceSceneDataOffsets;
TArrayView<uint32> NewUserData = RayTracingScene.Allocate<uint32>(NextCount);
if (PrevCount)
{
FMemory::Memcpy(NewUserData.GetData(), UserData.GetData(), UserData.GetTypeSize() * UserData.Num());
}
UserData = NewUserData;
}
InstanceSceneDataOffsets[Cursor] = InInstanceSceneDataOffset;
UserData[Cursor] = InUserData;
++Cursor;
return bNeedReallocation;
}
bool IsValid() const
{
return InstanceSceneDataOffsets.Num() != 0;
}
TArrayView<uint32> InstanceSceneDataOffsets;
TArrayView<uint32> UserData;
uint32 Cursor = 0;
};
Experimental::TSherwoodMap<uint64, FAutoInstanceBatch> InstanceBatches;
InstanceBatches.Reserve(RelevantPrimitives.Num());
TArray<FRayTracingCullPrimitiveInstancesClosure> CullInstancesClosures;
if (View.RayTracingCullingParameters.CullInRayTracing > 0 && GetRayTracingCullingPerInstance())
{
CullInstancesClosures.Reserve(RelevantPrimitives.Num());
View.RayTracingPerInstanceCullingTaskList.Reserve(RelevantPrimitives.Num() / 256 + 1);
}
// scan relevant primitives computing hash data to look for duplicate instances
for (const FRelevantPrimitive& RelevantPrimitive : RelevantPrimitives)
{
const int32 PrimitiveIndex = RelevantPrimitive.PrimitiveIndex;
FPrimitiveSceneInfo* SceneInfo = Scene->Primitives[PrimitiveIndex];
ERayTracingPrimitiveFlags Flags = Scene->PrimitiveRayTracingFlags[PrimitiveIndex];
if (EnumHasAnyFlags(Flags, ERayTracingPrimitiveFlags::CacheInstances))
{
// TODO: support GRayTracingExcludeDecals, but not in the form of RayTracingMeshCommand.bDecal as that requires looping over all cached MDCs
// Instead, either make r.RayTracing.ExcludeDecals read only or request a recache of all ray tracing commands during which decals are excluded
const int32 NewInstanceIndex = RayTracingScene.Instances.Num();
// At the moment we only support SM & ISMs on this path
check(EnumHasAnyFlags(Flags, ERayTracingPrimitiveFlags::CacheMeshCommands));
if (SceneInfo->CachedRayTracingMeshCommandIndicesPerLOD.Num() > 0 && SceneInfo->CachedRayTracingMeshCommandIndicesPerLOD[0].Num() > 0)
{
for (int32 CommandIndex : SceneInfo->CachedRayTracingMeshCommandIndicesPerLOD[0])
{
FVisibleRayTracingMeshCommand NewVisibleMeshCommand;
NewVisibleMeshCommand.RayTracingMeshCommand = &Scene->CachedRayTracingMeshCommands[CommandIndex];
NewVisibleMeshCommand.InstanceIndex = NewInstanceIndex;
View.VisibleRayTracingMeshCommands.Add(NewVisibleMeshCommand);
}
}
checkf(SceneInfo->CachedRayTracingInstance.GeometryRHI, TEXT("Ray tracing instance must have a valid geometry."));
RayTracingScene.Instances.Add(SceneInfo->CachedRayTracingInstance);
if (View.RayTracingCullingParameters.CullInRayTracing > 0 && GetRayTracingCullingPerInstance() && SceneInfo->CachedRayTracingInstance.NumTransforms > 1)
{
FRayTracingGeometryInstance& NewInstance = RayTracingScene.Instances.Last();
const bool bIsFarFieldPrimitive = EnumHasAnyFlags(Flags, ERayTracingPrimitiveFlags::FarField);
TArrayView<uint32> InstanceActivationMask = RayTracingScene.Allocate<uint32>(FMath::DivideAndRoundUp(NewInstance.NumTransforms, 32u));
NewInstance.ActivationMask = InstanceActivationMask;
FRayTracingCullPrimitiveInstancesClosure Closure;
Closure.Scene = Scene;
Closure.SceneInfo = SceneInfo;
Closure.PrimitiveIndex = PrimitiveIndex;
Closure.bIsFarFieldPrimitive = bIsFarFieldPrimitive;
Closure.CullingParameters = &View.RayTracingCullingParameters;
Closure.OutInstanceActivationMask = InstanceActivationMask;
CullInstancesClosures.Add(MoveTemp(Closure));
if (CullInstancesClosures.Num() >= 256)
{
View.RayTracingPerInstanceCullingTaskList.Add(FFunctionGraphTask::CreateAndDispatchWhenReady([CullInstancesClosures = MoveTemp(CullInstancesClosures)]()
{
for (auto& Closure : CullInstancesClosures)
{
Closure();
}
}, TStatId(), nullptr, ENamedThreads::AnyThread));
}
}
AddDebugRayTracingInstanceFlags(RayTracingScene.Instances.Last().Flags);
}
else
{
const int8 LODIndex = RelevantPrimitive.LODIndex;
if (LODIndex < 0 || !RelevantPrimitive.bStatic)
{
continue; // skip dynamic primitives and other
}
if ((GRayTracingExcludeDecals && RelevantPrimitive.bAnySegmentsDecal)
|| (GRayTracingExcludeTranslucent && RelevantPrimitive.bAllSegmentsTranslucent)
|| RelevantPrimitive.bIsSky)
{
continue;
}
// location if this is a new entry
const int32 NewInstanceIndex = RayTracingScene.Instances.Num();
const uint64 InstanceKey = RelevantPrimitive.InstancingKey();
FAutoInstanceBatch DummyInstanceBatch = { NewInstanceIndex };
FAutoInstanceBatch& InstanceBatch = bAutoInstance ? InstanceBatches.FindOrAdd(InstanceKey, DummyInstanceBatch) : DummyInstanceBatch;
if (InstanceBatch.Index != NewInstanceIndex)
{
// Reusing a previous entry, just append to the instance list.
FRayTracingGeometryInstance& RayTracingInstance = RayTracingScene.Instances[InstanceBatch.Index];
bool bReallocated = InstanceBatch.Add(RayTracingScene, SceneInfo->GetInstanceSceneDataOffset(), (uint32)PrimitiveIndex);
++RayTracingInstance.NumTransforms;
check(RayTracingInstance.NumTransforms == InstanceBatch.Cursor); // sanity check
if (bReallocated)
{
RayTracingInstance.InstanceSceneDataOffsets = InstanceBatch.InstanceSceneDataOffsets;
RayTracingInstance.UserData = InstanceBatch.UserData;
}
}
else
{
// Starting new instance batch
for (int32 CommandIndex : RelevantPrimitive.CachedRayTracingMeshCommandIndices)
{
if (CommandIndex >= 0)
{
FVisibleRayTracingMeshCommand NewVisibleMeshCommand;
NewVisibleMeshCommand.RayTracingMeshCommand = &Scene->CachedRayTracingMeshCommands[CommandIndex];
NewVisibleMeshCommand.InstanceIndex = NewInstanceIndex;
View.VisibleRayTracingMeshCommands.Add(NewVisibleMeshCommand);
}
else
{
// CommandIndex == -1 indicates that the mesh batch has been filtered by FRayTracingMeshProcessor (like the shadow depth pass batch)
// Do nothing in this case
}
}
FRayTracingGeometryInstance& RayTracingInstance = RayTracingScene.Instances.AddDefaulted_GetRef();
RayTracingInstance.GeometryRHI = RelevantPrimitive.RayTracingGeometryRHI;
checkf(RayTracingInstance.GeometryRHI, TEXT("Ray tracing instance must have a valid geometry."));
InstanceBatch.Add(RayTracingScene, SceneInfo->GetInstanceSceneDataOffset(), (uint32)PrimitiveIndex);
RayTracingInstance.InstanceSceneDataOffsets = InstanceBatch.InstanceSceneDataOffsets;
RayTracingInstance.UserData = InstanceBatch.UserData;
RayTracingInstance.NumTransforms = 1;
RayTracingInstance.Mask = RelevantPrimitive.InstanceMask; // When no cached command is found, InstanceMask == 0 and the instance is effectively filtered out
if (RelevantPrimitive.bAllSegmentsOpaque)
{
RayTracingInstance.Flags |= ERayTracingInstanceFlags::ForceOpaque;
}
if (RelevantPrimitive.bTwoSided)
{
RayTracingInstance.Flags |= ERayTracingInstanceFlags::TriangleCullDisable;
}
AddDebugRayTracingInstanceFlags(RayTracingInstance.Flags);
}
}
}
View.RayTracingPerInstanceCullingTaskList.Add(FFunctionGraphTask::CreateAndDispatchWhenReady([CullInstancesClosures = MoveTemp(CullInstancesClosures)]()
{
for (auto& Closure : CullInstancesClosures)
{
Closure();
}
}, TStatId(), nullptr, ENamedThreads::AnyThread));
}
// Inform the coarse mesh streaming manager about all the used streamable render assets in the scene
Nanite::FCoarseMeshStreamingManager* CoarseMeshSM = IStreamingManager::Get().GetNaniteCoarseMeshStreamingManager();
if (CoarseMeshSM)
{
CoarseMeshSM->AddUsedStreamingHandles(RayTracingScene.UsedCoarseMeshStreamingHandles);
}
return true;
}
static void DeduplicateRayGenerationShaders(TArray< FRHIRayTracingShader*>& RayGenShaders)
{
TSet<FRHIRayTracingShader*> UniqueRayGenShaders;
for (FRHIRayTracingShader* Shader : RayGenShaders)
{
UniqueRayGenShaders.Add(Shader);
}
RayGenShaders = UniqueRayGenShaders.Array();
}
BEGIN_SHADER_PARAMETER_STRUCT(FBuildAccelerationStructurePassParams, )
RDG_BUFFER_ACCESS(RayTracingSceneScratchBuffer, ERHIAccess::UAVCompute)
RDG_BUFFER_ACCESS(DynamicGeometryScratchBuffer, ERHIAccess::UAVCompute)
RDG_BUFFER_ACCESS(RayTracingSceneInstanceBuffer, ERHIAccess::SRVCompute)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FRaytracingLightDataPacked, LightDataPacked)
END_SHADER_PARAMETER_STRUCT()
bool FDeferredShadingSceneRenderer::SetupRayTracingPipelineStates(FRDGBuilder& GraphBuilder)
{
if (!IsRayTracingEnabled() || Views.Num() == 0)
{
return false;
}
bool bAnyRayTracingPassEnabled = false;
for (const FViewInfo& View : Views)
{
bAnyRayTracingPassEnabled |= AnyRayTracingPassEnabled(Scene, View);
}
if (!bAnyRayTracingPassEnabled)
{
return false;
}
TRACE_CPUPROFILER_EVENT_SCOPE(FDeferredShadingSceneRenderer::SetupRayTracingPipelineStates);
const int32 ReferenceViewIndex = 0;
FViewInfo& ReferenceView = Views[ReferenceViewIndex];
if (ReferenceView.AddRayTracingMeshBatchTaskList.Num() > 0)
{
SCOPE_CYCLE_COUNTER(STAT_WaitRayTracingAddMesh);
FTaskGraphInterface::Get().WaitUntilTasksComplete(ReferenceView.AddRayTracingMeshBatchTaskList, ENamedThreads::GetRenderThread_Local());
for (int32 TaskIndex = 0; TaskIndex < ReferenceView.AddRayTracingMeshBatchTaskList.Num(); TaskIndex++)
{
ReferenceView.VisibleRayTracingMeshCommands.Append(*ReferenceView.VisibleRayTracingMeshCommandsPerTask[TaskIndex]);
}
ReferenceView.AddRayTracingMeshBatchTaskList.Empty();
}
const bool bIsPathTracing = ActiveViewFamily->EngineShowFlags.PathTracing;
if (GRHISupportsRayTracingShaders)
{
// #dxr_todo: UE-72565: refactor ray tracing effects to not be member functions of DeferredShadingRenderer.
// Should register each effect at startup and just loop over them automatically to gather all required shaders.
TArray<FRHIRayTracingShader*> RayGenShaders;
// We typically see ~120 raygen shaders, but allow some headroom to avoid reallocation if our estimate is wrong.
RayGenShaders.Reserve(256);
if (bIsPathTracing)
{
// This view only needs the path tracing raygen shaders as all other
// passes should be disabled.
PreparePathTracing(*ActiveViewFamily, RayGenShaders);
}
else
{
// Path tracing is disabled, get all other possible raygen shaders
PrepareRayTracingDebug(*ActiveViewFamily, RayGenShaders);
// These other cases do potentially depend on the camera position since they are
// driven by FinalPostProcessSettings, which is why we need to merge them across views
if (!IsForwardShadingEnabled(ShaderPlatform))
{
for (const FViewInfo& View : Views)
{
PrepareRayTracingReflections(View, *Scene, RayGenShaders);
PrepareSingleLayerWaterRayTracingReflections(View, *Scene, RayGenShaders);
PrepareRayTracingShadows(View, *Scene, RayGenShaders);
PrepareRayTracingAmbientOcclusion(View, RayGenShaders);
PrepareRayTracingSkyLight(View, *Scene, RayGenShaders);
PrepareRayTracingGlobalIllumination(View, RayGenShaders);
PrepareRayTracingGlobalIlluminationPlugin(View, RayGenShaders);
PrepareRayTracingTranslucency(View, RayGenShaders);
if (DoesPlatformSupportLumenGI(ShaderPlatform) && Lumen::UseHardwareRayTracing(*ActiveViewFamily))
{
PrepareLumenHardwareRayTracingScreenProbeGather(View, RayGenShaders);
PrepareLumenHardwareRayTracingRadianceCache(View, RayGenShaders);
PrepareLumenHardwareRayTracingReflections(View, RayGenShaders);
PrepareLumenHardwareRayTracingVisualize(View, RayGenShaders);
}
}
}
DeduplicateRayGenerationShaders(RayGenShaders);
}
if (RayGenShaders.Num())
{
ReferenceView.RayTracingMaterialPipeline = BindRayTracingMaterialPipeline(GraphBuilder.RHICmdList, ReferenceView, RayGenShaders);
}
}
// Initialize common resources used for lighting in ray tracing effects
ReferenceView.RayTracingSubSurfaceProfileTexture = GetSubsurfaceProfileTextureWithFallback();
ReferenceView.RayTracingSubSurfaceProfileSRV = RHICreateShaderResourceView(ReferenceView.RayTracingSubSurfaceProfileTexture, 0);
uint32 NumOfSkippedRayTracingLights = 0;
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex)
{
FViewInfo& View = Views[ViewIndex];
// Send common ray tracing resources from reference view to all others.
if (ViewIndex != ReferenceViewIndex)
{
View.RayTracingSubSurfaceProfileTexture = ReferenceView.RayTracingSubSurfaceProfileTexture;
View.RayTracingSubSurfaceProfileSRV = ReferenceView.RayTracingSubSurfaceProfileSRV;
View.RayTracingMaterialPipeline = ReferenceView.RayTracingMaterialPipeline;
}
if (bIsPathTracing)
{
// Path Tracing currently uses its own code to manage lights, so doesn't need to run this.
// TODO: merge the lighting representations between ray traced and path traced cases?
}
else
{
// This light data is a function of the camera position, so must be computed per view.
View.RayTracingLightDataUniformBuffer = CreateRayTracingLightData(GraphBuilder, Scene->Lights, View, NumOfSkippedRayTracingLights);
}
}
#if !UE_BUILD_SHIPPING
if (!bIsPathTracing && NumOfSkippedRayTracingLights > 0)
{
OnGetOnScreenMessages.AddLambda([NumOfSkippedRayTracingLights](FScreenMessageWriter& ScreenMessageWriter)->void
{
FString String = FString::Printf(
TEXT("%d light(s) skipped. Active Ray Tracing light count > RAY_TRACING_LIGHT_COUNT_MAXIMUM (%d)."),
NumOfSkippedRayTracingLights,
RAY_TRACING_LIGHT_COUNT_MAXIMUM);
ScreenMessageWriter.DrawLine(FText::FromString(String), 10, FColor::Yellow);
});
}
#endif
return true;
}
bool FDeferredShadingSceneRenderer::DispatchRayTracingWorldUpdates(FRDGBuilder& GraphBuilder, FRDGBufferRef& OutDynamicGeometryScratchBuffer)
{
OutDynamicGeometryScratchBuffer = nullptr;
bool bAnyRayTracingPassEnabled = false;
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
bAnyRayTracingPassEnabled |= AnyRayTracingPassEnabled(Scene, Views[ViewIndex]);
}
if (!IsRayTracingEnabled() || !bAnyRayTracingPassEnabled || Views.Num() == 0)
{
// This needs to happen even when ray tracing is not enabled
// because importers might batch BVH creation requests that need to be resolved in any case
GRayTracingGeometryManager.ProcessBuildRequests(GraphBuilder.RHICmdList);
return false;
}
TRACE_CPUPROFILER_EVENT_SCOPE(FDeferredShadingSceneRenderer::DispatchRayTracingWorldUpdates);
// Make sure there are no pending skin cache builds and updates anymore:
// FSkeletalMeshObjectGPUSkin::UpdateDynamicData_RenderThread could have enqueued build operations which might not have
// been processed by CommitRayTracingGeometryUpdates.
// All pending builds should be done before adding them to the top level BVH.
if (FRayTracingSkinnedGeometryUpdateQueue* RayTracingSkinnedGeometryUpdateQueue = Scene->GetRayTracingSkinnedGeometryUpdateQueue())
{
RayTracingSkinnedGeometryUpdateQueue->Commit(GraphBuilder);
}
GRayTracingGeometryManager.ProcessBuildRequests(GraphBuilder.RHICmdList);
const int32 ReferenceViewIndex = 0;
FViewInfo& ReferenceView = Views[ReferenceViewIndex];
FRayTracingScene& RayTracingScene = Scene->RayTracingScene;
if (RayTracingScene.GeometriesToBuild.Num() > 0)
{
// Force update all the collected geometries (use stack allocator?)
GRayTracingGeometryManager.ForceBuildIfPending(GraphBuilder.RHICmdList, RayTracingScene.GeometriesToBuild);
}
FTaskGraphInterface::Get().WaitUntilTasksComplete(ReferenceView.RayTracingPerInstanceCullingTaskList, ENamedThreads::GetRenderThread_Local());
ReferenceView.RayTracingPerInstanceCullingTaskList.Empty();
RDG_GPU_MASK_SCOPE(GraphBuilder, FRHIGPUMask::All());
RayTracingScene.Create(GraphBuilder, Scene->GPUScene, ReferenceView.ViewMatrices);
const uint32 BLASScratchSize = Scene->GetRayTracingDynamicGeometryCollection()->ComputeScratchBufferSize();
if (BLASScratchSize > 0)
{
const uint32 ScratchAlignment = GRHIRayTracingAccelerationStructureAlignment;
FRDGBufferDesc ScratchBufferDesc;
ScratchBufferDesc.Usage = EBufferUsageFlags::RayTracingScratch | EBufferUsageFlags::StructuredBuffer;
ScratchBufferDesc.BytesPerElement = ScratchAlignment;
ScratchBufferDesc.NumElements = FMath::DivideAndRoundUp(BLASScratchSize, ScratchAlignment);
OutDynamicGeometryScratchBuffer = GraphBuilder.CreateBuffer(ScratchBufferDesc, TEXT("DynamicGeometry.BLASSharedScratchBuffer"));
}
const bool bRayTracingAsyncBuild = CVarRayTracingAsyncBuild.GetValueOnRenderThread() != 0 && GRHISupportsRayTracingAsyncBuildAccelerationStructure;
const ERDGPassFlags ComputePassFlags = bRayTracingAsyncBuild ? ERDGPassFlags::AsyncCompute : ERDGPassFlags::Compute;
{
RDG_GPU_STAT_SCOPE(GraphBuilder, RayTracingScene);
FBuildAccelerationStructurePassParams* PassParams = GraphBuilder.AllocParameters<FBuildAccelerationStructurePassParams>();
PassParams->RayTracingSceneScratchBuffer = Scene->RayTracingScene.BuildScratchBuffer;
PassParams->RayTracingSceneInstanceBuffer = Scene->RayTracingScene.InstanceBuffer;
PassParams->DynamicGeometryScratchBuffer = OutDynamicGeometryScratchBuffer;
PassParams->LightDataPacked = nullptr;
// Use ERDGPassFlags::NeverParallel so the pass never runs off the render thread and we always get the following order of execution on the CPU:
// BuildTLASInstanceBuffer, RayTracingScene, EndUpdate, ..., ReleaseRayTracingResources
GraphBuilder.AddPass(RDG_EVENT_NAME("RayTracingScene"), PassParams, ComputePassFlags | ERDGPassFlags::NeverCull | ERDGPassFlags::NeverParallel,
[this, PassParams, bRayTracingAsyncBuild](FRHIComputeCommandList& RHICmdList)
{
FRHIBuffer* DynamicGeometryScratchBuffer = PassParams->DynamicGeometryScratchBuffer ? PassParams->DynamicGeometryScratchBuffer->GetRHI() : nullptr;
Scene->GetRayTracingDynamicGeometryCollection()->DispatchUpdates(RHICmdList, DynamicGeometryScratchBuffer);
FRHIRayTracingScene* RayTracingSceneRHI = Scene->RayTracingScene.GetRHIRayTracingSceneChecked();
FRHIBuffer* AccelerationStructureBuffer = Scene->RayTracingScene.GetBufferChecked();
FRHIBuffer* ScratchBuffer = PassParams->RayTracingSceneScratchBuffer->GetRHI();
FRHIBuffer* InstanceBuffer = PassParams->RayTracingSceneInstanceBuffer->GetRHI();
FRayTracingSceneBuildParams BuildParams;
BuildParams.Scene = RayTracingSceneRHI;
BuildParams.ScratchBuffer = ScratchBuffer;
BuildParams.ScratchBufferOffset = 0;
BuildParams.InstanceBuffer = InstanceBuffer;
BuildParams.InstanceBufferOffset = 0;
RHICmdList.BindAccelerationStructureMemory(RayTracingSceneRHI, AccelerationStructureBuffer, 0);
RHICmdList.BuildAccelerationStructure(BuildParams);
if (!bRayTracingAsyncBuild)
{
// Submit potentially expensive BVH build commands to the GPU as soon as possible.
// Avoids a GPU bubble in some CPU-limited cases.
RHICmdList.SubmitCommandsHint();
}
});
}
AddPass(GraphBuilder, RDG_EVENT_NAME("EndUpdate"), [this](FRHICommandListImmediate& RHICmdList)
{
Scene->GetRayTracingDynamicGeometryCollection()->EndUpdate(RHICmdList);
});
return true;
}
static void ReleaseRaytracingResources(FRDGBuilder& GraphBuilder, TArrayView<FViewInfo> Views, FRayTracingScene &RayTracingScene)
{
AddPass(GraphBuilder, RDG_EVENT_NAME("ReleaseRayTracingResources"), [Views, &RayTracingScene](FRHICommandListImmediate& RHICmdList)
{
if (RayTracingScene.IsCreated())
{
RHICmdList.ClearRayTracingBindings(RayTracingScene.GetRHIRayTracingScene());
// If we did not end up rendering anything this frame, then release all ray tracing scene resources.
if (RayTracingScene.Instances.Num() == 0)
{
RayTracingScene.ResetAndReleaseResources();
}
}
// Release resources that were bound to the ray tracing scene to allow them to be immediately recycled.
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex)
{
FViewInfo& View = Views[ViewIndex];
// Release common lighting resources
View.RayTracingSubSurfaceProfileSRV.SafeRelease();
View.RayTracingSubSurfaceProfileTexture.SafeRelease();
}
});
}
void FDeferredShadingSceneRenderer::WaitForRayTracingScene(FRDGBuilder& GraphBuilder, FRDGBufferRef DynamicGeometryScratchBuffer)
{
bool bAnyRayTracingPassEnabled = false;
for (int32 ViewIndex = 0; ViewIndex < AllFamilyViews.Num(); ViewIndex++)
{
bAnyRayTracingPassEnabled |= AnyRayTracingPassEnabled(Scene, AllFamilyViews[ViewIndex]);
}
if (!bAnyRayTracingPassEnabled)
{
return;
}
TRACE_CPUPROFILER_EVENT_SCOPE(FDeferredShadingSceneRenderer::WaitForRayTracingScene);
RDG_GPU_MASK_SCOPE(GraphBuilder, FRHIGPUMask::All());
SetupRayTracingPipelineStates(GraphBuilder);
const int32 ReferenceViewIndex = 0;
FViewInfo& ReferenceView = AllFamilyViews[ReferenceViewIndex];
bool bAnyLumenHardwareInlineRayTracingPassEnabled = false;
for (const FViewInfo& View : AllFamilyViews)
{
bAnyLumenHardwareInlineRayTracingPassEnabled |= Lumen::AnyLumenHardwareInlineRayTracingPassEnabled(Scene, View);
}
if (bAnyLumenHardwareInlineRayTracingPassEnabled)
{
SetupLumenHardwareRayTracingHitGroupBuffer(ReferenceView);
}
const bool bIsPathTracing = ActiveViewFamily->EngineShowFlags.PathTracing;
// Scratch buffer must be referenced in this pass, as it must live until the BVH build is complete.
FBuildAccelerationStructurePassParams* PassParams = GraphBuilder.AllocParameters<FBuildAccelerationStructurePassParams>();
PassParams->RayTracingSceneScratchBuffer = Scene->RayTracingScene.BuildScratchBuffer;
PassParams->DynamicGeometryScratchBuffer = DynamicGeometryScratchBuffer;
PassParams->LightDataPacked = bIsPathTracing ? nullptr : ReferenceView.RayTracingLightDataUniformBuffer; // accessed by FRayTracingLightingMS
GraphBuilder.AddPass(RDG_EVENT_NAME("WaitForRayTracingScene"), PassParams, ERDGPassFlags::Compute | ERDGPassFlags::NeverCull,
[this, PassParams, bIsPathTracing, &ReferenceView, bAnyLumenHardwareInlineRayTracingPassEnabled](FRHICommandListImmediate& RHICmdList)
{
check(ReferenceView.RayTracingMaterialPipeline || ReferenceView.RayTracingMaterialBindings.Num() == 0);
if (ReferenceView.RayTracingMaterialPipeline && (ReferenceView.RayTracingMaterialBindings.Num() || ReferenceView.RayTracingCallableBindings.Num()))
{
// Gather bindings from all chunks and submit them all as a single batch to allow RHI to bind all shader parameters in parallel.
auto MergeAndSetBindings =
[
&RHICmdList,
RayTracingScene = ReferenceView.GetRayTracingSceneChecked(),
Pipeline = ReferenceView.RayTracingMaterialPipeline
](TConstArrayView<FRayTracingLocalShaderBindingWriter*> Bindings, ERayTracingBindingType BindingType)
{
uint32 NumTotalBindings = 0;
for (FRayTracingLocalShaderBindingWriter* BindingWriter : Bindings)
{
const FRayTracingLocalShaderBindingWriter::FChunk* Chunk = BindingWriter->GetFirstChunk();
while (Chunk)
{
NumTotalBindings += Chunk->Num;
Chunk = Chunk->Next;
}
}
if (NumTotalBindings == 0)
{
return;
}
const uint32 MergedBindingsSize = sizeof(FRayTracingLocalShaderBindings) * NumTotalBindings;
FRayTracingLocalShaderBindings* MergedBindings = (FRayTracingLocalShaderBindings*)(RHICmdList.Bypass()
? FMemStack::Get().Alloc(MergedBindingsSize, alignof(FRayTracingLocalShaderBindings))
: RHICmdList.Alloc(MergedBindingsSize, alignof(FRayTracingLocalShaderBindings)));
uint32 MergedBindingIndex = 0;
for (FRayTracingLocalShaderBindingWriter* BindingWriter : Bindings)
{
const FRayTracingLocalShaderBindingWriter::FChunk* Chunk = BindingWriter->GetFirstChunk();
while (Chunk)
{
const uint32 Num = Chunk->Num;
for (uint32_t i = 0; i < Num; ++i)
{
MergedBindings[MergedBindingIndex] = Chunk->Bindings[i];
MergedBindingIndex++;
}
Chunk = Chunk->Next;
}
}
const bool bCopyDataToInlineStorage = false; // Storage is already allocated from RHICmdList, no extra copy necessary
RHICmdList.SetRayTracingBindings(
RayTracingScene,
Pipeline,
NumTotalBindings, MergedBindings,
BindingType,
bCopyDataToInlineStorage);
};
FTaskGraphInterface::Get().WaitUntilTaskCompletes(ReferenceView.RayTracingMaterialBindingsTask, ENamedThreads::GetRenderThread_Local());
MergeAndSetBindings(ReferenceView.RayTracingMaterialBindings, ERayTracingBindingType::HitGroup);
FTaskGraphInterface::Get().WaitUntilTaskCompletes(ReferenceView.RayTracingCallableBindingsTask, ENamedThreads::GetRenderThread_Local());
MergeAndSetBindings(ReferenceView.RayTracingCallableBindings, ERayTracingBindingType::CallableShader);
// Move the ray tracing binding container ownership to the command list, so that memory will be
// released on the RHI thread timeline, after the commands that reference it are processed.
RHICmdList.EnqueueLambda([PtrsA = MoveTemp(ReferenceView.RayTracingMaterialBindings), PtrsB = MoveTemp(ReferenceView.RayTracingCallableBindings)](FRHICommandListImmediate&)
{
for (auto Ptr : PtrsA)
{
delete Ptr;
}
for (auto Ptr : PtrsB)
{
delete Ptr;
}
});
if (!bIsPathTracing)
{
SetupRayTracingLightingMissShader(RHICmdList, ReferenceView);
}
}
if (!bIsPathTracing)
{
FMemMark Mark(FMemStack::Get());
FRayTracingLocalShaderBindings* LumenHardwareRayTracingMaterialBindings = nullptr;
// When Lumen passes are running in inline-only mode we need to build bindings for HitGroupData here instead of when building the pipeline.
if (bAnyLumenHardwareInlineRayTracingPassEnabled && !GRHISupportsRayTracingShaders)
{
LumenHardwareRayTracingMaterialBindings = BuildLumenHardwareRayTracingMaterialBindings(RHICmdList, ReferenceView, ReferenceView.LumenHardwareRayTracingHitDataBuffer, true);
}
if (GRHISupportsRayTracingShaders)
{
TArray<FRHIRayTracingShader*> DeferredMaterialRayGenShaders;
if (!IsForwardShadingEnabled(ShaderPlatform))
{
for (const FViewInfo& View : AllFamilyViews)
{
PrepareRayTracingReflectionsDeferredMaterial(View, *Scene, DeferredMaterialRayGenShaders);
PrepareRayTracingDeferredReflectionsDeferredMaterial(View, *Scene, DeferredMaterialRayGenShaders);
PrepareRayTracingGlobalIlluminationDeferredMaterial(View, DeferredMaterialRayGenShaders);
if (DoesPlatformSupportLumenGI(ShaderPlatform))
{
PrepareLumenHardwareRayTracingReflectionsDeferredMaterial(View, DeferredMaterialRayGenShaders);
PrepareLumenHardwareRayTracingRadianceCacheDeferredMaterial(View, DeferredMaterialRayGenShaders);
PrepareLumenHardwareRayTracingScreenProbeGatherDeferredMaterial(View, DeferredMaterialRayGenShaders);
PrepareLumenHardwareRayTracingVisualizeDeferredMaterial(View, DeferredMaterialRayGenShaders);
}
}
}
DeduplicateRayGenerationShaders(DeferredMaterialRayGenShaders);
if (DeferredMaterialRayGenShaders.Num())
{
ReferenceView.RayTracingMaterialGatherPipeline = BindRayTracingDeferredMaterialGatherPipeline(RHICmdList, ReferenceView, DeferredMaterialRayGenShaders);
}
// Add Lumen hardware ray tracing materials
TArray<FRHIRayTracingShader*> LumenHardwareRayTracingRayGenShaders;
if (DoesPlatformSupportLumenGI(ShaderPlatform))
{
for (const FViewInfo& View : AllFamilyViews)
{
PrepareLumenHardwareRayTracingVisualizeLumenMaterial(View, LumenHardwareRayTracingRayGenShaders);
PrepareLumenHardwareRayTracingRadianceCacheLumenMaterial(View, LumenHardwareRayTracingRayGenShaders);
PrepareLumenHardwareRayTracingTranslucencyVolumeLumenMaterial(View, LumenHardwareRayTracingRayGenShaders);
PrepareLumenHardwareRayTracingRadiosityLumenMaterial(View, LumenHardwareRayTracingRayGenShaders);
PrepareLumenHardwareRayTracingReflectionsLumenMaterial(View, LumenHardwareRayTracingRayGenShaders);
PrepareLumenHardwareRayTracingScreenProbeGatherLumenMaterial(View, LumenHardwareRayTracingRayGenShaders);
PrepareLumenHardwareRayTracingDirectLightingLumenMaterial(View, LumenHardwareRayTracingRayGenShaders);
}
}
DeduplicateRayGenerationShaders(LumenHardwareRayTracingRayGenShaders);
if (LumenHardwareRayTracingRayGenShaders.Num())
{
ReferenceView.LumenHardwareRayTracingMaterialPipeline = BindLumenHardwareRayTracingMaterialPipeline(RHICmdList, LumenHardwareRayTracingMaterialBindings, ReferenceView, LumenHardwareRayTracingRayGenShaders, ReferenceView.LumenHardwareRayTracingHitDataBuffer);
}
}
}
// Send ray tracing resources from reference view to all others.
for (int32 ViewIndex = 1; ViewIndex < AllFamilyViews.Num(); ++ViewIndex)
{
FViewInfo& View = AllFamilyViews[ViewIndex];
View.RayTracingMaterialGatherPipeline = ReferenceView.RayTracingMaterialGatherPipeline;
View.LumenHardwareRayTracingMaterialPipeline = ReferenceView.LumenHardwareRayTracingMaterialPipeline;
}
if (RayTracingDynamicGeometryUpdateEndTransition)
{
RHICmdList.EndTransition(RayTracingDynamicGeometryUpdateEndTransition);
RayTracingDynamicGeometryUpdateEndTransition = nullptr;
}
FRHIRayTracingScene* RayTracingScene = ReferenceView.GetRayTracingSceneChecked();
RHICmdList.Transition(FRHITransitionInfo(RayTracingScene, ERHIAccess::BVHWrite, ERHIAccess::BVHRead));
if (ReferenceView.LumenHardwareRayTracingHitDataBuffer)
{
RHICmdList.Transition(FRHITransitionInfo(ReferenceView.LumenHardwareRayTracingHitDataBuffer, ERHIAccess::None, ERHIAccess::SRVMask));
}
});
}
#endif // RHI_RAYTRACING
static TAutoConsoleVariable<float> CVarStallInitViews(
TEXT("CriticalPathStall.AfterInitViews"),
0.0f,
TEXT("Sleep for the given time after InitViews. Time is given in ms. This is a debug option used for critical path analysis and forcing a change in the critical path."));
void FDeferredShadingSceneRenderer::CommitFinalPipelineState()
{
int32 FamilyIndex = GetViewFamilyIndexInScene(*ActiveViewFamily);
TPipelineState<FFamilyPipelineState>& FamilyPipelineState = FamilyPipelineStates[FamilyIndex];
// Family pipeline state
{
FamilyPipelineState.Set(&FFamilyPipelineState::bNanite, UseNanite(ShaderPlatform)); // TODO: Should this respect ActiveViewFamily->EngineShowFlags.NaniteMeshes?
static const auto ICVarHZBOcc = IConsoleManager::Get().FindConsoleVariable(TEXT("r.HZBOcclusion"));
FamilyPipelineState.Set(&FFamilyPipelineState::bHZBOcclusion, ICVarHZBOcc->GetInt() != 0);
}
CommitIndirectLightingState();
// Views pipeline states
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
TPipelineState<FPerViewPipelineState>& ViewPipelineState = GetViewPipelineStateWritable(View);
// Commit HZB state
{
const bool bHasSSGI = ViewPipelineState[&FPerViewPipelineState::DiffuseIndirectMethod] == EDiffuseIndirectMethod::SSGI;
const bool bUseLumen = ViewPipelineState[&FPerViewPipelineState::DiffuseIndirectMethod] == EDiffuseIndirectMethod::Lumen
|| ViewPipelineState[&FPerViewPipelineState::ReflectionsMethod] == EReflectionsMethod::Lumen;
// Requires FurthestHZB
ViewPipelineState.Set(&FPerViewPipelineState::bFurthestHZB,
FamilyPipelineState[&FFamilyPipelineState::bHZBOcclusion] ||
FamilyPipelineState[&FFamilyPipelineState::bNanite] ||
ViewPipelineState[&FPerViewPipelineState::bUseLumenProbeHierarchy] ||
ViewPipelineState[&FPerViewPipelineState::AmbientOcclusionMethod] == EAmbientOcclusionMethod::SSAO ||
ViewPipelineState[&FPerViewPipelineState::ReflectionsMethod] == EReflectionsMethod::SSR ||
bHasSSGI || bUseLumen);
ViewPipelineState.Set(&FPerViewPipelineState::bClosestHZB,
bHasSSGI || bUseLumen);
}
}
// Commit all the pipeline states.
{
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
GetViewPipelineStateWritable(View).Commit();
}
FamilyPipelineState.Commit();
}
}
bool FDeferredShadingSceneRenderer::IsNaniteEnabled() const
{
return UseNanite(ShaderPlatform) && ActiveViewFamily->EngineShowFlags.NaniteMeshes && Nanite::GStreamingManager.HasResourceEntries();
}
#if WITH_MGPU
BEGIN_SHADER_PARAMETER_STRUCT(FGBufferTemporalTextureParams, )
RDG_TEXTURE_ACCESS(DepthResolve, ERHIAccess::CopySrc)
RDG_TEXTURE_ACCESS(GBufferA, ERHIAccess::CopySrc)
END_SHADER_PARAMETER_STRUCT()
#endif
void FDeferredShadingSceneRenderer::Render(FRDGBuilder& GraphBuilder)
{
const bool bNaniteEnabled = IsNaniteEnabled();
GPU_MESSAGE_SCOPE(GraphBuilder);
#if RHI_RAYTRACING
{
ERayTracingMeshCommandsMode CurrentMode = ActiveViewFamily->EngineShowFlags.PathTracing ? ERayTracingMeshCommandsMode::PATH_TRACING : ERayTracingMeshCommandsMode::RAY_TRACING;
bool bNaniteCoarseMeshStreamingModeChanged = false;
#if WITH_EDITOR
bNaniteCoarseMeshStreamingModeChanged = Nanite::FCoarseMeshStreamingManager::CheckStreamingMode();
#endif // WITH_EDITOR
if (CurrentMode != Scene->CachedRayTracingMeshCommandsMode || bNaniteCoarseMeshStreamingModeChanged)
{
// If we change to or from a path traced render, we need to refresh the cached ray tracing mesh commands
// because they contain data about the currently bound shader. This operation is a bit expensive but
// only happens once as we transition between modes which should be rare.
Scene->CachedRayTracingMeshCommandsMode = CurrentMode;
Scene->RefreshRayTracingMeshCommandCache();
}
Scene->UpdateRayTracedLights();
}
#endif
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
FViewInfo& View = Views[ViewIndex];
RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask);
ShaderPrint::BeginView(GraphBuilder, View);
ShadingEnergyConservation::Init(GraphBuilder, View);
}
ON_SCOPE_EXIT
{
for (FViewInfo& View : Views)
{
ShaderPrint::EndView(View);
}
};
Scene->UpdateAllPrimitiveSceneInfos(GraphBuilder, true);
FGPUSceneScopeBeginEndHelper GPUSceneScopeBeginEndHelper(Scene->GPUScene, GPUSceneDynamicContext, Scene);
bool bUpdateNaniteStreaming = false;
bool bVisualizeNanite = false;
if (bNaniteEnabled)
{
Nanite::GGlobalResources.Update(GraphBuilder);
// Only update Nanite streaming residency for the first view when multiple view rendering (nDisplay) is enabled.
// Streaming requests are still accumulated from the remaining views.
bUpdateNaniteStreaming = !ActiveViewFamily->bIsMultipleViewFamily || ActiveViewFamily->bIsFirstViewInMultipleViewFamily;
if(bUpdateNaniteStreaming)
{
Nanite::GStreamingManager.BeginAsyncUpdate(GraphBuilder);
}
FNaniteVisualizationData& NaniteVisualization = GetNaniteVisualizationData();
if (Views.Num() > 0)
{
const FName& NaniteViewMode = Views[0].CurrentNaniteVisualizationMode;
if (NaniteVisualization.Update(NaniteViewMode))
{
// When activating the view modes from the command line, automatically enable the VisualizeNanite show flag for convenience.
ActiveViewFamily->EngineShowFlags.SetVisualizeNanite(true);
}
bVisualizeNanite = NaniteVisualization.IsActive() && ActiveViewFamily->EngineShowFlags.VisualizeNanite;
}
}
CSV_SCOPED_TIMING_STAT_EXCLUSIVE(RenderOther);
// Setups the final FViewInfo::ViewRect.
PrepareViewRectsForRendering(GraphBuilder.RHICmdList);
const bool bPathTracedAtmosphere = ActiveViewFamily->EngineShowFlags.PathTracing && Views.Num() > 0 && Views[0].FinalPostProcessSettings.PathTracingEnableReferenceAtmosphere;
if (ShouldRenderSkyAtmosphere(Scene, ActiveViewFamily->EngineShowFlags) && !bPathTracedAtmosphere)
{
for (int32 LightIndex = 0; LightIndex < NUM_ATMOSPHERE_LIGHTS; ++LightIndex)
{
if (Scene->AtmosphereLights[LightIndex])
{
PrepareSunLightProxy(*Scene->GetSkyAtmosphereSceneInfo(),LightIndex, *Scene->AtmosphereLights[LightIndex]);
}
}
}
else
{
Scene->ResetAtmosphereLightsProperties();
}
SCOPED_NAMED_EVENT(FDeferredShadingSceneRenderer_Render, FColor::Emerald);
#if WITH_MGPU
const FRHIGPUMask RenderTargetGPUMask = ComputeGPUMasks(GraphBuilder.RHICmdList);
#endif // WITH_MGPU
// By default, limit our GPU usage to only GPUs specified in the view masks.
RDG_GPU_MASK_SCOPE(GraphBuilder, AllViewsGPUMask);
WaitOcclusionTests(GraphBuilder.RHICmdList);
if (!ActiveViewFamily->EngineShowFlags.Rendering)
{
return;
}
RDG_EVENT_SCOPE(GraphBuilder, "Scene");
RDG_GPU_STAT_SCOPE_VERBOSE(GraphBuilder, Unaccounted, *ActiveViewFamily->ProfileDescription);
{
SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_Render_Init);
RDG_RHI_GPU_STAT_SCOPE(GraphBuilder, AllocateRendertargets);
// Initialize global system textures (pass-through if already initialized).
GSystemTextures.InitializeTextures(GraphBuilder.RHICmdList, FeatureLevel);
// Force the subsurface profile texture to be updated.
UpdateSubsurfaceProfileTexture(GraphBuilder, ShaderPlatform);
// Force the rect light texture to be updated.
RectLightAtlas::UpdateRectLightAtlasTexture(GraphBuilder, FeatureLevel);
}
FSceneTexturesConfig::InitializeViewFamily(*ActiveViewFamily);
FSceneTexturesConfig& SceneTexturesConfig = GetActiveSceneTexturesConfig();
FSceneTexturesConfig::Set(SceneTexturesConfig);
const FRDGSystemTextures& SystemTextures = FRDGSystemTextures::Create(GraphBuilder);
const bool bHasRayTracedOverlay = HasRayTracedOverlay(*ActiveViewFamily);
const bool bAllowStaticLighting = !bHasRayTracedOverlay && IsStaticLightingAllowed();
const bool bUseVirtualTexturing = UseVirtualTexturing(FeatureLevel);
if (bUseVirtualTexturing)
{
RDG_GPU_STAT_SCOPE(GraphBuilder, VirtualTextureUpdate);
// AllocateResources needs to be called before RHIBeginScene
FVirtualTextureSystem::Get().AllocateResources(GraphBuilder, FeatureLevel);
FVirtualTextureSystem::Get().CallPendingCallbacks();
VirtualTextureFeedbackBegin(GraphBuilder, Views, SceneTexturesConfig.Extent);
}
// Important that this uses consistent logic throughout the frame, so evaluate once and pass in the flag from here
// NOTE: Must be done after system texture initialization
// TODO: This doesn't take into account the potential for split screen views with separate shadow caches
ActiveViewFamily->VirtualShadowMapArray.Initialize(GraphBuilder, Scene->GetVirtualShadowMapCache(Views[0]), UseVirtualShadowMaps(ShaderPlatform, FeatureLevel));
// if DDM_AllOpaqueNoVelocity was used, then velocity should have already been rendered as well
const bool bIsEarlyDepthComplete = (DepthPass.EarlyZPassMode == DDM_AllOpaque || DepthPass.EarlyZPassMode == DDM_AllOpaqueNoVelocity);
// Use read-only depth in the base pass if we have a full depth prepass.
const bool bAllowReadOnlyDepthBasePass = bIsEarlyDepthComplete
&& !ActiveViewFamily->EngineShowFlags.ShaderComplexity
&& !ActiveViewFamily->UseDebugViewPS()
&& !ActiveViewFamily->EngineShowFlags.Wireframe
&& !ActiveViewFamily->EngineShowFlags.LightMapDensity;
const FExclusiveDepthStencil::Type BasePassDepthStencilAccess =
bAllowReadOnlyDepthBasePass
? FExclusiveDepthStencil::DepthRead_StencilWrite
: FExclusiveDepthStencil::DepthWrite_StencilWrite;
FILCUpdatePrimTaskData ILCTaskData;
// Find the visible primitives.
GraphBuilder.RHICmdList.ImmediateFlush(EImmediateFlushType::DispatchToRHIThread);
FInstanceCullingManager& InstanceCullingManager = *GraphBuilder.AllocObject<FInstanceCullingManager>(Scene->GPUScene.IsEnabled(), GraphBuilder);
{
RDG_GPU_STAT_SCOPE(GraphBuilder, VisibilityCommands);
InitViews(GraphBuilder, SceneTexturesConfig, BasePassDepthStencilAccess, ILCTaskData, InstanceCullingManager);
}
// Compute & commit the final state of the entire dependency topology of the renderer.
CommitFinalPipelineState();
#if !UE_BUILD_SHIPPING
if (CVarStallInitViews.GetValueOnRenderThread() > 0.0f)
{
SCOPE_CYCLE_COUNTER(STAT_InitViews_Intentional_Stall);
FPlatformProcess::Sleep(CVarStallInitViews.GetValueOnRenderThread() / 1000.0f);
}
#endif
extern TSet<IPersistentViewUniformBufferExtension*> PersistentViewUniformBufferExtensions;
for (IPersistentViewUniformBufferExtension* Extension : PersistentViewUniformBufferExtensions)
{
Extension->BeginFrame();
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
// Must happen before RHI thread flush so any tasks we dispatch here can land in the idle gap during the flush
Extension->PrepareView(&Views[ViewIndex]);
}
}
#if RHI_RAYTRACING
// Gather mesh instances, shaders, resources, parameters, etc. and build ray tracing acceleration structure
FRayTracingScene& RayTracingScene = Scene->RayTracingScene;
RayTracingScene.Reset(); // Resets the internal arrays, but does not release any resources.
const int32 ReferenceViewIndex = 0;
FViewInfo& ReferenceView = Views[ReferenceViewIndex];
// Prepare the scene for rendering this frame.
GatherRayTracingWorldInstancesForView(GraphBuilder, ReferenceView, RayTracingScene);
if (ActiveViewFamily->EngineShowFlags.PathTracing)
{
ReferenceView.RayTracingDecalUniformBuffer = CreateRayTracingDecalData(GraphBuilder, *Scene, ReferenceView, RayTracingScene.NumCallableShaderSlots);
RayTracingScene.NumCallableShaderSlots += Scene->Decals.Num();
}
#endif // RHI_RAYTRACING
// Dynamic vertex and index buffers need to be committed before rendering.
{
SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_FGlobalDynamicVertexBuffer_Commit);
DynamicIndexBufferForInitViews.Commit();
DynamicVertexBufferForInitViews.Commit();
DynamicReadBufferForInitViews.Commit();
}
// Notify the FX system that the scene is about to be rendered.
if (FXSystem && Views.IsValidIndex(0))
{
SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_FXSystem_PreRender);
GraphBuilder.SetCommandListStat(GET_STATID(STAT_CLM_FXPreRender));
FXSystem->PreRender(GraphBuilder, Views, true /*bAllowGPUParticleUpdate*/);
if (FGPUSortManager* GPUSortManager = FXSystem->GetGPUSortManager())
{
GPUSortManager->OnPreRender(GraphBuilder);
}
}
{
RDG_GPU_STAT_SCOPE(GraphBuilder, GPUSceneUpdate);
if (IsFirstViewFamily())
{
GraphBuilder.SetFlushResourcesRHI();
}
FRDGExternalAccessQueue ExternalAccessQueue;
Scene->GPUScene.Update(GraphBuilder, *Scene, ExternalAccessQueue);
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
FViewInfo& View = Views[ViewIndex];
RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask);
Scene->GPUScene.UploadDynamicPrimitiveShaderDataForView(GraphBuilder, Scene, View, ExternalAccessQueue);
Scene->GPUScene.DebugRender(GraphBuilder, *Scene, View);
}
ExternalAccessQueue.Submit(GraphBuilder);
InstanceCullingManager.BeginDeferredCulling(GraphBuilder, Scene->GPUScene);
if (Views.Num() > 0)
{
FViewInfo& View = Views[0];
Scene->UpdatePhysicsField(GraphBuilder, View);
}
}
FSceneTextures::InitializeViewFamily(GraphBuilder, *ActiveViewFamily);
FSceneTextures& SceneTextures = GetActiveSceneTextures();
// Note, should happen after the GPU-Scene update to ensure rendering to runtime virtual textures is using the correctly updated scene
if (bUseVirtualTexturing)
{
RDG_GPU_STAT_SCOPE(GraphBuilder, VirtualTextureUpdate);
FVirtualTextureSystem::Get().Update(GraphBuilder, FeatureLevel, Scene);
}
const bool bUseGBuffer = IsUsingGBuffers(ShaderPlatform);
const bool bRenderDeferredLighting = ActiveViewFamily->EngineShowFlags.Lighting
&& FeatureLevel >= ERHIFeatureLevel::SM5
&& ActiveViewFamily->EngineShowFlags.DeferredLighting
&& bUseGBuffer
&& !bHasRayTracedOverlay;
bool bComputeLightGrid = false;
bool bAnyLumenEnabled = false;
// Simple forward shading doesn't support local lights. No need to compute light grid
if (!IsSimpleForwardShadingEnabled(ShaderPlatform))
{
if (bUseGBuffer)
{
bComputeLightGrid = bRenderDeferredLighting;
}
else
{
bComputeLightGrid = ActiveViewFamily->EngineShowFlags.Lighting;
}
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
FViewInfo& View = Views[ViewIndex];
bAnyLumenEnabled = bAnyLumenEnabled
|| GetViewPipelineState(View).DiffuseIndirectMethod == EDiffuseIndirectMethod::Lumen
|| GetViewPipelineState(View).ReflectionsMethod == EReflectionsMethod::Lumen;
}
bComputeLightGrid |= (
ShouldRenderVolumetricFog() ||
VolumetricCloudWantsToSampleLocalLights(Scene, ActiveViewFamily->EngineShowFlags) ||
ActiveViewFamily->ViewMode != VMI_Lit ||
bAnyLumenEnabled ||
ActiveViewFamily->VirtualShadowMapArray.IsEnabled());
}
// force using occ queries for wireframe if rendering is parented or frozen in the first view
check(Views.Num());
#if (UE_BUILD_SHIPPING || UE_BUILD_TEST)
const bool bIsViewFrozen = false;
const bool bHasViewParent = false;
#else
const bool bIsViewFrozen = Views[0].State && ((FSceneViewState*)Views[0].State)->bIsFrozen;
const bool bHasViewParent = Views[0].State && ((FSceneViewState*)Views[0].State)->HasViewParent();
#endif
const bool bIsOcclusionTesting = DoOcclusionQueries() && !ActiveViewFamily->EngineShowFlags.DisableOcclusionQueries
&& (!ActiveViewFamily->EngineShowFlags.Wireframe || bIsViewFrozen || bHasViewParent);
const bool bNeedsPrePass = ShouldRenderPrePass();
GEngine->GetPreRenderDelegateEx().Broadcast(GraphBuilder);
// Strata initialisation is always run even when not enabled.
const bool bStrataEnabled = Strata::IsStrataEnabled();
Strata::InitialiseStrataFrameSceneData(GraphBuilder, *this);
if (DepthPass.IsComputeStencilDitherEnabled())
{
AddDitheredStencilFillPass(GraphBuilder, Views, SceneTextures.Depth.Target, DepthPass);
}
FHairStrandsBookmarkParameters& HairStrandsBookmarkParameters = *GraphBuilder.AllocObject<FHairStrandsBookmarkParameters>();
if (IsHairStrandsEnabled(EHairStrandsShaderType::All, Scene->GetShaderPlatform()))
{
HairStrandsBookmarkParameters = CreateHairStrandsBookmarkParameters(Scene, Views[0]);
RunHairStrandsBookmark(GraphBuilder, EHairStrandsBookmark::ProcessTasks, HairStrandsBookmarkParameters);
// Interpolation needs to happen after the skin cache run as there is a dependency
// on the skin cache output.
const bool bRunHairStrands = HairStrandsBookmarkParameters.HasInstances() && (Views.Num() > 0);
if (bRunHairStrands)
{
if (IsHairStrandsEnabled(EHairStrandsShaderType::Strands, Scene->GetShaderPlatform()))
{
RunHairStrandsBookmark(GraphBuilder, EHairStrandsBookmark::ProcessGatherCluster, HairStrandsBookmarkParameters);
FHairCullingParams CullingParams;
CullingParams.bCullingProcessSkipped = false;
ComputeHairStrandsClustersCulling(GraphBuilder, *HairStrandsBookmarkParameters.ShaderMap, Views, CullingParams, HairStrandsBookmarkParameters.HairClusterData);
}
RunHairStrandsBookmark(GraphBuilder, EHairStrandsBookmark::ProcessStrandsInterpolation, HairStrandsBookmarkParameters);
}
else
{
for (FViewInfo& View : Views)
{
View.HairStrandsViewData.UniformBuffer = HairStrands::CreateDefaultHairStrandsViewUniformBuffer(GraphBuilder, View);
}
}
}
if (bNaniteEnabled)
{
Nanite::ListStatFilters(this);
// Must happen before any Nanite rendering in the frame
if (bUpdateNaniteStreaming)
{
Nanite::GStreamingManager.EndAsyncUpdate(GraphBuilder);
}
}
{
RDG_RHI_GPU_STAT_SCOPE(GraphBuilder, GPUSceneUpdate);
PrepareDistanceFieldScene(GraphBuilder, false);
}
const bool bShouldRenderVelocities = ShouldRenderVelocities();
const bool bBasePassCanOutputVelocity = FVelocityRendering::BasePassCanOutputVelocity(FeatureLevel);
const bool bHairStrandsEnable = HairStrandsBookmarkParameters.HasInstances() && Views.Num() > 0 && IsHairStrandsEnabled(EHairStrandsShaderType::Strands, GetViewFamily(Views).GetShaderPlatform());
{
GraphBuilder.SetCommandListStat(GET_STATID(STAT_CLM_PrePass));
// Both compute approaches run earlier, so skip clearing stencil here, just load existing.
const ERenderTargetLoadAction StencilLoadAction = DepthPass.IsComputeStencilDitherEnabled()
? ERenderTargetLoadAction::ELoad
: ERenderTargetLoadAction::EClear;
const ERenderTargetLoadAction DepthLoadAction = ERenderTargetLoadAction::EClear;
AddClearDepthStencilPass(GraphBuilder, SceneTextures.Depth.Target, DepthLoadAction, StencilLoadAction);
// Draw the scene pre-pass / early z pass, populating the scene depth buffer and HiZ
if (bNeedsPrePass)
{
RenderPrePass(GraphBuilder, SceneTextures.Depth.Target, InstanceCullingManager);
}
else
{
// We didn't do the prepass, but we still want the HMD mask if there is one
RenderPrePassHMD(GraphBuilder, SceneTextures.Depth.Target);
}
GraphBuilder.SetCommandListStat(GET_STATID(STAT_CLM_AfterPrePass));
// special pass for DDM_AllOpaqueNoVelocity, which uses the velocity pass to finish the early depth pass write
if (bShouldRenderVelocities && Scene->EarlyZPassMode == DDM_AllOpaqueNoVelocity)
{
// Render the velocities of movable objects
GraphBuilder.SetCommandListStat(GET_STATID(STAT_CLM_Velocity));
RenderVelocities(GraphBuilder, SceneTextures, EVelocityPass::Opaque, bHairStrandsEnable);
GraphBuilder.SetCommandListStat(GET_STATID(STAT_CLM_AfterVelocity));
}
}
FLumenSceneFrameTemporaries LumenFrameTemporaries;
{
{
RDG_RHI_GPU_STAT_SCOPE(GraphBuilder, VisibilityCommands);
InitViewsAfterPrepass(GraphBuilder, LumenFrameTemporaries, ILCTaskData, InstanceCullingManager);
}
{
SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_FGlobalDynamicVertexBuffer_Commit);
DynamicVertexBufferForInitShadows.Commit();
DynamicIndexBufferForInitShadows.Commit();
DynamicReadBufferForInitShadows.Commit();
}
}
TArray<Nanite::FRasterResults, TInlineAllocator<2>> NaniteRasterResults;
if (bNaniteEnabled && Views.Num() > 0)
{
LLM_SCOPE_BYTAG(Nanite);
TRACE_CPUPROFILER_EVENT_SCOPE(InitNaniteRaster);
NaniteRasterResults.AddDefaulted(Views.Num());
RDG_GPU_STAT_SCOPE(GraphBuilder, NaniteRaster);
const FIntPoint RasterTextureSize = SceneTextures.Depth.Target->Desc.Extent;
// Primary raster view
{
Nanite::FSharedContext SharedContext{};
SharedContext.FeatureLevel = Scene->GetFeatureLevel();
SharedContext.ShaderMap = GetGlobalShaderMap(SharedContext.FeatureLevel);
SharedContext.Pipeline = Nanite::EPipeline::Primary;
Nanite::FRasterState RasterState;
Nanite::FRasterContext RasterContext = Nanite::InitRasterContext(GraphBuilder, SharedContext, RasterTextureSize, ActiveViewFamily->EngineShowFlags.VisualizeNanite);
Nanite::FCullingContext::FConfiguration CullingConfig = { 0 };
CullingConfig.bTwoPassOcclusion = true;
CullingConfig.bUpdateStreaming = true;
CullingConfig.bPrimaryContext = true;
CullingConfig.bForceHWRaster = RasterContext.RasterScheduling == Nanite::ERasterScheduling::HardwareOnly;
CullingConfig.bProgrammableRaster = GNaniteProgrammableRasterPrimary != 0;
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
RDG_EVENT_SCOPE_CONDITIONAL(GraphBuilder, Views.Num() > 1, "View%d", ViewIndex);
const FViewInfo& View = Views[ViewIndex];
CullingConfig.SetViewFlags(View);
Nanite::FCullingContext CullingContext = Nanite::InitCullingContext(
GraphBuilder,
SharedContext,
*Scene,
!bIsEarlyDepthComplete ? View.PrevViewInfo.NaniteHZB : View.PrevViewInfo.HZB,
View.ViewRect,
CullingConfig
);
static FString EmptyFilterName = TEXT(""); // Empty filter represents primary view.
const bool bExtractStats = Nanite::IsStatFilterActive(EmptyFilterName);
float LODScaleFactor = 1.0f;
if (View.PrimaryScreenPercentageMethod == EPrimaryScreenPercentageMethod::TemporalUpscale &&
CVarNaniteViewMeshLODBiasEnable.GetValueOnRenderThread() != 0)
{
float TemporalUpscaleFactor = float(View.GetSecondaryViewRectSize().X) / float(View.ViewRect.Width());
LODScaleFactor = TemporalUpscaleFactor * FMath::Exp2(-CVarNaniteViewMeshLODBiasOffset.GetValueOnRenderThread());
LODScaleFactor = FMath::Min(LODScaleFactor, FMath::Exp2(-CVarNaniteViewMeshLODBiasMin.GetValueOnRenderThread()));
}
FIntRect HZBTestRect(0, 0, View.PrevViewInfo.ViewRect.Width(), View.PrevViewInfo.ViewRect.Height());
Nanite::FPackedView PackedView = Nanite::CreatePackedViewFromViewInfo(
View,
RasterTextureSize,
NANITE_VIEW_FLAG_HZBTEST | NANITE_VIEW_FLAG_NEAR_CLIP,
/* StreamingPriorityCategory = */ 3,
/* MinBoundsRadius = */ 0.0f,
LODScaleFactor,
/* viewport rect in HZB space. HZB is built per view and is always 0,0-based */
&HZBTestRect
);
Nanite::CullRasterize(
GraphBuilder,
Scene->NaniteRasterPipelines[ENaniteMeshPass::BasePass],
*Scene,
View,
{ PackedView },
SharedContext,
CullingContext,
RasterContext,
RasterState,
/*OptionalInstanceDraws*/ nullptr,
bExtractStats
);
Nanite::FRasterResults& RasterResults = NaniteRasterResults[ViewIndex];
if (bNeedsPrePass)
{
// Emit velocity with depth if not writing it in base pass.
FRDGTexture* VelocityBuffer = !IsUsingBasePassVelocity(ShaderPlatform) ? SceneTextures.Velocity : nullptr;
const bool bEmitStencilMask = NANITE_MATERIAL_STENCIL != 0;
Nanite::EmitDepthTargets(
GraphBuilder,
*Scene,
Views[ViewIndex],
CullingContext.PageConstants,
CullingContext.VisibleClustersSWHW,
CullingContext.ViewsBuffer,
SceneTextures.Depth.Target,
RasterContext.VisBuffer64,
VelocityBuffer,
RasterResults.MaterialDepth,
RasterResults.MaterialResolve,
bNeedsPrePass,
bEmitStencilMask
);
}
if (!bIsEarlyDepthComplete && CullingConfig.bTwoPassOcclusion && View.ViewState)
{
// Won't have a complete SceneDepth for post pass so can't use complete HZB for main pass or it will poke holes in the post pass HZB killing occlusion culling.
RDG_EVENT_SCOPE(GraphBuilder, "Nanite::BuildHZB");
FRDGTextureRef SceneDepth = SystemTextures.Black;
FRDGTextureRef GraphHZB = nullptr;
const FIntRect PrimaryViewRect = View.GetPrimaryView()->ViewRect;
BuildHZBFurthest(
GraphBuilder,
SceneDepth,
RasterContext.VisBuffer64,
PrimaryViewRect,
FeatureLevel,
ShaderPlatform,
TEXT("Nanite.HZB"),
/* OutFurthestHZBTexture = */ &GraphHZB );
GraphBuilder.QueueTextureExtraction( GraphHZB, &View.ViewState->PrevFrameViewInfo.NaniteHZB );
}
Nanite::ExtractResults(GraphBuilder, CullingContext, RasterContext, RasterResults);
if (GNaniteShowStats != 0 && IStereoRendering::IsAPrimaryView(View))
{
Nanite::PrintStats(GraphBuilder, View);
}
}
}
}
SceneTextures.SetupMode = ESceneTextureSetupMode::SceneDepth;
SceneTextures.UniformBuffer = CreateSceneTextureUniformBuffer(GraphBuilder, &SceneTextures, FeatureLevel, SceneTextures.SetupMode);
AddResolveSceneDepthPass(GraphBuilder, Views, SceneTextures.Depth);
// NOTE: The ordering of the lights is used to select sub-sets for different purposes, e.g., those that support clustered deferred.
FSortedLightSetSceneInfo& SortedLightSet = *GraphBuilder.AllocObject<FSortedLightSetSceneInfo>();
{
RDG_GPU_STAT_SCOPE(GraphBuilder, SortLights);
GatherLightsAndComputeLightGrid(GraphBuilder, bComputeLightGrid, SortedLightSet);
}
CSV_CUSTOM_STAT(LightCount, All, float(SortedLightSet.SortedLights.Num()), ECsvCustomStatOp::Set);
CSV_CUSTOM_STAT(LightCount, Batched, float(SortedLightSet.UnbatchedLightStart), ECsvCustomStatOp::Set);
CSV_CUSTOM_STAT(LightCount, Unbatched, float(SortedLightSet.SortedLights.Num()) - float(SortedLightSet.UnbatchedLightStart), ECsvCustomStatOp::Set);
FCompositionLighting CompositionLighting(Views, SceneTextures, [this] (int32 ViewIndex)
{
return GetViewPipelineState(Views[ViewIndex]).AmbientOcclusionMethod == EAmbientOcclusionMethod::SSAO;
});
const auto RenderOcclusionLambda = [&]()
{
RenderOcclusion(GraphBuilder, SceneTextures, bIsOcclusionTesting);
CompositionLighting.ProcessAfterOcclusion(GraphBuilder);
};
// Early occlusion queries
const bool bOcclusionBeforeBasePass = ((DepthPass.EarlyZPassMode == EDepthDrawingMode::DDM_AllOccluders) || bIsEarlyDepthComplete);
#if RHI_RAYTRACING
bool bRayTracingSceneReady = false;
#endif
if (bOcclusionBeforeBasePass)
{
RenderOcclusionLambda();
}
// End early occlusion queries
BeginAsyncDistanceFieldShadowProjections(GraphBuilder, SceneTextures);
const bool bShouldRenderSkyAtmosphere = ShouldRenderSkyAtmosphere(Scene, ActiveViewFamily->EngineShowFlags);
const bool bShouldRenderVolumetricCloudBase = ShouldRenderVolumetricCloud(Scene, ActiveViewFamily->EngineShowFlags);
const bool bShouldRenderVolumetricCloud = bShouldRenderVolumetricCloudBase && !ActiveViewFamily->EngineShowFlags.VisualizeVolumetricCloudConservativeDensity;
const bool bShouldVisualizeVolumetricCloud = bShouldRenderVolumetricCloudBase && !!ActiveViewFamily->EngineShowFlags.VisualizeVolumetricCloudConservativeDensity;
bool bAsyncComputeVolumetricCloud = IsVolumetricRenderTargetEnabled() && IsVolumetricRenderTargetAsyncCompute();
bool bHasHalfResCheckerboardMinMaxDepth = false;
bool bVolumetricRenderTargetRequired = bShouldRenderVolumetricCloud && !bHasRayTracedOverlay;
if (bShouldRenderVolumetricCloudBase)
{
InitVolumetricRenderTargetForViews(GraphBuilder, Views);
}
InitVolumetricCloudsForViews(GraphBuilder, bShouldRenderVolumetricCloudBase, InstanceCullingManager);
// Generate sky LUTs
// TODO: Valid shadow maps (for volumetric light shafts) have not yet been generated at this point in the frame. Need to resolve dependency ordering!
// This also must happen before the BasePass for Sky material to be able to sample valid LUTs.
if (bShouldRenderSkyAtmosphere)
{
// Generate the Sky/Atmosphere look up tables
RenderSkyAtmosphereLookUpTables(GraphBuilder);
}
// Capture the SkyLight using the SkyAtmosphere and VolumetricCloud component if available.
const bool bRealTimeSkyCaptureEnabled = Scene->SkyLight && Scene->SkyLight->bRealTimeCaptureEnabled && Views.Num() > 0 && ActiveViewFamily->EngineShowFlags.SkyLighting;
if (bRealTimeSkyCaptureEnabled)
{
FViewInfo& MainView = Views[0];
Scene->AllocateAndCaptureFrameSkyEnvMap(GraphBuilder, *this, MainView, bShouldRenderSkyAtmosphere, bShouldRenderVolumetricCloud, InstanceCullingManager);
}
const ECustomDepthPassLocation CustomDepthPassLocation = GetCustomDepthPassLocation(ShaderPlatform);
if (CustomDepthPassLocation == ECustomDepthPassLocation::BeforeBasePass)
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_CustomDepthPass_BeforeBasePass);
if (RenderCustomDepthPass(GraphBuilder, SceneTextures.CustomDepth, SceneTextures.GetSceneTextureShaderParameters(FeatureLevel)))
{
SceneTextures.SetupMode |= ESceneTextureSetupMode::CustomDepth;
SceneTextures.UniformBuffer = CreateSceneTextureUniformBuffer(GraphBuilder, &SceneTextures, FeatureLevel, SceneTextures.SetupMode);
}
}
UpdateLumenScene(GraphBuilder, LumenFrameTemporaries);
FRDGTextureRef HalfResolutionDepthCheckerboardMinMaxTexture = nullptr;
// Kick off async compute cloud eraly if all depth has been written in the prepass
if (bShouldRenderVolumetricCloud && bAsyncComputeVolumetricCloud && DepthPass.EarlyZPassMode == DDM_AllOpaque && !bHasRayTracedOverlay)
{
HalfResolutionDepthCheckerboardMinMaxTexture = CreateHalfResolutionDepthCheckerboardMinMax(GraphBuilder, Views, SceneTextures.Depth.Resolve);
bHasHalfResCheckerboardMinMaxDepth = true;
bool bSkipVolumetricRenderTarget = false;
bool bSkipPerPixelTracing = true;
bAsyncComputeVolumetricCloud = RenderVolumetricCloud(GraphBuilder, SceneTextures, bSkipVolumetricRenderTarget, bSkipPerPixelTracing, HalfResolutionDepthCheckerboardMinMaxTexture, true, InstanceCullingManager);
}
FRDGTextureRef ForwardScreenSpaceShadowMaskTexture = nullptr;
FRDGTextureRef ForwardScreenSpaceShadowMaskHairTexture = nullptr;
if (IsForwardShadingEnabled(ShaderPlatform))
{
// With forward shading we need to render shadow maps early
ensureMsgf(!ActiveViewFamily->VirtualShadowMapArray.IsEnabled(), TEXT("Virtual shadow maps are not supported in the forward shading path"));
RenderShadowDepthMaps(GraphBuilder, InstanceCullingManager);
if (bHairStrandsEnable && !bHasRayTracedOverlay)
{
RenderHairPrePass(GraphBuilder, Scene, Views, InstanceCullingManager);
RenderHairBasePass(GraphBuilder, Scene, SceneTextures, Views, InstanceCullingManager);
}
RenderForwardShadowProjections(GraphBuilder, SceneTextures, ForwardScreenSpaceShadowMaskTexture, ForwardScreenSpaceShadowMaskHairTexture);
// With forward shading we need to render volumetric fog before the base pass
ComputeVolumetricFog(GraphBuilder, SceneTextures);
}
FDBufferTextures DBufferTextures = CreateDBufferTextures(GraphBuilder, SceneTextures.Config.Extent, ShaderPlatform);
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE(DeferredShadingSceneRenderer_DBuffer);
SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_DBuffer);
CompositionLighting.ProcessBeforeBasePass(GraphBuilder, DBufferTextures);
}
if (IsForwardShadingEnabled(ShaderPlatform) && bAllowStaticLighting)
{
RenderIndirectCapsuleShadows(GraphBuilder, SceneTextures);
}
FTranslucencyLightingVolumeTextures TranslucencyLightingVolumeTextures;
if (bRenderDeferredLighting && GbEnableAsyncComputeTranslucencyLightingVolumeClear && GSupportsEfficientAsyncCompute)
{
TranslucencyLightingVolumeTextures.Init(GraphBuilder, Views, ERDGPassFlags::AsyncCompute);
}
#if RHI_RAYTRACING
// Async AS builds can potentially overlap with BasePass. We only need to update ray tracing scene for the first view family,
// if multiple are rendered in a single scene render call.
FRDGBufferRef DynamicGeometryScratchBuffer = nullptr;
if (IsFirstViewFamily())
{
DispatchRayTracingWorldUpdates(GraphBuilder, DynamicGeometryScratchBuffer);
}
else
{
bRayTracingSceneReady = true;
}
#endif
{
RenderBasePass(GraphBuilder, SceneTextures, DBufferTextures, BasePassDepthStencilAccess, ForwardScreenSpaceShadowMaskTexture, InstanceCullingManager, bNaniteEnabled, NaniteRasterResults);
GraphBuilder.AddDispatchHint();
if (!bAllowReadOnlyDepthBasePass)
{
AddResolveSceneDepthPass(GraphBuilder, Views, SceneTextures.Depth);
}
#if WITH_MGPU
if (SceneTextures.Depth.Resolve && SceneTextures.GBufferA)
{
FGBufferTemporalTextureParams* PassParameters = GraphBuilder.AllocParameters<FGBufferTemporalTextureParams>();
PassParameters->DepthResolve = SceneTextures.Depth.Resolve;
PassParameters->GBufferA = SceneTextures.GBufferA;
GraphBuilder.AddPass(
RDG_EVENT_NAME("GBuffer Temporal Copy"),
PassParameters,
ERDGPassFlags::Copy | ERDGPassFlags::NeverCull,
[PassParameters](FRHIComputeCommandList& RHICmdList)
{
FName GBufferTemporalEffect("GBufferTemporalCopy");
RHICmdList.WaitForTemporalEffect(GBufferTemporalEffect);
const uint32 NumGBufferTemporalTextures = 2;
TStaticArray<FRHITexture*, NumGBufferTemporalTextures> GBufferTemporalTexturesRHI;
GBufferTemporalTexturesRHI[0] = PassParameters->DepthResolve->GetRHI();
GBufferTemporalTexturesRHI[1] = PassParameters->GBufferA->GetRHI();
RHICmdList.BroadcastTemporalEffect(
GBufferTemporalEffect, MakeArrayView(GBufferTemporalTexturesRHI.GetData(), GBufferTemporalTexturesRHI.Num()));
});
}
#endif // WITH_MGPU
if (bVisualizeNanite)
{
Nanite::AddVisualizationPasses(
GraphBuilder,
Scene,
SceneTextures,
ActiveViewFamily->EngineShowFlags,
Views,
NaniteRasterResults
);
}
// VisualizeVirtualShadowMap TODO
}
if (ActiveViewFamily->EngineShowFlags.VisualizeLightCulling)
{
FRDGTextureRef VisualizeLightCullingTexture = GraphBuilder.CreateTexture(SceneTextures.Color.Target->Desc, TEXT("SceneColorVisualizeLightCulling"));
AddClearRenderTargetPass(GraphBuilder, VisualizeLightCullingTexture, FLinearColor::Transparent);
SceneTextures.Color.Target = VisualizeLightCullingTexture;
// When not in MSAA, assign to both targets.
if (SceneTexturesConfig.NumSamples == 1)
{
SceneTextures.Color.Resolve = SceneTextures.Color.Target;
}
}
if (bUseGBuffer)
{
// mark GBufferA for saving for next frame if it's needed
ExtractNormalsForNextFrameReprojection(GraphBuilder, SceneTextures, Views);
}
// Rebuild scene textures to include GBuffers.
SceneTextures.SetupMode |= ESceneTextureSetupMode::GBuffers;
SceneTextures.UniformBuffer = CreateSceneTextureUniformBuffer(GraphBuilder, &SceneTextures, FeatureLevel, SceneTextures.SetupMode);
if (bRealTimeSkyCaptureEnabled)
{
Scene->ValidateSkyLightRealTimeCapture(GraphBuilder, Views[0], SceneTextures.Color.Target);
}
VisualizeVolumetricLightmap(GraphBuilder, SceneTextures);
// Occlusion after base pass
if (!bOcclusionBeforeBasePass)
{
RenderOcclusionLambda();
}
// End occlusion after base
if (!bUseGBuffer)
{
AddResolveSceneColorPass(GraphBuilder, Views, SceneTextures.Color);
}
// Render hair
if (bHairStrandsEnable && !IsForwardShadingEnabled(ShaderPlatform) && !bHasRayTracedOverlay)
{
RenderHairPrePass(GraphBuilder, Scene, Views, InstanceCullingManager);
RenderHairBasePass(GraphBuilder, Scene, SceneTextures, Views, InstanceCullingManager);
}
// Post base pass for material classification
// This needs to run before virtual shadow map, in order to have ready&cleared classified SSS data
if (Strata::IsStrataEnabled())
{
Strata::AddStrataMaterialClassificationPass(GraphBuilder, SceneTextures, Views);
}
// Shadows, lumen and fog after base pass
if (!bHasRayTracedOverlay)
{
// If forward shading is enabled, we rendered shadow maps earlier already
if (!IsForwardShadingEnabled(ShaderPlatform))
{
if (ActiveViewFamily->VirtualShadowMapArray.IsEnabled())
{
ensureMsgf(AreLightsInLightGrid(), TEXT("Virtual shadow map setup requires local lights to be injected into the light grid (this may be caused by 'r.LightCulling.Quality=0')."));
ActiveViewFamily->VirtualShadowMapArray.BuildPageAllocations(GraphBuilder, SceneTextures, Views, ActiveViewFamily->EngineShowFlags, SortedLightSet, ActiveViewFamily->VisibleLightInfos, NaniteRasterResults, *Scene);
}
RenderShadowDepthMaps(GraphBuilder, InstanceCullingManager);
}
CheckShadowDepthRenderCompleted();
#if RHI_RAYTRACING
// Lumen scene lighting requires ray tracing scene to be ready if HWRT shadows are desired
if (!bRayTracingSceneReady && Lumen::UseHardwareRayTracedSceneLighting(*ActiveViewFamily))
{
WaitForRayTracingScene(GraphBuilder, DynamicGeometryScratchBuffer);
bRayTracingSceneReady = true;
}
#endif // RHI_RAYTRACING
{
LLM_SCOPE_BYTAG(Lumen);
RenderLumenSceneLighting(GraphBuilder, LumenFrameTemporaries);
}
}
// End shadow and fog after base pass
if (bUpdateNaniteStreaming)
{
Nanite::GStreamingManager.SubmitFrameStreamingRequests(GraphBuilder);
}
{
FVirtualShadowMapArrayCacheManager* CacheManager = ActiveViewFamily->VirtualShadowMapArray.CacheManager;
if (CacheManager)
{
// Do this even if VSMs are disabled this frame to clean up any previously extracted data
CacheManager->ExtractFrameData(
GraphBuilder,
ActiveViewFamily->VirtualShadowMapArray,
*this,
ActiveViewFamily->EngineShowFlags.VirtualShadowMapCaching);
}
}
// If not all depth is written during the prepass, kick off async compute cloud after basepass
if (bShouldRenderVolumetricCloud && bAsyncComputeVolumetricCloud && DepthPass.EarlyZPassMode != DDM_AllOpaque && !bHasRayTracedOverlay)
{
HalfResolutionDepthCheckerboardMinMaxTexture = CreateHalfResolutionDepthCheckerboardMinMax(GraphBuilder, Views, SceneTextures.Depth.Resolve);
bHasHalfResCheckerboardMinMaxDepth = true;
bool bSkipVolumetricRenderTarget = false;
bool bSkipPerPixelTracing = true;
bAsyncComputeVolumetricCloud = RenderVolumetricCloud(GraphBuilder, SceneTextures, bSkipVolumetricRenderTarget, bSkipPerPixelTracing, HalfResolutionDepthCheckerboardMinMaxTexture, true, InstanceCullingManager);
}
if (CustomDepthPassLocation == ECustomDepthPassLocation::AfterBasePass)
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_CustomDepthPass_AfterBasePass);
if (RenderCustomDepthPass(GraphBuilder, SceneTextures.CustomDepth, SceneTextures.GetSceneTextureShaderParameters(FeatureLevel)))
{
SceneTextures.SetupMode |= ESceneTextureSetupMode::CustomDepth;
SceneTextures.UniformBuffer = CreateSceneTextureUniformBuffer(GraphBuilder, &SceneTextures, FeatureLevel, SceneTextures.SetupMode);
}
}
// If we are not rendering velocities in depth or base pass then do that here.
if (bShouldRenderVelocities && !bBasePassCanOutputVelocity && (Scene->EarlyZPassMode != DDM_AllOpaqueNoVelocity))
{
GraphBuilder.SetCommandListStat(GET_STATID(STAT_CLM_Velocity));
RenderVelocities(GraphBuilder, SceneTextures, EVelocityPass::Opaque, bHairStrandsEnable);
GraphBuilder.SetCommandListStat(GET_STATID(STAT_CLM_AfterVelocity));
}
// Copy lighting channels out of stencil before deferred decals which overwrite those values
FRDGTextureRef LightingChannelsTexture = CopyStencilToLightingChannelTexture(GraphBuilder, SceneTextures.Stencil);
// Pre-lighting composition lighting stage
// e.g. deferred decals, SSAO
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE(AfterBasePass);
SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_AfterBasePass);
if (!IsForwardShadingEnabled(ShaderPlatform))
{
AddResolveSceneDepthPass(GraphBuilder, Views, SceneTextures.Depth);
}
CompositionLighting.ProcessAfterBasePass(GraphBuilder);
}
// Rebuild scene textures to include velocity, custom depth, and SSAO.
SceneTextures.SetupMode |= ESceneTextureSetupMode::All;
SceneTextures.UniformBuffer = CreateSceneTextureUniformBuffer(GraphBuilder, &SceneTextures, FeatureLevel, SceneTextures.SetupMode);
if (!IsForwardShadingEnabled(ShaderPlatform))
{
// Clear stencil to 0 now that deferred decals are done using what was setup in the base pass.
AddClearStencilPass(GraphBuilder, SceneTextures.Depth.Target);
}
#if RHI_RAYTRACING
// If Lumen did not force an earlier ray tracing scene sync, we must wait for it here.
if (!bRayTracingSceneReady)
{
WaitForRayTracingScene(GraphBuilder, DynamicGeometryScratchBuffer);
bRayTracingSceneReady = true;
}
#endif // RHI_RAYTRACING
if (bRenderDeferredLighting)
{
RDG_GPU_STAT_SCOPE(GraphBuilder, RenderDeferredLighting);
RDG_CSV_STAT_EXCLUSIVE_SCOPE(GraphBuilder, RenderLighting);
SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_Lighting);
BeginGatheringLumenSurfaceCacheFeedback(GraphBuilder, Views[0], LumenFrameTemporaries);
FRDGTextureRef DynamicBentNormalAOTexture = nullptr;
RenderDiffuseIndirectAndAmbientOcclusion(GraphBuilder, SceneTextures, LumenFrameTemporaries, LightingChannelsTexture, /* bIsVisualizePass = */ false);
// These modulate the scenecolor output from the basepass, which is assumed to be indirect lighting
if (bAllowStaticLighting)
{
RenderIndirectCapsuleShadows(GraphBuilder, SceneTextures);
}
// These modulate the scene color output from the base pass, which is assumed to be indirect lighting
RenderDFAOAsIndirectShadowing(GraphBuilder, SceneTextures, DynamicBentNormalAOTexture);
// Clear the translucent lighting volumes before we accumulate
if ((GbEnableAsyncComputeTranslucencyLightingVolumeClear && GSupportsEfficientAsyncCompute) == false)
{
TranslucencyLightingVolumeTextures.Init(GraphBuilder, Views, ERDGPassFlags::Compute);
}
#if RHI_RAYTRACING
if (IsRayTracingEnabled())
{
RenderDitheredLODFadingOutMask(GraphBuilder, Views[0], SceneTextures.Depth.Target);
}
#endif
GraphBuilder.SetCommandListStat(GET_STATID(STAT_CLM_Lighting));
RenderLights(GraphBuilder, SceneTextures, TranslucencyLightingVolumeTextures, LightingChannelsTexture, SortedLightSet);
GraphBuilder.SetCommandListStat(GET_STATID(STAT_CLM_AfterLighting));
InjectTranslucencyLightingVolumeAmbientCubemap(GraphBuilder, Views, TranslucencyLightingVolumeTextures);
FilterTranslucencyLightingVolume(GraphBuilder, Views, TranslucencyLightingVolumeTextures);
// Render diffuse sky lighting and reflections that only operate on opaque pixels
RenderDeferredReflectionsAndSkyLighting(GraphBuilder, SceneTextures, DynamicBentNormalAOTexture);
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
// Renders debug visualizations for global illumination plugins
RenderGlobalIlluminationPluginVisualizations(GraphBuilder, LightingChannelsTexture);
#endif
AddSubsurfacePass(GraphBuilder, SceneTextures, Views);
Strata::AddStrataOpaqueRoughRefractionPasses(GraphBuilder, SceneTextures, Views);
{
RenderHairStrandsSceneColorScattering(GraphBuilder, SceneTextures.Color.Target, Scene, Views);
}
#if RHI_RAYTRACING
if (ShouldRenderRayTracingSkyLight(Scene->SkyLight)
//@todo - integrate RenderRayTracingSkyLight into RenderDiffuseIndirectAndAmbientOcclusion
&& GetViewPipelineState(Views[0]).DiffuseIndirectMethod != EDiffuseIndirectMethod::Lumen
&& ActiveViewFamily->EngineShowFlags.GlobalIllumination)
{
FRDGTextureRef SkyLightTexture = nullptr;
FRDGTextureRef SkyLightHitDistanceTexture = nullptr;
RenderRayTracingSkyLight(GraphBuilder, SceneTextures.Color.Target, SkyLightTexture, SkyLightHitDistanceTexture);
CompositeRayTracingSkyLight(GraphBuilder, SceneTextures, SkyLightTexture, SkyLightHitDistanceTexture);
}
#endif
}
else if (HairStrands::HasViewHairStrandsData(Views) && ActiveViewFamily->EngineShowFlags.Lighting)
{
RenderLightsForHair(GraphBuilder, SceneTextures, SortedLightSet, ForwardScreenSpaceShadowMaskHairTexture, LightingChannelsTexture);
RenderDeferredReflectionsAndSkyLightingHair(GraphBuilder);
}
// Volumetric fog after Lumen GI and shadow depths
if (!IsForwardShadingEnabled(ShaderPlatform) && !bHasRayTracedOverlay)
{
ComputeVolumetricFog(GraphBuilder, SceneTextures);
}
if (bShouldRenderVolumetricCloud && IsVolumetricRenderTargetEnabled() && !bHasHalfResCheckerboardMinMaxDepth && !bHasRayTracedOverlay)
{
HalfResolutionDepthCheckerboardMinMaxTexture = CreateHalfResolutionDepthCheckerboardMinMax(GraphBuilder, Views, SceneTextures.Depth.Resolve);
}
if (bShouldRenderVolumetricCloud && !bHasRayTracedOverlay)
{
if (!bAsyncComputeVolumetricCloud)
{
// Generate the volumetric cloud render target
bool bSkipVolumetricRenderTarget = false;
bool bSkipPerPixelTracing = true;
RenderVolumetricCloud(GraphBuilder, SceneTextures, bSkipVolumetricRenderTarget, bSkipPerPixelTracing, HalfResolutionDepthCheckerboardMinMaxTexture, false, InstanceCullingManager);
}
// Reconstruct the volumetric cloud render target to be ready to compose it over the scene
ReconstructVolumetricRenderTarget(GraphBuilder, Views, SceneTextures.Depth.Resolve, HalfResolutionDepthCheckerboardMinMaxTexture, bAsyncComputeVolumetricCloud);
}
const bool bShouldRenderTranslucency = !bHasRayTracedOverlay && ShouldRenderTranslucency();
// Union of all translucency view render flags.
ETranslucencyView TranslucencyViewsToRender = bShouldRenderTranslucency ? GetTranslucencyViews(Views) : ETranslucencyView::None;
FTranslucencyPassResourcesMap TranslucencyResourceMap(Views.Num());
const bool bShouldRenderSingleLayerWater = !bHasRayTracedOverlay && ShouldRenderSingleLayerWater(Views);
FSceneWithoutWaterTextures SceneWithoutWaterTextures;
if (bShouldRenderSingleLayerWater)
{
if (EnumHasAnyFlags(TranslucencyViewsToRender, ETranslucencyView::UnderWater))
{
RDG_CSV_STAT_EXCLUSIVE_SCOPE(GraphBuilder, RenderTranslucency);
SCOPE_CYCLE_COUNTER(STAT_TranslucencyDrawTime);
GraphBuilder.SetCommandListStat(GET_STATID(STAT_CLM_Translucency));
RenderTranslucency(GraphBuilder, SceneTextures, TranslucencyLightingVolumeTextures, &TranslucencyResourceMap, ETranslucencyView::UnderWater, InstanceCullingManager);
EnumRemoveFlags(TranslucencyViewsToRender, ETranslucencyView::UnderWater);
}
GraphBuilder.SetCommandListStat(GET_STATID(STAT_CLM_WaterPass));
RenderSingleLayerWater(GraphBuilder, SceneTextures, bShouldRenderVolumetricCloud, SceneWithoutWaterTextures, LumenFrameTemporaries);
}
// Rebuild scene textures to include scene color.
SceneTextures.UniformBuffer = CreateSceneTextureUniformBuffer(GraphBuilder, &SceneTextures, FeatureLevel, SceneTextures.SetupMode);
FRDGTextureRef LightShaftOcclusionTexture = nullptr;
// Draw Lightshafts
if (!bHasRayTracedOverlay && ActiveViewFamily->EngineShowFlags.LightShafts)
{
SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_RenderLightShaftOcclusion);
LightShaftOcclusionTexture = RenderLightShaftOcclusion(GraphBuilder, SceneTextures);
}
// Draw the sky atmosphere
if (!bHasRayTracedOverlay && bShouldRenderSkyAtmosphere && !IsForwardShadingEnabled(ShaderPlatform))
{
SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_RenderSkyAtmosphere);
RenderSkyAtmosphere(GraphBuilder, SceneTextures);
}
// Draw fog.
if (!bHasRayTracedOverlay && ShouldRenderFog(*ActiveViewFamily))
{
RDG_CSV_STAT_EXCLUSIVE_SCOPE(GraphBuilder, RenderFog);
SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_RenderFog);
RenderFog(GraphBuilder, SceneTextures, LightShaftOcclusionTexture);
}
// After the height fog, Draw volumetric clouds (having fog applied on them already) when using per pixel tracing,
if (!bHasRayTracedOverlay && bShouldRenderVolumetricCloud)
{
bool bSkipVolumetricRenderTarget = true;
bool bSkipPerPixelTracing = false;
RenderVolumetricCloud(GraphBuilder, SceneTextures, bSkipVolumetricRenderTarget, bSkipPerPixelTracing, HalfResolutionDepthCheckerboardMinMaxTexture, false, InstanceCullingManager);
}
// or composite the off screen buffer over the scene.
if (bVolumetricRenderTargetRequired)
{
ComposeVolumetricRenderTargetOverScene(GraphBuilder, Views, SceneTextures.Color.Target, SceneTextures.Depth.Target, bShouldRenderSingleLayerWater, SceneWithoutWaterTextures, SceneTextures);
}
FRendererModule& RendererModule = static_cast<FRendererModule&>(GetRendererModule());
RendererModule.RenderPostOpaqueExtensions(GraphBuilder, Views, SceneTextures);
RenderOpaqueFX(GraphBuilder, Views, FXSystem, SceneTextures.UniformBuffer);
if (Scene->GPUScene.ExecuteDeferredGPUWritePass(GraphBuilder, Views, EGPUSceneGPUWritePass::PostOpaqueRendering))
{
InstanceCullingManager.BeginDeferredCulling(GraphBuilder, Scene->GPUScene);
}
if (GetHairStrandsComposition() == EHairStrandsCompositionType::BeforeTranslucent)
{
RDG_GPU_STAT_SCOPE(GraphBuilder, HairRendering);
RenderHairComposition(GraphBuilder, Views, SceneTextures.Color.Target, SceneTextures.Depth.Target);
}
// Draw translucency.
if (!bHasRayTracedOverlay && TranslucencyViewsToRender != ETranslucencyView::None)
{
RDG_CSV_STAT_EXCLUSIVE_SCOPE(GraphBuilder, RenderTranslucency);
SCOPE_CYCLE_COUNTER(STAT_TranslucencyDrawTime);
RDG_EVENT_SCOPE(GraphBuilder, "Translucency");
// Raytracing doesn't need the distortion effect.
const bool bShouldRenderDistortion = TranslucencyViewsToRender != ETranslucencyView::RayTracing;
#if RHI_RAYTRACING
if (EnumHasAnyFlags(TranslucencyViewsToRender, ETranslucencyView::RayTracing))
{
RenderRayTracingTranslucency(GraphBuilder, SceneTextures.Color);
EnumRemoveFlags(TranslucencyViewsToRender, ETranslucencyView::RayTracing);
}
#endif
for (FViewInfo& View : Views)
{
if (GetViewPipelineState(View).ReflectionsMethod == EReflectionsMethod::Lumen)
{
RenderLumenFrontLayerTranslucencyReflections(GraphBuilder, View, SceneTextures, LumenFrameTemporaries);
}
}
// Sort objects' triangles
for (FViewInfo& View : Views)
{
if (OIT::IsEnabled(EOITSortingType::SortedTriangles, View))
{
OIT::AddSortTrianglesPass(GraphBuilder, View, Scene->OITSceneData, FTriangleSortingOrder::BackToFront);
}
}
// Render all remaining translucency views.
GraphBuilder.SetCommandListStat(GET_STATID(STAT_CLM_Translucency));
RenderTranslucency(GraphBuilder, SceneTextures, TranslucencyLightingVolumeTextures, &TranslucencyResourceMap, TranslucencyViewsToRender, InstanceCullingManager);
TranslucencyViewsToRender = ETranslucencyView::None;
// Compose hair before velocity/distortion pass since these pass write depth value,
// and this would make the hair composition fails in this cases.
if (GetHairStrandsComposition() == EHairStrandsCompositionType::AfterTranslucent)
{
RDG_GPU_STAT_SCOPE(GraphBuilder, HairRendering);
RenderHairComposition(GraphBuilder, Views, SceneTextures.Color.Target, SceneTextures.Depth.Target);
}
if (bShouldRenderDistortion)
{
GraphBuilder.SetCommandListStat(GET_STATID(STAT_CLM_Distortion));
RenderDistortion(GraphBuilder, SceneTextures.Color.Target, SceneTextures.Depth.Target);
}
if (bShouldRenderVelocities)
{
const bool bRecreateSceneTextures = !SceneTextures.Velocity;
GraphBuilder.SetCommandListStat(GET_STATID(STAT_CLM_TranslucentVelocity));
RenderVelocities(GraphBuilder, SceneTextures, EVelocityPass::Translucent, false);
if (bRecreateSceneTextures)
{
// Rebuild scene textures to include newly allocated velocity.
SceneTextures.UniformBuffer = CreateSceneTextureUniformBuffer(GraphBuilder, &SceneTextures, FeatureLevel, SceneTextures.SetupMode);
}
}
GraphBuilder.SetCommandListStat(GET_STATID(STAT_CLM_AfterTranslucency));
}
else if (GetHairStrandsComposition() == EHairStrandsCompositionType::AfterTranslucent)
{
RDG_GPU_STAT_SCOPE(GraphBuilder, HairRendering);
RenderHairComposition(GraphBuilder, Views, SceneTextures.Color.Target, SceneTextures.Depth.Target);
}
#if !UE_BUILD_SHIPPING
if (CVarForceBlackVelocityBuffer.GetValueOnRenderThread())
{
SceneTextures.Velocity = SystemTextures.Black;
// Rebuild the scene texture uniform buffer to include black.
SceneTextures.UniformBuffer = CreateSceneTextureUniformBuffer(GraphBuilder, &SceneTextures, FeatureLevel, SceneTextures.SetupMode);
}
#endif
{
if (HairStrandsBookmarkParameters.HasInstances())
{
RenderHairStrandsDebugInfo(GraphBuilder, Scene, Views, HairStrandsBookmarkParameters.HairClusterData, SceneTextures.Color.Target, SceneTextures.Depth.Target);
}
}
if (ActiveViewFamily->VirtualShadowMapArray.IsEnabled())
{
ActiveViewFamily->VirtualShadowMapArray.RenderDebugInfo(GraphBuilder, Views);
if (Views.Num() > 0)
{
ActiveViewFamily->VirtualShadowMapArray.PrintStats(GraphBuilder, Views[0]);
}
}
for (FViewInfo& View : Views)
{
ShadingEnergyConservation::Debug(GraphBuilder, View, SceneTextures);
}
for (FViewInfo& View : Views)
{
ShadingEnergyConservation::Debug(GraphBuilder, View, SceneTextures);
}
if (!bHasRayTracedOverlay && ActiveViewFamily->EngineShowFlags.LightShafts)
{
SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_RenderLightShaftBloom);
GraphBuilder.SetCommandListStat(GET_STATID(STAT_CLM_LightShaftBloom));
RenderLightShaftBloom(GraphBuilder, SceneTextures, /* inout */ TranslucencyResourceMap);
}
if (bUseVirtualTexturing)
{
RDG_GPU_STAT_SCOPE(GraphBuilder, VirtualTextureUpdate);
VirtualTextureFeedbackEnd(GraphBuilder);
}
#if RHI_RAYTRACING
if (IsRayTracingEnabled())
{
// Path tracer requires the full ray tracing pipeline support, as well as specialized extra shaders.
// Most of the ray tracing debug visualizations also require the full pipeline, but some support inline mode.
if (ActiveViewFamily->EngineShowFlags.PathTracing
&& FDataDrivenShaderPlatformInfo::GetSupportsPathTracing(Scene->GetShaderPlatform()))
{
for (const FViewInfo& View : Views)
{
RenderPathTracing(GraphBuilder, View, SceneTextures.UniformBuffer, SceneTextures.Color.Target);
}
}
else if (ActiveViewFamily->EngineShowFlags.RayTracingDebug)
{
for (const FViewInfo& View : Views)
{
RenderRayTracingDebug(GraphBuilder, View, SceneTextures.Color.Target);
}
}
}
#endif
RendererModule.RenderOverlayExtensions(GraphBuilder, Views, SceneTextures);
if (ActiveViewFamily->EngineShowFlags.PhysicsField && Scene->PhysicsField)
{
RenderPhysicsField(GraphBuilder, Views, Scene->PhysicsField, SceneTextures.Color.Target);
}
if (ActiveViewFamily->EngineShowFlags.VisualizeDistanceFieldAO && ShouldRenderDistanceFieldLighting())
{
GraphBuilder.SetCommandListStat(GET_STATID(STAT_CLM_RenderDistanceFieldLighting));
// Use the skylight's max distance if there is one, to be consistent with DFAO shadowing on the skylight
const float OcclusionMaxDistance = Scene->SkyLight && !Scene->SkyLight->bWantsStaticShadowing ? Scene->SkyLight->OcclusionMaxDistance : Scene->DefaultMaxDistanceFieldOcclusionDistance;
FRDGTextureRef DummyOutput = nullptr;
RenderDistanceFieldLighting(GraphBuilder, SceneTextures, FDistanceFieldAOParameters(OcclusionMaxDistance), DummyOutput, false, ActiveViewFamily->EngineShowFlags.VisualizeDistanceFieldAO);
}
// Draw visualizations just before use to avoid target contamination
if (ActiveViewFamily->EngineShowFlags.VisualizeMeshDistanceFields || ActiveViewFamily->EngineShowFlags.VisualizeGlobalDistanceField)
{
RenderMeshDistanceFieldVisualization(GraphBuilder, SceneTextures, FDistanceFieldAOParameters(Scene->DefaultMaxDistanceFieldOcclusionDistance));
}
if (bRenderDeferredLighting)
{
RenderLumenMiscVisualizations(GraphBuilder, SceneTextures, LumenFrameTemporaries);
RenderDiffuseIndirectAndAmbientOcclusion(GraphBuilder, SceneTextures, LumenFrameTemporaries, LightingChannelsTexture, /* bIsVisualizePass = */ true);
}
if (ActiveViewFamily->EngineShowFlags.StationaryLightOverlap)
{
RenderStationaryLightOverlap(GraphBuilder, SceneTextures, LightingChannelsTexture);
}
if (bShouldVisualizeVolumetricCloud && !bHasRayTracedOverlay)
{
RenderVolumetricCloud(GraphBuilder, SceneTextures, false, true, HalfResolutionDepthCheckerboardMinMaxTexture, false, InstanceCullingManager);
ReconstructVolumetricRenderTarget(GraphBuilder, Views, SceneTextures.Depth.Resolve, HalfResolutionDepthCheckerboardMinMaxTexture, false);
ComposeVolumetricRenderTargetOverSceneForVisualization(GraphBuilder, Views, SceneTextures.Color.Target, SceneTextures);
RenderVolumetricCloud(GraphBuilder, SceneTextures, true, false, HalfResolutionDepthCheckerboardMinMaxTexture, false, InstanceCullingManager);
}
// Resolve the scene color for post processing.
AddResolveSceneColorPass(GraphBuilder, Views, SceneTextures.Color);
RendererModule.RenderPostResolvedSceneColorExtension(GraphBuilder, SceneTextures);
FRDGTextureRef ViewFamilyTexture = TryCreateViewFamilyTexture(GraphBuilder, *ActiveViewFamily);
CopySceneCaptureComponentToTarget(GraphBuilder, SceneTextures, ViewFamilyTexture, *ActiveViewFamily, Views);
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex)
{
const FViewInfo& View = Views[ViewIndex];
if (((View.FinalPostProcessSettings.DynamicGlobalIlluminationMethod == EDynamicGlobalIlluminationMethod::ScreenSpace && ScreenSpaceRayTracing::ShouldKeepBleedFreeSceneColor(View))
|| GetViewPipelineState(View).DiffuseIndirectMethod == EDiffuseIndirectMethod::Lumen
|| GetViewPipelineState(View).ReflectionsMethod == EReflectionsMethod::Lumen)
&& !View.bStatePrevViewInfoIsReadOnly)
{
// Keep scene color and depth for next frame screen space ray tracing.
FSceneViewState* ViewState = View.ViewState;
GraphBuilder.QueueTextureExtraction(SceneTextures.Depth.Resolve, &ViewState->PrevFrameViewInfo.DepthBuffer);
GraphBuilder.QueueTextureExtraction(SceneTextures.Color.Resolve, &ViewState->PrevFrameViewInfo.ScreenSpaceRayTracingInput);
}
}
// Finish rendering for each view.
if (ActiveViewFamily->bResolveScene && ViewFamilyTexture)
{
RDG_EVENT_SCOPE(GraphBuilder, "PostProcessing");
RDG_GPU_STAT_SCOPE(GraphBuilder, Postprocessing);
SCOPE_CYCLE_COUNTER(STAT_FinishRenderViewTargetTime);
GraphBuilder.SetCommandListStat(GET_STATID(STAT_CLM_PostProcessing));
FPostProcessingInputs PostProcessingInputs;
PostProcessingInputs.ViewFamilyTexture = ViewFamilyTexture;
PostProcessingInputs.CustomDepthTexture = SceneTextures.CustomDepth.Depth;
PostProcessingInputs.SceneTextures = SceneTextures.UniformBuffer;
if (ActiveViewFamily->UseDebugViewPS())
{
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
const Nanite::FRasterResults* NaniteResults = bNaniteEnabled ? &NaniteRasterResults[ViewIndex] : nullptr;
RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask);
RDG_EVENT_SCOPE_CONDITIONAL(GraphBuilder, Views.Num() > 1, "View%d", ViewIndex);
PostProcessingInputs.TranslucencyViewResourcesMap = FTranslucencyViewResourcesMap(TranslucencyResourceMap, ViewIndex);
AddDebugViewPostProcessingPasses(GraphBuilder, View, PostProcessingInputs, NaniteResults);
}
}
else
{
for (int32 ViewExt = 0; ViewExt < ActiveViewFamily->ViewExtensions.Num(); ++ViewExt)
{
for (int32 ViewIndex = 0; ViewIndex < ActiveViewFamily->Views.Num(); ++ViewIndex)
{
FViewInfo& View = Views[ViewIndex];
RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask);
PostProcessingInputs.TranslucencyViewResourcesMap = FTranslucencyViewResourcesMap(TranslucencyResourceMap, ViewIndex);
ActiveViewFamily->ViewExtensions[ViewExt]->PrePostProcessPass_RenderThread(GraphBuilder, View, PostProcessingInputs);
}
}
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
const Nanite::FRasterResults* NaniteResults = bNaniteEnabled ? &NaniteRasterResults[ViewIndex] : nullptr;
RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask);
RDG_EVENT_SCOPE_CONDITIONAL(GraphBuilder, Views.Num() > 1, "View%d", ViewIndex);
PostProcessingInputs.TranslucencyViewResourcesMap = FTranslucencyViewResourcesMap(TranslucencyResourceMap, ViewIndex);
if (IsPostProcessVisualizeCalibrationMaterialEnabled(View))
{
const UMaterialInterface* DebugMaterialInterface = GetPostProcessVisualizeCalibrationMaterialInterface(View);
check(DebugMaterialInterface);
AddVisualizeCalibrationMaterialPostProcessingPasses(GraphBuilder, View, PostProcessingInputs, DebugMaterialInterface);
}
else
{
const FPerViewPipelineState& ViewPipelineState = GetViewPipelineState(View);
const bool bAnyLumenActive = ViewPipelineState.DiffuseIndirectMethod == EDiffuseIndirectMethod::Lumen || ViewPipelineState.ReflectionsMethod == EReflectionsMethod::Lumen;
AddPostProcessingPasses(GraphBuilder, View, ViewIndex, bAnyLumenActive, PostProcessingInputs, NaniteResults, InstanceCullingManager, &ActiveViewFamily->VirtualShadowMapArray, LumenFrameTemporaries);
}
}
}
}
// After AddPostProcessingPasses in case of Lumen Visualizations writing to feedback
FinishGatheringLumenSurfaceCacheFeedback(GraphBuilder, Views[0], LumenFrameTemporaries);
GEngine->GetPostRenderDelegateEx().Broadcast(GraphBuilder);
#if RHI_RAYTRACING
ReleaseRaytracingResources(GraphBuilder, Views, Scene->RayTracingScene);
#endif // RHI_RAYTRACING
#if WITH_MGPU
DoCrossGPUTransfers(GraphBuilder, RenderTargetGPUMask, ViewFamilyTexture);
#endif
{
SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_RenderFinish);
RDG_GPU_STAT_SCOPE(GraphBuilder, FrameRenderFinish);
GraphBuilder.SetCommandListStat(GET_STATID(STAT_CLM_RenderFinish));
RenderFinish(GraphBuilder, ViewFamilyTexture);
GraphBuilder.SetCommandListStat(GET_STATID(STAT_CLM_AfterFrame));
GraphBuilder.AddDispatchHint();
}
QueueSceneTextureExtractions(GraphBuilder, SceneTextures);
// Release the view's previous frame histories so that their memory can be reused at the graph's execution.
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
Views[ViewIndex].PrevViewInfo = FPreviousViewInfo();
}
}
#if RHI_RAYTRACING
bool AnyRayTracingPassEnabled(const FScene* Scene, const FViewInfo& View)
{
if (!IsRayTracingEnabled() || Scene == nullptr)
{
return false;
}
return ShouldRenderRayTracingAmbientOcclusion(View)
|| ShouldRenderRayTracingReflections(View)
|| ShouldRenderRayTracingGlobalIllumination(View)
|| ShouldRenderRayTracingTranslucency(View)
|| ShouldRenderRayTracingSkyLight(Scene->SkyLight)
|| ShouldRenderRayTracingShadows()
|| Scene->bHasRayTracedLights
|| ShouldRenderPluginRayTracingGlobalIllumination(View)
|| Lumen::AnyLumenHardwareRayTracingPassEnabled(Scene, View)
|| HasRayTracedOverlay(*View.Family);
}
bool ShouldRenderRayTracingEffect(bool bEffectEnabled, ERayTracingPipelineCompatibilityFlags CompatibilityFlags, const FSceneView* View)
{
if (!IsRayTracingEnabled() || (View && !View->bAllowRayTracing))
{
return false;
}
const bool bAllowPipeline = GRHISupportsRayTracingShaders &&
CVarRayTracingAllowPipeline.GetValueOnRenderThread() &&
EnumHasAnyFlags(CompatibilityFlags, ERayTracingPipelineCompatibilityFlags::FullPipeline);
const bool bAllowInline = GRHISupportsInlineRayTracing &&
CVarRayTracingAllowInline.GetValueOnRenderThread() &&
EnumHasAnyFlags(CompatibilityFlags, ERayTracingPipelineCompatibilityFlags::Inline);
// Disable the effect if current machine does not support the full ray tracing pipeline and the effect can't fall back to inline mode or vice versa.
if (!bAllowPipeline && !bAllowInline)
{
return false;
}
const int32 OverrideMode = CVarForceAllRayTracingEffects.GetValueOnRenderThread();
if (OverrideMode >= 0)
{
return OverrideMode > 0;
}
else
{
return bEffectEnabled;
}
}
bool HasRayTracedOverlay(const FSceneViewFamily& ViewFamily)
{
// Return true if a full screen ray tracing pass will be displayed on top of the raster pass
// This can be used to skip certain calculations
return
ViewFamily.EngineShowFlags.PathTracing ||
ViewFamily.EngineShowFlags.RayTracingDebug;
}
#endif // RHI_RAYTRACING