// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. /*============================================================================= DeferredShadingRenderer.cpp: Top level rendering loop for deferred shading =============================================================================*/ #include "DeferredShadingRenderer.h" #include "VelocityRendering.h" #include "AtmosphereRendering.h" #include "ScenePrivate.h" #include "ScreenRendering.h" #include "PostProcess/SceneFilterRendering.h" #include "PostProcess/ScreenSpaceReflections.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/VirtualTextureSystem.h" #include "VT/VirtualTextureFeedback.h" #include "GPUScene.h" #include "RayTracing/RayTracingMaterialHitShaders.h" #include "RayTracingDynamicGeometryCollection.h" #include "SceneViewFamilyBlackboard.h" #include "ScreenSpaceDenoise.h" #include "RayTracing/RaytracingOptions.h" #include "RayTracingDefinitions.h" #include "RayTracingInstance.h" static TAutoConsoleVariable CVarStencilForLODDither( TEXT("r.StencilForLODDither"), 0, TEXT("Whether to use stencil tests in the prepass, and depth-equal tests in the base pass to implement LOD dithering.\n") TEXT("If disabled, LOD dithering will be done through clip() instructions in the prepass and base pass, which disables EarlyZ.\n") TEXT("Forces a full prepass when enabled."), ECVF_RenderThreadSafe | ECVF_ReadOnly); TAutoConsoleVariable CVarCustomDepthOrder( TEXT("r.CustomDepth.Order"), 1, TEXT("When CustomDepth (and CustomStencil) is getting rendered\n") TEXT(" 0: Before GBuffer (can be more efficient with AsyncCompute, allows using it in DBuffer pass, no GBuffer blending decals allow GBuffer compression)\n") TEXT(" 1: After Base Pass (default)"), ECVF_RenderThreadSafe); #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) static TAutoConsoleVariable CVarVisualizeTexturePool( TEXT("r.VisualizeTexturePool"), 0, TEXT("Allows to enable the visualize the texture pool (currently only on console).\n") TEXT(" 0: off (default)\n") TEXT(" 1: on"), ECVF_Cheat | ECVF_RenderThreadSafe); #endif static TAutoConsoleVariable CVarClearCoatNormal( TEXT("r.ClearCoatNormal"), 0, TEXT("0 to disable clear coat normal.\n") TEXT(" 0: off\n") TEXT(" 1: on"), ECVF_ReadOnly); static TAutoConsoleVariable CVarIrisNormal( TEXT("r.IrisNormal"), 0, TEXT("0 to disable iris normal.\n") TEXT(" 0: off\n") TEXT(" 1: on"), ECVF_ReadOnly); int32 GbEnableAsyncComputeTranslucencyLightingVolumeClear = 1; static FAutoConsoleVariableRef CVarEnableAsyncComputeTranslucencyLightingVolumeClear( TEXT("r.EnableAsyncComputeTranslucencyLightingVolumeClear"), GbEnableAsyncComputeTranslucencyLightingVolumeClear, TEXT("Whether to clear the translucency lighting volume using async compute.\n"), ECVF_RenderThreadSafe | ECVF_Scalability ); int32 GDoPrepareDistanceFieldSceneAfterRHIFlush = 1; static FAutoConsoleVariableRef CVarDoPrepareDistanceFieldSceneAfterRHIFlush( TEXT("r.DoPrepareDistanceFieldSceneAfterRHIFlush"), GDoPrepareDistanceFieldSceneAfterRHIFlush, TEXT("If true, then do the distance field scene after the RHI sync and flush. Improves pipelining."), ECVF_RenderThreadSafe ); static TAutoConsoleVariable CVarParallelBasePass( TEXT("r.ParallelBasePass"), 1, TEXT("Toggles parallel base pass rendering. Parallel rendering must be enabled for this to have an effect."), ECVF_RenderThreadSafe ); static int32 GRayTracing = 0; static TAutoConsoleVariable CVarRayTracing( TEXT("r.RayTracing"), GRayTracing, TEXT("0 to disable ray tracing.\n") TEXT(" 0: off\n") TEXT(" 1: on"), ECVF_RenderThreadSafe | ECVF_ReadOnly); static TAutoConsoleVariable CVarUseAODenoiser( TEXT("r.AmbientOcclusion.Denoiser"), 2, TEXT("Choose the denoising algorithm.\n") TEXT(" 0: Disabled;\n") TEXT(" 1: Forces the default denoiser of the renderer;\n") TEXT(" 2: GScreenSpaceDenoiser witch may be overriden by a third party plugin (default)."), ECVF_RenderThreadSafe); static TAutoConsoleVariable CVarRayTracingTranslucency( TEXT("r.RayTracing.Translucency"), -1, TEXT("-1: Value driven by postprocess volume (default) \n") TEXT(" 0: ray tracing translucency off (use raster) \n") TEXT(" 1: ray tracing translucency enabled"), ECVF_RenderThreadSafe); #if !UE_BUILD_SHIPPING static TAutoConsoleVariable CVarForceBlackVelocityBuffer( TEXT("r.Test.ForceBlackVelocityBuffer"), 0, TEXT("Force the velocity buffer to have no motion vector for debugging purpose."), ECVF_RenderThreadSafe); #endif DECLARE_CYCLE_STAT(TEXT("PostInitViews FlushDel"), STAT_PostInitViews_FlushDel, STATGROUP_InitViews); 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 Render ServiceLocalQueue"), STAT_FDeferredShadingSceneRenderer_Render_ServiceLocalQueue, STATGROUP_SceneRendering); DECLARE_CYCLE_STAT(TEXT("DeferredShadingSceneRenderer DistanceFieldAO Init"), STAT_FDeferredShadingSceneRenderer_DistanceFieldAO_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 ClearLPVs"), STAT_FDeferredShadingSceneRenderer_ClearLPVs, STATGROUP_SceneRendering); DECLARE_CYCLE_STAT(TEXT("DeferredShadingSceneRenderer DBuffer"), STAT_FDeferredShadingSceneRenderer_DBuffer, STATGROUP_SceneRendering); DECLARE_CYCLE_STAT(TEXT("DeferredShadingSceneRenderer SetAndClearViewGBuffer"), STAT_FDeferredShadingSceneRenderer_SetAndClearViewGBuffer, STATGROUP_SceneRendering); DECLARE_CYCLE_STAT(TEXT("DeferredShadingSceneRenderer ClearGBufferAtMaxZ"), STAT_FDeferredShadingSceneRenderer_ClearGBufferAtMaxZ, 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 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_CYCLE_STAT(TEXT("DeferredShadingSceneRenderer ViewExtensionPostRenderBasePass"), STAT_FDeferredShadingSceneRenderer_ViewExtensionPostRenderBasePass, STATGROUP_SceneRendering); DECLARE_GPU_STAT_NAMED(RayTracingTLAS, TEXT("Ray Tracing Top Level Acceleration Structure")); DECLARE_GPU_STAT(Postprocessing); DECLARE_GPU_STAT(HZB); DECLARE_GPU_STAT_NAMED(AmbientOcclusionDenoiser, TEXT("Ambient Occlusion Denoiser")); DECLARE_GPU_STAT_NAMED(Unaccounted, TEXT("[unaccounted]")); const TCHAR* GetDepthPassReason(bool bDitheredLODTransitionsUseStencil, EShaderPlatform ShaderPlatform) { if (IsForwardShadingEnabled(ShaderPlatform)) { return TEXT("(Forced by ForwardShading)"); } bool bDBufferAllowed = IsUsingDBuffers(ShaderPlatform); if (bDBufferAllowed) { return TEXT("(Forced by DBuffer)"); } if (bDitheredLODTransitionsUseStencil) { return TEXT("(Forced by StencilLODDither)"); } return TEXT(""); } /*----------------------------------------------------------------------------- FDeferredShadingSceneRenderer -----------------------------------------------------------------------------*/ FDeferredShadingSceneRenderer::FDeferredShadingSceneRenderer(const FSceneViewFamily* InViewFamily,FHitProxyConsumer* HitProxyConsumer) : FSceneRenderer(InViewFamily, HitProxyConsumer) , EarlyZPassMode(Scene ? Scene->EarlyZPassMode : DDM_None) , bEarlyZPassMovable(Scene ? Scene->bEarlyZPassMovable : false) { static const auto StencilLODDitherCVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.StencilForLODDither")); bDitheredLODTransitionsUseStencil = StencilLODDitherCVar->GetValueOnAnyThread() != 0; // Shader complexity requires depth only pass to display masked material cost correctly if (ViewFamily.UseDebugViewPS() && ViewFamily.GetDebugViewShaderMode() != DVSM_OutputMaterialTextureScales) { EarlyZPassMode = DDM_AllOpaque; bEarlyZPassMovable = true; } } float GetSceneColorClearAlpha() { // Scene color alpha is used during scene captures and planar reflections. 1 indicates background should be shown, 0 indicates foreground is fully present. return 1.0f; } /** * Clears view where Z is still at the maximum value (ie no geometry rendered) */ void FDeferredShadingSceneRenderer::ClearGBufferAtMaxZ(FRHICommandList& RHICmdList) { // Assumes BeginRenderingSceneColor() has been called before this function check(RHICmdList.IsInsideRenderPass()); SCOPED_DRAW_EVENT(RHICmdList, ClearGBufferAtMaxZ); // Clear the G Buffer render targets const bool bClearBlack = Views[0].Family->EngineShowFlags.ShaderComplexity || Views[0].Family->EngineShowFlags.StationaryLightOverlap; const float ClearAlpha = GetSceneColorClearAlpha(); const FLinearColor ClearColor = bClearBlack ? FLinearColor(0, 0, 0, ClearAlpha) : FLinearColor(Views[0].BackgroundColor.R, Views[0].BackgroundColor.G, Views[0].BackgroundColor.B, ClearAlpha); FLinearColor ClearColors[MaxSimultaneousRenderTargets] = {ClearColor, FLinearColor(0.5f,0.5f,0.5f,0), FLinearColor(0,0,0,1), FLinearColor(0,0,0,0), FLinearColor(0,1,1,1), FLinearColor(1,1,1,1), FLinearColor::Transparent, FLinearColor::Transparent}; uint32 NumActiveRenderTargets = FSceneRenderTargets::Get(RHICmdList).GetNumGBufferTargets(); auto ShaderMap = GetGlobalShaderMap(FeatureLevel); TShaderMapRef > VertexShader(ShaderMap); FOneColorPS* PixelShader = NULL; // Assume for now all code path supports SM4, otherwise render target numbers are changed switch(NumActiveRenderTargets) { case 5: { TShaderMapRef > MRTPixelShader(ShaderMap); PixelShader = *MRTPixelShader; } break; case 6: { TShaderMapRef > MRTPixelShader(ShaderMap); PixelShader = *MRTPixelShader; } break; default: case 1: { TShaderMapRef > MRTPixelShader(ShaderMap); PixelShader = *MRTPixelShader; } break; } FGraphicsPipelineStateInitializer GraphicsPSOInit; RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit); // Opaque rendering, depth test but no depth writes GraphicsPSOInit.RasterizerState = TStaticRasterizerState::GetRHI(); GraphicsPSOInit.BlendState = TStaticBlendStateWriteMask<>::GetRHI(); GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState::GetRHI(); GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GetVertexDeclarationFVector4(); GraphicsPSOInit.BoundShaderState.VertexShaderRHI = GETSAFERHISHADER_VERTEX(*VertexShader); GraphicsPSOInit.BoundShaderState.PixelShaderRHI = GETSAFERHISHADER_PIXEL(PixelShader); GraphicsPSOInit.PrimitiveType = PT_TriangleStrip; SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit); VertexShader->SetDepthParameter(RHICmdList, float(ERHIZBuffer::FarPlane)); // Clear each viewport by drawing background color at MaxZ depth for(int32 ViewIndex = 0;ViewIndex < Views.Num();ViewIndex++) { SCOPED_CONDITIONAL_DRAW_EVENTF(RHICmdList, EventView, Views.Num() > 1, TEXT("ClearView%d"), ViewIndex); FViewInfo& View = Views[ViewIndex]; // Set viewport for this view RHICmdList.SetViewport(View.ViewRect.Min.X, View.ViewRect.Min.Y, 0, View.ViewRect.Max.X, View.ViewRect.Max.Y, 1); // Setup PS PixelShader->SetColors(RHICmdList, ClearColors, NumActiveRenderTargets); RHICmdList.SetStreamSource(0, GClearVertexBuffer.VertexBufferRHI, 0); // Render quad RHICmdList.DrawPrimitive(0, 2, 1); } } /** Render the TexturePool texture */ #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) void FDeferredShadingSceneRenderer::RenderVisualizeTexturePool(FRHICommandListImmediate& RHICmdList) { TRefCountPtr VisualizeTexturePool; /** Resolution for the texture pool visualizer texture. */ enum { TexturePoolVisualizerSizeX = 280, TexturePoolVisualizerSizeY = 140, }; FPooledRenderTargetDesc Desc(FPooledRenderTargetDesc::Create2DDesc(FIntPoint(TexturePoolVisualizerSizeX, TexturePoolVisualizerSizeY), PF_B8G8R8A8, FClearValueBinding::None, TexCreate_None, TexCreate_None, false)); GRenderTargetPool.FindFreeElement(RHICmdList, Desc, VisualizeTexturePool, TEXT("VisualizeTexturePool")); uint32 Pitch; FColor* TextureData = (FColor*)RHICmdList.LockTexture2D((FTexture2DRHIRef&)VisualizeTexturePool->GetRenderTargetItem().ShaderResourceTexture, 0, RLM_WriteOnly, Pitch, false); if(TextureData) { // clear with grey to get reliable background color FMemory::Memset(TextureData, 0x88, TexturePoolVisualizerSizeX * TexturePoolVisualizerSizeY * 4); RHICmdList.GetTextureMemoryVisualizeData(TextureData, TexturePoolVisualizerSizeX, TexturePoolVisualizerSizeY, Pitch, 4096); } RHICmdList.UnlockTexture2D((FTexture2DRHIRef&)VisualizeTexturePool->GetRenderTargetItem().ShaderResourceTexture, 0, false); FIntPoint RTExtent = FSceneRenderTargets::Get(RHICmdList).GetBufferSizeXY(); FVector2D Tex00 = FVector2D(0, 0); FVector2D Tex11 = FVector2D(1, 1); //todo VisualizeTexture(*VisualizeTexturePool, ViewFamily.RenderTarget, FIntRect(0, 0, RTExtent.X, RTExtent.Y), RTExtent, 1.0f, 0.0f, 0.0f, Tex00, Tex11, 1.0f, false); } #endif /** * Finishes the view family rendering. */ void FDeferredShadingSceneRenderer::RenderFinish(FRHICommandListImmediate& RHICmdList) { #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) { if(CVarVisualizeTexturePool.GetValueOnRenderThread()) { RenderVisualizeTexturePool(RHICmdList); } } #endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST) FSceneRenderer::RenderFinish(RHICmdList); // Some RT should be released as early as possible to allow sharing of that memory for other purposes. // SceneColor is be released in tone mapping, if not we want to get access to the HDR scene color after this pass so we keep it. // This becomes even more important with some limited VRam (XBoxOne). FSceneRenderTargets::Get(RHICmdList).SetLightAttenuation(0); } void BuildHZB( FRDGBuilder& GraphBuilder, FViewInfo& View ); /** * 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("BasePass"), STAT_CLM_BasePass, STATGROUP_CommandListMarkers); DECLARE_CYCLE_STAT(TEXT("AfterBasePass"), STAT_CLM_AfterBasePass, 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("Translucency"), STAT_CLM_Translucency, STATGROUP_CommandListMarkers); DECLARE_CYCLE_STAT(TEXT("RenderDistortion"), STAT_CLM_RenderDistortion, 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("RenderFinish"), STAT_CLM_RenderFinish, STATGROUP_CommandListMarkers); DECLARE_CYCLE_STAT(TEXT("AfterFrame"), STAT_CLM_AfterFrame, STATGROUP_CommandListMarkers); FGraphEventRef FDeferredShadingSceneRenderer::TranslucencyTimestampQuerySubmittedFence[FOcclusionQueryHelpers::MaxBufferedOcclusionFrames + 1]; FGlobalDynamicIndexBuffer FDeferredShadingSceneRenderer::DynamicIndexBufferForInitViews; FGlobalDynamicIndexBuffer FDeferredShadingSceneRenderer::DynamicIndexBufferForInitShadows; FGlobalDynamicVertexBuffer FDeferredShadingSceneRenderer::DynamicVertexBufferForInitViews; FGlobalDynamicVertexBuffer FDeferredShadingSceneRenderer::DynamicVertexBufferForInitShadows; TGlobalResource FDeferredShadingSceneRenderer::DynamicReadBufferForInitShadows; TGlobalResource FDeferredShadingSceneRenderer::DynamicReadBufferForInitViews; /** * Returns true if the depth Prepass needs to run */ static FORCEINLINE bool NeedsPrePass(const FDeferredShadingSceneRenderer* Renderer) { return (RHIHasTiledGPU(Renderer->ViewFamily.GetShaderPlatform()) == false) && (Renderer->EarlyZPassMode != DDM_None || Renderer->bEarlyZPassMovable != 0); } bool FDeferredShadingSceneRenderer::RenderHzb(FRHICommandListImmediate& RHICmdList) { FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList); SCOPED_GPU_STAT(RHICmdList, HZB); RHICmdList.TransitionResource(EResourceTransitionAccess::EReadable, SceneContext.GetSceneDepthSurface()); static const auto ICVarHZBOcc = IConsoleManager::Get().FindConsoleVariable(TEXT("r.HZBOcclusion")); bool bHZBOcclusion = ICVarHZBOcc->GetInt() != 0; for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) { FViewInfo& View = Views[ViewIndex]; FSceneViewState* ViewState = (FSceneViewState*)View.State; const uint32 bSSR = ShouldRenderScreenSpaceReflections(View); const bool bSSAO = ShouldRenderScreenSpaceAmbientOcclusion(View); if (bSSAO || bHZBOcclusion || bSSR) { FRDGBuilder GraphBuilder(RHICmdList); { RDG_EVENT_SCOPE(GraphBuilder, "BuildHZB(ViewId=%d)", ViewIndex); BuildHZB(GraphBuilder, Views[ViewIndex]); } GraphBuilder.Execute(); } if (bHZBOcclusion && ViewState && ViewState->HZBOcclusionTests.GetNum() != 0) { check(ViewState->HZBOcclusionTests.IsValidFrame(ViewState->OcclusionFrameCounter)); SCOPED_DRAW_EVENT(RHICmdList, HZB); ViewState->HZBOcclusionTests.Submit(RHICmdList, View); } } //async ssao only requires HZB and depth as inputs so get started ASAP if (CanOverlayRayTracingOutput() && GCompositionLighting.CanProcessAsyncSSAO(Views)) { GCompositionLighting.ProcessAsyncSSAO(RHICmdList, Views); } return bHZBOcclusion; } void FDeferredShadingSceneRenderer::RenderOcclusion(FRHICommandListImmediate& RHICmdList) { check(RHICmdList.IsOutsideRenderPass()); SCOPED_GPU_STAT(RHICmdList, HZB); { // Update the quarter-sized depth buffer with the current contents of the scene depth texture. // This needs to happen before occlusion tests, which makes use of the small depth buffer. SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_UpdateDownsampledDepthSurface); UpdateDownsampledDepthSurface(RHICmdList); } // Issue occlusion queries // This is done after the downsampled depth buffer is created so that it can be used for issuing queries BeginOcclusionTests(RHICmdList, true); } void FDeferredShadingSceneRenderer::FinishOcclusion(FRHICommandListImmediate& RHICmdList) { // Hint to the RHI to submit commands up to this point to the GPU if possible. Can help avoid CPU stalls next frame waiting // for these query results on some platforms. RHICmdList.SubmitCommandsHint(); } // The render thread is involved in sending stuff to the RHI, so we will periodically service that queue void ServiceLocalQueue() { SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_Render_ServiceLocalQueue); FTaskGraphInterface::Get().ProcessThreadUntilIdle(ENamedThreads::GetRenderThread_Local()); if (IsRunningRHIInSeparateThread()) { FRHICommandListExecutor::GetImmediateCommandList().ImmediateFlush(EImmediateFlushType::DispatchToRHIThread); } } // @return 0/1 static int32 GetCustomDepthPassLocation() { return FMath::Clamp(CVarCustomDepthOrder.GetValueOnRenderThread(), 0, 1); } void FDeferredShadingSceneRenderer::PrepareDistanceFieldScene(FRHICommandListImmediate& RHICmdList, bool bSplitDispatch) { if (ShouldPrepareDistanceFieldScene()) { CSV_SCOPED_TIMING_STAT_EXCLUSIVE(RenderDFAO); SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_DistanceFieldAO_Init); GDistanceFieldVolumeTextureAtlas.UpdateAllocations(); UpdateGlobalDistanceFieldObjectBuffers(RHICmdList); if (bSplitDispatch) { RHICmdList.ImmediateFlush(EImmediateFlushType::DispatchToRHIThread); } for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) { Views[ViewIndex].HeightfieldLightingViewInfo.SetupVisibleHeightfields(Views[ViewIndex], RHICmdList); if (ShouldPrepareGlobalDistanceField()) { float OcclusionMaxDistance = Scene->DefaultMaxDistanceFieldOcclusionDistance; // Use the skylight's max distance if there is one if (Scene->SkyLight && Scene->SkyLight->bCastShadows && !Scene->SkyLight->bWantsStaticShadowing) { OcclusionMaxDistance = Scene->SkyLight->OcclusionMaxDistance; } UpdateGlobalDistanceFieldVolume(RHICmdList, Views[ViewIndex], Scene, OcclusionMaxDistance, Views[ViewIndex].GlobalDistanceFieldInfo); } } if (!bSplitDispatch) { RHICmdList.ImmediateFlush(EImmediateFlushType::DispatchToRHIThread); } } } #if RHI_RAYTRACING bool FDeferredShadingSceneRenderer::GatherRayTracingWorldInstances(FRHICommandListImmediate& RHICmdList) { if (!IsRayTracingEnabled()) { return false; } { SCOPE_CYCLE_COUNTER(STAT_GenerateVisibleRayTracingMeshCommands); RayTracingCollector.ClearViewMeshArrays(); TArray DynamicMeshBatchStartOffset; TArray VisibleDrawCommandStartOffset; TArray DummyDynamicPrimitiveShaderData; for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) { FViewInfo& View = Views[ViewIndex]; DynamicMeshBatchStartOffset.Add(0); VisibleDrawCommandStartOffset.Add(0); View.RayTracingGeometryInstances.Reserve(Scene->Primitives.Num()); RayTracingCollector.AddViewMeshArrays( &View, &View.RayTracedDynamicMeshElements, &View.SimpleElementCollector, &DummyDynamicPrimitiveShaderData, ViewFamily.GetFeatureLevel(), &DynamicIndexBufferForInitViews, &DynamicVertexBufferForInitViews, &DynamicReadBufferForInitViews ); View.DynamicRayTracingMeshCommandStorage.RayTracingMeshCommands.Reserve(Scene->Primitives.Num()); View.VisibleRayTracingMeshCommands.Reserve(Scene->Primitives.Num()); } FViewInfo& ReferenceView = Views[0]; ReferenceView.RayTracingMeshResourceCollector = MakeUnique( Scene->GetFeatureLevel(), &DynamicIndexBufferForInitViews, &DynamicVertexBufferForInitViews, &DynamicReadBufferForInitViews); FRayTracingMaterialGatheringContext MaterialGatheringContext { Scene, &ReferenceView, ViewFamily, *ReferenceView.RayTracingMeshResourceCollector, *Scene->RayTracingDynamicGeometryCollection }; int32 BroadIndex = 0; for (int PrimitiveIndex = 0; PrimitiveIndex < Scene->PrimitiveSceneProxies.Num(); PrimitiveIndex++) { while (PrimitiveIndex >= int(Scene->TypeOffsetTable[BroadIndex].Offset)) { BroadIndex++; } FPrimitiveSceneInfo* SceneInfo = Scene->Primitives[PrimitiveIndex]; if (!SceneInfo->bIsRayTracingRelevant) { //skip over unsupported SceneProxies (warning don't make IsRayTracingRelevant data dependent other than the vtable) PrimitiveIndex = Scene->TypeOffsetTable[BroadIndex].Offset - 1; continue; } if (!SceneInfo->bIsVisibleInRayTracing) { continue; } uint8 RayTracedMeshElementsMask = 0; for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex) { FViewInfo& View = Views[ViewIndex]; if (!View.State)// || View.RayTracingRenderMode == ERayTracingRenderMode::Disabled) { continue; } if (View.bIsReflectionCapture && !SceneInfo->bIsVisibleInReflectionCaptures) { continue; } //#dxr_todo The Raytracing codepath does not support Showflags since data moved to the SceneInfo. //Touching the SceneProxy to determine this would simply cost too much if (SceneInfo->bShouldRenderInMainPass && SceneInfo->bDrawInGame) { if (SceneInfo->bIsRayTracingStaticRelevant && View.Family->EngineShowFlags.StaticMeshes) { static const auto ICVarStaticMeshLODDistanceScale = IConsoleManager::Get().FindConsoleVariable(TEXT("r.StaticMeshLODDistanceScale")); float LODScale = ICVarStaticMeshLODDistanceScale->GetFloat() * View.LODDistanceFactor; 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; int32 ForcedLODLevel = GetCVarForceLOD(); if (SceneInfo->bIsUsingCustomLODRules) { FPrimitiveSceneProxy* SceneProxy = Scene->PrimitiveSceneProxies[PrimitiveIndex]; LODToRender = SceneProxy->GetCustomLOD(View, View.LODDistanceFactor, ForcedLODLevel, MeshScreenSizeSquared); LODToRender.ClampToFirstLOD(CurFirstLODIdx); } else { LODToRender = ComputeLODForMeshes(SceneInfo->StaticMeshRelevances, View, Bounds.BoxSphereBounds.Origin, Bounds.BoxSphereBounds.SphereRadius, ForcedLODLevel, MeshScreenSizeSquared, CurFirstLODIdx, LODScale, false); } FRayTracingGeometryRHIRef RayTracingGeometryInstance = SceneInfo->GetStaticRayTracingGeometryInstance(LODToRender.GetRayTracedLOD()); if (!RayTracingGeometryInstance.IsValid()) { continue; } const int NewInstanceIndex = View.RayTracingGeometryInstances.Num(); uint8 NewInstanceMask = 0; bool bAllSegmentsOpaque = true; bool bAnySegmentsCastShadow = false; uint32 LODIndex = LODToRender.GetRayTracedLOD(); // 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)) { const auto& CachedRayTracingMeshCommandIndices = SceneInfo->CachedRayTracingMeshCommandIndicesPerLOD[LODIndex]; for (auto CommandIndex : CachedRayTracingMeshCommandIndices) { if (CommandIndex >= 0) { FVisibleRayTracingMeshCommand NewVisibleMeshCommand; NewVisibleMeshCommand.RayTracingMeshCommand = &Scene->CachedRayTracingMeshCommands.RayTracingMeshCommands[CommandIndex]; NewVisibleMeshCommand.InstanceIndex = NewInstanceIndex; View.VisibleRayTracingMeshCommands.Add(NewVisibleMeshCommand); VisibleDrawCommandStartOffset[ViewIndex]++; NewInstanceMask |= NewVisibleMeshCommand.RayTracingMeshCommand->InstanceMask; bAllSegmentsOpaque &= NewVisibleMeshCommand.RayTracingMeshCommand->bOpaque; bAnySegmentsCastShadow |= NewVisibleMeshCommand.RayTracingMeshCommand->bCastRayTracedShadows; } else { // CommandIndex == -1 indicates that the mesh batch has been filtered by FRayTracingMeshProcessor (like the shadow depth pass batch) // Do nothing in this case } } NewInstanceMask |= bAnySegmentsCastShadow ? RAY_TRACING_MASK_SHADOW : 0; // When no cached command is found, NewInstanceMask == 0 and the instance is effectively filtered out FRayTracingGeometryInstance RayTracingInstance = { RayTracingGeometryInstance }; RayTracingInstance.Transform = Scene->PrimitiveTransforms[PrimitiveIndex]; RayTracingInstance.UserData = (uint32)PrimitiveIndex; RayTracingInstance.Mask = NewInstanceMask; RayTracingInstance.bForceOpaque = bAllSegmentsOpaque; View.RayTracingGeometryInstances.Add(RayTracingInstance); } } else if (View.Family->EngineShowFlags.SkeletalMeshes) { RayTracedMeshElementsMask |= 1 << ViewIndex; } } } if (RayTracedMeshElementsMask != 0) { FPrimitiveSceneProxy* SceneProxy = Scene->PrimitiveSceneProxies[PrimitiveIndex]; TArray RayTracingInstances; SceneProxy->GetDynamicRayTracingInstances(MaterialGatheringContext, RayTracingInstances); if (RayTracingInstances.Num() > 0) { for (FRayTracingInstance& Instance : RayTracingInstances) { FRayTracingGeometryInstance RayTracingInstance = { Instance.Geometry->RayTracingGeometryRHI }; RayTracingInstance.Transform = Instance.InstanceTransforms[0]; ensureMsgf(Instance.InstanceTransforms.Num() == 1, TEXT("Multi-instancing hasn't been supported")); RayTracingInstance.UserData = (uint32)PrimitiveIndex; RayTracingInstance.Mask = Instance.Mask; RayTracingInstance.bForceOpaque = Instance.bForceOpaque; check(Instance.Materials.Num() == Instance.Geometry->Initializer.Segments.Num() || (Instance.Geometry->Initializer.Segments.Num() == 0 && Instance.Materials.Num() == 1)); uint32 InstanceIndex = ReferenceView.RayTracingGeometryInstances.Add(RayTracingInstance); for (int32 ViewIndex = 1; ViewIndex < Views.Num(); ViewIndex++) { Views[ViewIndex].RayTracingGeometryInstances.Add(RayTracingInstance); } for (int32 SegmentIndex = 0; SegmentIndex < Instance.Materials.Num(); SegmentIndex++) { FMeshBatch& MeshBatch = Instance.Materials[SegmentIndex]; FDynamicRayTracingMeshCommandContext CommandContext(ReferenceView.DynamicRayTracingMeshCommandStorage, ReferenceView.VisibleRayTracingMeshCommands, SegmentIndex, InstanceIndex); FRayTracingMeshProcessor RayTracingMeshProcessor(&CommandContext, Scene, &ReferenceView); RayTracingMeshProcessor.AddMeshBatch(MeshBatch, 1, SceneProxy); } } } } } } return true; } bool FDeferredShadingSceneRenderer::DispatchRayTracingWorldUpdates(FRHICommandListImmediate& RHICmdList) { if (!IsRayTracingEnabled()) { return false; } SCOPED_GPU_STAT(RHICmdList, RayTracingTLAS); Scene->GetRayTracingDynamicGeometryCollection()->DispatchUpdates(RHICmdList); for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex) { FViewInfo& View = Views[ViewIndex]; SET_DWORD_STAT(STAT_RayTracingInstances, View.RayTracingGeometryInstances.Num()); FRayTracingSceneInitializer Initializer; Initializer.Instances = View.RayTracingGeometryInstances; Initializer.ShaderSlotsPerGeometrySegment = RAY_TRACING_NUM_SHADER_SLOTS; View.RayTracingScene.RayTracingSceneRHI = RHICreateRayTracingScene(Initializer); RHICmdList.BuildAccelerationStructure(View.RayTracingScene.RayTracingSceneRHI); // #dxr_todo: register each effect at startup and just loop over them automatically to gather all required shaders TArray RayGenShaders; PrepareRayTracingReflections(View, RayGenShaders); PrepareRayTracingShadows(View, RayGenShaders); PrepareRayTracingRectLight(View, RayGenShaders); PrepareRayTracingGlobalIllumination(View, RayGenShaders); PrepareRayTracingTranslucency(View, RayGenShaders); PrepareRayTracingDebug(View, RayGenShaders); PreparePathTracing(View, RayGenShaders); if (RayGenShaders.Num()) { auto DefaultHitShader = View.ShaderMap->GetShader()->GetRayTracingShader(); auto DefaultMissShader = View.ShaderMap->GetShader()->GetRayTracingShader(); View.RayTracingMaterialPipeline = BindRayTracingMaterialPipeline(RHICmdList, View, RayGenShaders, DefaultMissShader, DefaultHitShader ); } } return true; } #endif // RHI_RAYTRACING extern bool IsLpvIndirectPassRequired(const FViewInfo& View); static TAutoConsoleVariable 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::Render(FRHICommandListImmediate& RHICmdList) { check(RHICmdList.IsOutsideRenderPass()); CSV_SCOPED_TIMING_STAT_EXCLUSIVE(RenderOther); PrepareViewRectsForRendering(); SCOPED_NAMED_EVENT(FDeferredShadingSceneRenderer_Render, FColor::Emerald); #if RHI_RAYTRACING // Gather mesh instances, shaders, resources, parameters, etc. and build ray tracing acceleration structure GatherRayTracingWorldInstances(RHICmdList); if (Views[0].RayTracingRenderMode != ERayTracingRenderMode::PathTracing) { extern ENGINE_API float GAveragePathTracedMRays; GAveragePathTracedMRays = 0.0f; } #endif // RHI_RAYTRACING #if WITH_MGPU const FRHIGPUMask RenderTargetGPUMask = (GNumExplicitGPUsForRendering > 1 && ViewFamily.RenderTarget) ? ViewFamily.RenderTarget->GetGPUMask(RHICmdList) : FRHIGPUMask::GPU0(); ComputeViewGPUMasks(RenderTargetGPUMask); #endif // WITH_MGPU FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList); //make sure all the targets we're going to use will be safely writable. GRenderTargetPool.TransitionTargetsWritable(RHICmdList); // this way we make sure the SceneColor format is the correct one and not the one from the end of frame before SceneContext.ReleaseSceneColor(); const bool bDBuffer = !ViewFamily.EngineShowFlags.ShaderComplexity && ViewFamily.EngineShowFlags.Decals && IsUsingDBuffers(ShaderPlatform); WaitOcclusionTests(RHICmdList); if (!ViewFamily.EngineShowFlags.Rendering) { return; } SCOPED_DRAW_EVENT(RHICmdList, Scene); // Anything rendered inside Render() which isn't accounted for will fall into this stat // This works because child stat events do not contribute to their parents' times (see GPU_STATS_CHILD_TIMES_INCLUDED) SCOPED_GPU_STAT(RHICmdList, Unaccounted); { SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_Render_Init); // Initialize global system textures (pass-through if already initialized). GSystemTextures.InitializeTextures(RHICmdList, FeatureLevel); // Allocate the maximum scene render target space for the current view family. SceneContext.Allocate(RHICmdList, this); } const bool bIsWireframe = ViewFamily.EngineShowFlags.Wireframe; // Use readonly depth in the base pass if we have a full depth prepass const bool bAllowReadonlyDepthBasePass = EarlyZPassMode == DDM_AllOpaque && !ViewFamily.EngineShowFlags.ShaderComplexity && !ViewFamily.UseDebugViewPS() && !bIsWireframe && !ViewFamily.EngineShowFlags.LightMapDensity; const FExclusiveDepthStencil::Type BasePassDepthStencilAccess = bAllowReadonlyDepthBasePass ? FExclusiveDepthStencil::DepthRead_StencilWrite : FExclusiveDepthStencil::DepthWrite_StencilWrite; FGraphEventArray UpdateViewCustomDataEvents; FILCUpdatePrimTaskData ILCTaskData; // Find the visible primitives. RHICmdList.ImmediateFlush(EImmediateFlushType::DispatchToRHIThread); bool bDoInitViewAftersPrepass = InitViews(RHICmdList, BasePassDepthStencilAccess, ILCTaskData, UpdateViewCustomDataEvents); static const auto CVarVirtualTextureLightmaps = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.VirtualTexturedLightmaps")); if (CVarVirtualTextureLightmaps && CVarVirtualTextureLightmaps->GetValueOnRenderThread()) { // TODO should probably be in InitViews GetVirtualTextureSystem()->Update( RHICmdList, FeatureLevel ); } #if !UE_BUILD_SHIPPING if (CVarStallInitViews.GetValueOnRenderThread() > 0.0f) { SCOPE_CYCLE_COUNTER(STAT_InitViews_Intentional_Stall); FPlatformProcess::Sleep(CVarStallInitViews.GetValueOnRenderThread() / 1000.0f); } #endif if (GRHICommandList.UseParallelAlgorithms()) { // there are dynamic attempts to get this target during parallel rendering for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) { Views[ViewIndex].GetEyeAdaptation(RHICmdList); } } if (GDoPrepareDistanceFieldSceneAfterRHIFlush && (GRHINeedsExtraDeletionLatency || !GRHICommandList.Bypass())) { // we will probably stall on occlusion queries, so might as well have the RHI thread and GPU work while we wait. SCOPE_CYCLE_COUNTER(STAT_PostInitViews_FlushDel); RHICmdList.ImmediateFlush(EImmediateFlushType::FlushRHIThreadFlushResources); } IRendererModule& RendererModule = GetRendererModule(); FPreSceneRenderValues PreSceneRenderValues = RendererModule.PreSceneRenderExtension(); Views[0].bUsesGlobalDistanceField |= PreSceneRenderValues.bUsesGlobalDistanceField; UpdateGPUScene(RHICmdList, *Scene); for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) { UploadDynamicPrimitiveShaderDataForView(RHICmdList, *Scene, Views[ViewIndex]); } if (!bDoInitViewAftersPrepass) { bool bSplitDispatch = !GDoPrepareDistanceFieldSceneAfterRHIFlush; PrepareDistanceFieldScene(RHICmdList, bSplitDispatch); } if (!GDoPrepareDistanceFieldSceneAfterRHIFlush && (GRHINeedsExtraDeletionLatency || !GRHICommandList.Bypass())) { // we will probably stall on occlusion queries, so might as well have the RHI thread and GPU work while we wait. SCOPE_CYCLE_COUNTER(STAT_PostInitViews_FlushDel); FRHICommandListExecutor::GetImmediateCommandList().ImmediateFlush(EImmediateFlushType::FlushRHIThreadFlushResources); } static const auto ClearMethodCVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.ClearSceneMethod")); bool bRequiresRHIClear = true; bool bRequiresFarZQuadClear = false; const bool bUseGBuffer = IsUsingGBuffers(ShaderPlatform); const bool bRenderDeferredLighting = ViewFamily.EngineShowFlags.Lighting && FeatureLevel >= ERHIFeatureLevel::SM4 && ViewFamily.EngineShowFlags.DeferredLighting && bUseGBuffer #if RHI_RAYTRACING && (Views[0].RayTracingRenderMode != ERayTracingRenderMode::PathTracing) // #dxr_todo: what about multi-view case? && (Views[0].RayTracingRenderMode != ERayTracingRenderMode::RayTracingDebug) #endif ; bool bCanOverlayRayTracingOutput = CanOverlayRayTracingOutput(); bool bComputeLightGrid = false; // Simple forward shading doesn't support local lights. No need to compute light grid if (!IsSimpleForwardShadingEnabled(ShaderPlatform)) { if (bUseGBuffer) { bComputeLightGrid = bRenderDeferredLighting; } else { bComputeLightGrid = ViewFamily.EngineShowFlags.Lighting; } bComputeLightGrid |= ( ShouldRenderVolumetricFog() || ViewFamily.ViewMode != VMI_Lit); } if (ClearMethodCVar) { int32 ClearMethod = ClearMethodCVar->GetValueOnRenderThread(); if (ClearMethod == 0 && !ViewFamily.EngineShowFlags.Game) { // Do not clear the scene only if the view family is in game mode. ClearMethod = 1; } switch (ClearMethod) { case 0: // No clear { bRequiresRHIClear = false; bRequiresFarZQuadClear = false; break; } case 1: // RHICmdList.Clear { bRequiresRHIClear = true; bRequiresFarZQuadClear = false; break; } case 2: // Clear using far-z quad { bRequiresFarZQuadClear = true; bRequiresRHIClear = false; break; } } } // Always perform a full buffer clear for wireframe, shader complexity view mode, and stationary light overlap viewmode. if (bIsWireframe || ViewFamily.EngineShowFlags.ShaderComplexity || ViewFamily.EngineShowFlags.StationaryLightOverlap) { bRequiresRHIClear = true; } // 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(FeatureLevel) && (!bIsWireframe || bIsViewFrozen || bHasViewParent); // Dynamic vertex and index buffers need to be committed before rendering. GEngine->GetPreRenderDelegate().Broadcast(); { SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_FGlobalDynamicVertexBuffer_Commit); DynamicIndexBufferForInitViews.Commit(); DynamicVertexBufferForInitViews.Commit(); DynamicReadBufferForInitViews.Commit(); if (!bDoInitViewAftersPrepass) { DynamicVertexBufferForInitShadows.Commit(); DynamicIndexBufferForInitShadows.Commit(); DynamicReadBufferForInitShadows.Commit(); } } // Only update the GPU particle simulation for the main view //@todo - this is needed because the GPU particle simulation is updated within a frame render. Simulation should happen outside of a visible frame rendering. // This also causes GPU particles to be one frame behind in scene captures and planar reflections. const bool bAllowGPUParticleSceneUpdate = !Views[0].bIsPlanarReflection && !Views[0].bIsSceneCapture && !Views[0].bIsReflectionCapture; // Notify the FX system that the scene is about to be rendered. bool bDoFXPrerender = Scene->FXSystem && Views.IsValidIndex(0) && bAllowGPUParticleSceneUpdate; if (bDoFXPrerender) { SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_FXSystem_PreRender); RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_FXPreRender)); Scene->FXSystem->PreRender(RHICmdList, &Views[0].GlobalDistanceFieldInfo.ParameterData); } bool bDidAfterTaskWork = false; auto AfterTasksAreStarted = [&bDidAfterTaskWork, bDoInitViewAftersPrepass, this, &RHICmdList, &ILCTaskData, &UpdateViewCustomDataEvents, bDoFXPrerender]() { if (!bDidAfterTaskWork) { QUICK_SCOPE_CYCLE_COUNTER(STAT_AfterPrepassTasksWork); bDidAfterTaskWork = true; // only do this once if (bDoInitViewAftersPrepass) { InitViewsPossiblyAfterPrepass(RHICmdList, ILCTaskData, UpdateViewCustomDataEvents); PrepareDistanceFieldScene(RHICmdList, false); { SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_FGlobalDynamicVertexBuffer_Commit); DynamicVertexBufferForInitShadows.Commit(); DynamicIndexBufferForInitShadows.Commit(); DynamicReadBufferForInitShadows.Commit(); } ServiceLocalQueue(); } } }; if (FGPUSkinCache* GPUSkinCache = Scene->GetGPUSkinCache()) { GPUSkinCache->TransitionAllToReadable(RHICmdList); } // Before starting the render, all async task for the Custom data must be completed if (UpdateViewCustomDataEvents.Num() > 0) { QUICK_SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_AsyncUpdateViewCustomData_Wait); FTaskGraphInterface::Get().WaitUntilTasksComplete(UpdateViewCustomDataEvents, ENamedThreads::GetRenderThread()); } if (CVarVirtualTextureLightmaps && CVarVirtualTextureLightmaps->GetValueOnRenderThread()) { // Create VT feedback buffer FIntPoint Size = SceneContext.GetBufferSizeXY(); Size = FIntPoint::DivideAndRoundUp( Size, 16 ); GVirtualTextureFeedback.CreateResourceGPU( RHICmdList, Size.X, Size.Y ); } checkSlow(RHICmdList.IsOutsideRenderPass()); // The Z-prepass // Draw the scene pre-pass / early z pass, populating the scene depth buffer and HiZ GRenderTargetPool.AddPhaseEvent(TEXT("EarlyZPass")); const bool bNeedsPrePass = NeedsPrePass(this); bool bDepthWasCleared; if (bNeedsPrePass) { bDepthWasCleared = RenderPrePass(RHICmdList, AfterTasksAreStarted); } else { // we didn't do the prepass, but we still want the HMD mask if there is one AfterTasksAreStarted(); RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_PrePass)); bDepthWasCleared = RenderPrePassHMD(RHICmdList); } check(bDidAfterTaskWork); RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_AfterPrePass)); ServiceLocalQueue(); #if RHI_RAYTRACING // Must be done after FGlobalDynamicVertexBuffer::Get().Commit() for dynamic geometries to be updated DispatchRayTracingWorldUpdates(RHICmdList); #endif // Z-Prepass End checkSlow(RHICmdList.IsOutsideRenderPass()); const bool bShouldRenderVelocities = ShouldRenderVelocities(); const bool bBasePassCanOutputVelocity = FVelocityRendering::BasePassCanOutputVelocity(FeatureLevel); const bool bUseSelectiveBasePassOutputs = IsUsingSelectiveBasePassOutputs(ShaderPlatform); SceneContext.ResolveSceneDepthTexture(RHICmdList, FResolveRect(0, 0, FamilySize.X, FamilySize.Y)); SceneContext.ResolveSceneDepthToAuxiliaryTexture(RHICmdList); ComputeLightGrid(RHICmdList, bComputeLightGrid); { SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_AllocGBufferTargets); SceneContext.PreallocGBufferTargets(); // Even if !bShouldRenderVelocities, the velocity buffer must be bound because it's a compile time option for the shader. SceneContext.AllocGBufferTargets(RHICmdList); } checkSlow(RHICmdList.IsOutsideRenderPass()); // Early occlusion queries const bool bOcclusionBeforeBasePass = (EarlyZPassMode == EDepthDrawingMode::DDM_AllOccluders) || (EarlyZPassMode == EDepthDrawingMode::DDM_AllOpaque); if (bOcclusionBeforeBasePass) { if (bIsOcclusionTesting) { RenderOcclusion(RHICmdList); } bool bUseHzbOcclusion = RenderHzb(RHICmdList); SCOPED_GPU_STAT(RHICmdList, HZB); if (bUseHzbOcclusion || bIsOcclusionTesting) { FinishOcclusion(RHICmdList); } if (bIsOcclusionTesting) { FenceOcclusionTests(RHICmdList); } } ServiceLocalQueue(); // End early occlusion queries checkSlow(RHICmdList.IsOutsideRenderPass()); // Early Shadow depth rendering if (bOcclusionBeforeBasePass) { // Before starting the shadow render, all async task for the shadow Custom data must be completed if (bDoInitViewAftersPrepass && UpdateViewCustomDataEvents.Num() > 0) { QUICK_SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_AsyncUpdateViewCustomData_Wait); FTaskGraphInterface::Get().WaitUntilTasksComplete(UpdateViewCustomDataEvents, ENamedThreads::GetRenderThread()); } RenderShadowDepthMaps(RHICmdList); ServiceLocalQueue(); } // End early Shadow depth rendering checkSlow(RHICmdList.IsOutsideRenderPass()); // Clear LPVs for all views if (FeatureLevel >= ERHIFeatureLevel::SM5) { SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_ClearLPVs); ClearLPVs(RHICmdList); ServiceLocalQueue(); } if(GetCustomDepthPassLocation() == 0) { QUICK_SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_CustomDepthPass0); RenderCustomDepthPassAtLocation(RHICmdList, 0); } if (bOcclusionBeforeBasePass) { ComputeVolumetricFog(RHICmdList); } TRefCountPtr ForwardScreenSpaceShadowMask; if (IsForwardShadingEnabled(ShaderPlatform)) { RenderForwardShadingShadowProjections(RHICmdList, ForwardScreenSpaceShadowMask); RenderIndirectCapsuleShadows( RHICmdList, NULL, NULL); } // only temporarily available after early z pass and until base pass check(!SceneContext.DBufferA); check(!SceneContext.DBufferB); check(!SceneContext.DBufferC); if (bDBuffer || IsForwardShadingEnabled(ShaderPlatform)) { SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_DBuffer); // e.g. DBuffer deferred decals for(int32 ViewIndex = 0;ViewIndex < Views.Num();ViewIndex++) { SCOPED_CONDITIONAL_DRAW_EVENTF(RHICmdList, EventView,Views.Num() > 1, TEXT("View%d"), ViewIndex); FViewInfo& View = Views[ViewIndex]; Scene->UniformBuffers.UpdateViewUniformBuffer(View); uint32 SSAOLevels = FSSAOHelper::ComputeAmbientOcclusionPassCount(View); // In deferred shader, the SSAO uses the GBuffer and must be executed after base pass. Otherwise, async compute runs the shader in RenderHzb() // In forward, if zprepass is off - as SSAO here requires a valid HZB buffer - disable SSAO if (!IsForwardShadingEnabled(ShaderPlatform) || !View.HZB.IsValid() || FSSAOHelper::IsAmbientOcclusionAsyncCompute(View, SSAOLevels)) { SSAOLevels = 0; } GCompositionLighting.ProcessBeforeBasePass(RHICmdList, View, bDBuffer, SSAOLevels); } ServiceLocalQueue(); } checkSlow(RHICmdList.IsOutsideRenderPass()); if (bRenderDeferredLighting) { bool bShouldAllocateDeferredShadingPathRenderTargets = false; const char* str = SceneContext.ScreenSpaceAO ? "Allocated" : "Unallocated"; //ScreenSpaceAO is determining factor of detecting render target allocation for(int Index = 0; Index < (NumTranslucentVolumeRenderTargetSets * Views.Num()); ++Index) { if(!SceneContext.TranslucencyLightingVolumeAmbient[Index] || !SceneContext.TranslucencyLightingVolumeDirectional[Index]) { ensureMsgf(SceneContext.TranslucencyLightingVolumeAmbient[Index], TEXT("%s%d is unallocated, Deferred Render Targets would be detected as: %s"), "TranslucencyLightingVolumeAmbient", Index, str); ensureMsgf(SceneContext.TranslucencyLightingVolumeDirectional[Index], TEXT("%s%d is unallocated, Deferred Render Targets would be detected as: %s"), "TranslucencyLightingVolumeDirectional", Index, str); bShouldAllocateDeferredShadingPathRenderTargets = true; break; } } if(bShouldAllocateDeferredShadingPathRenderTargets) { SceneContext.AllocateDeferredShadingPathRenderTargets(RHICmdList); } if (GbEnableAsyncComputeTranslucencyLightingVolumeClear && GSupportsEfficientAsyncCompute) { ClearTranslucentVolumeLightingAsyncCompute(RHICmdList); } } checkSlow(RHICmdList.IsOutsideRenderPass()); bool bIsWireframeRenderpass = bIsWireframe && FSceneRenderer::ShouldCompositeEditorPrimitives(Views[0]); bool bRenderLightmapDensity = ViewFamily.EngineShowFlags.LightMapDensity && AllowDebugViewmodes(); bool bDoParallelBasePass = GRHICommandList.UseParallelAlgorithms() && CVarParallelBasePass.GetValueOnRenderThread(); // BASE PASS AND GBUFFER SETUP // Gross logic to cover all the cases of special rendering modes + parallel dispatch // Clear the GBuffer render targets bool bIsGBufferCurrent = false; if (bRequiresRHIClear) { SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_SetAndClearViewGBuffer); bool bClearDepth = !bDepthWasCleared; // if we didn't to the prepass above, then we will need to clear now, otherwise, it's already been cleared and rendered to ERenderTargetLoadAction ColorLoadAction = ERenderTargetLoadAction::ELoad; ERenderTargetLoadAction DepthLoadAction = bClearDepth ? ERenderTargetLoadAction::EClear : (!IsMetalPlatform(ShaderPlatform) ? ERenderTargetLoadAction::ENoAction : ERenderTargetLoadAction::ELoad); const bool bClearBlack = ViewFamily.EngineShowFlags.ShaderComplexity || ViewFamily.EngineShowFlags.StationaryLightOverlap; const float ClearAlpha = GetSceneColorClearAlpha(); FLinearColor ClearColor = bClearBlack ? FLinearColor(0, 0, 0, ClearAlpha) : FLinearColor(Views[0].BackgroundColor.R, Views[0].BackgroundColor.G, Views[0].BackgroundColor.B, ClearAlpha); ColorLoadAction = ERenderTargetLoadAction::EClear; // The first time through we'll clear the Overdraw UAVs. SceneContext.BeginRenderingGBuffer(RHICmdList, ColorLoadAction, DepthLoadAction, BasePassDepthStencilAccess, ViewFamily.EngineShowFlags.ShaderComplexity, true, ClearColor); // If we are in wireframe mode or will go wide later this pass is just the clear. if (bIsWireframeRenderpass || bDoParallelBasePass) { RHICmdList.EndRenderPass(); } else { bIsGBufferCurrent = true; } ServiceLocalQueue(); } // Wireframe mode requires bRequiresRHIClear to be true. // Rendering will be very funny without it and the call to BeginRenderingGBuffer will call AllocSceneColor which is needed for the EditorPrimitives resolve. if (bIsWireframeRenderpass) { check(bRequiresRHIClear); // In Editor we want wire frame view modes to be MSAA for better quality. Resolve will be done with EditorPrimitives FRHIRenderPassInfo RPInfo(SceneContext.GetEditorPrimitivesColor(RHICmdList), ERenderTargetActions::Clear_Store); RPInfo.DepthStencilRenderTarget.Action = EDepthStencilTargetActions::ClearDepthStencil_StoreDepthStencil; RPInfo.DepthStencilRenderTarget.DepthStencilTarget = SceneContext.GetEditorPrimitivesDepth(RHICmdList); RPInfo.DepthStencilRenderTarget.ExclusiveDepthStencil = FExclusiveDepthStencil::DepthWrite_StencilWrite; RHICmdList.BeginRenderPass(RPInfo, TEXT("Wireframe")); // #todo-renderpasses In serial mode wireframe rendering only binds one target // In parallel the entire gbuffer is bound. This was the previous SetRenderTarget behavior, preserved here. // This is just a clear in the parallel case. if (bDoParallelBasePass) { RHICmdList.EndRenderPass(); } } else if (!bIsGBufferCurrent && (!bDoParallelBasePass || bRenderLightmapDensity)) { // Make sure we have began the renderpass ERenderTargetLoadAction DepthLoadAction = bDepthWasCleared ? ERenderTargetLoadAction::ELoad : ERenderTargetLoadAction::EClear; SceneContext.BeginRenderingGBuffer(RHICmdList, (!IsMetalPlatform(ShaderPlatform) ? ERenderTargetLoadAction::ENoAction : ERenderTargetLoadAction::ELoad), DepthLoadAction, BasePassDepthStencilAccess, ViewFamily.EngineShowFlags.ShaderComplexity); } // Wait for Async SSAO before rendering base pass with forward rendering if (IsForwardShadingEnabled(ShaderPlatform)) { GCompositionLighting.GfxWaitForAsyncSSAO(RHICmdList); } GRenderTargetPool.AddPhaseEvent(TEXT("BasePass")); RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_BasePass)); RenderBasePass(RHICmdList, BasePassDepthStencilAccess, ForwardScreenSpaceShadowMask.GetReference(), bDoParallelBasePass, bRenderLightmapDensity); // Release forward screen space shadow mask right after base pass in forward rendering to free resources, such as FastVRAM if (IsForwardShadingEnabled(ShaderPlatform)) { ForwardScreenSpaceShadowMask.SafeRelease(); } RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_AfterBasePass)); ServiceLocalQueue(); // If we ran parallel in the basepass there will be no renderpass at this point. if (bDoParallelBasePass && !bRenderLightmapDensity) { SceneContext.BeginRenderingGBuffer(RHICmdList, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ELoad, BasePassDepthStencilAccess, ViewFamily.EngineShowFlags.ShaderComplexity); } { SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_ViewExtensionPostRenderBasePass); for (int32 ViewExt = 0; ViewExt < ViewFamily.ViewExtensions.Num(); ++ViewExt) { for (int32 ViewIndex = 0; ViewIndex < ViewFamily.Views.Num(); ++ViewIndex) { ViewFamily.ViewExtensions[ViewExt]->PostRenderBasePass_RenderThread(RHICmdList, Views[ViewIndex]); } } } // #todo-renderpasses Should this be further below? if (bRequiresFarZQuadClear) { SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_ClearGBufferAtMaxZ); // Clears view by drawing quad at maximum Z // TODO: if all the platforms have fast color clears, we can replace this with an RHICmdList.Clear. ClearGBufferAtMaxZ(RHICmdList); ServiceLocalQueue(); bRequiresFarZQuadClear = false; } { SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_Resolve_After_Basepass); // Will early return if simple forward SceneContext.FinishGBufferPassAndResolve(RHICmdList); } if (!bAllowReadonlyDepthBasePass) { SceneContext.ResolveSceneDepthTexture(RHICmdList, FResolveRect(0, 0, FamilySize.X, FamilySize.Y)); SceneContext.ResolveSceneDepthToAuxiliaryTexture(RHICmdList); } // BASE PASS ENDS HERE. if (ViewFamily.EngineShowFlags.VisualizeLightCulling) { // clear out emissive and baked lighting (not too efficient but simple and only needed for this debug view) SceneContext.BeginRenderingSceneColor(RHICmdList); DrawClearQuad(RHICmdList, FLinearColor(0, 0, 0, 0)); SceneContext.FinishRenderingSceneColor(RHICmdList); } checkSlow(RHICmdList.IsOutsideRenderPass()); SceneContext.DBufferA.SafeRelease(); SceneContext.DBufferB.SafeRelease(); SceneContext.DBufferC.SafeRelease(); // only temporarily available after early z pass and until base pass check(!SceneContext.DBufferA); check(!SceneContext.DBufferB); check(!SceneContext.DBufferC); if (CVarVirtualTextureLightmaps && CVarVirtualTextureLightmaps->GetValueOnRenderThread()) { // No pass after this can make VT page requests GVirtualTextureFeedback.TransferGPUToCPU( RHICmdList ); } // #todo-renderpass Zfar clear was here. where should it really go? VisualizeVolumetricLightmap(RHICmdList); SceneContext.ResolveSceneDepthToAuxiliaryTexture(RHICmdList); // Occlusion after base pass if (!bOcclusionBeforeBasePass) { // #todo-renderpasses Needs its own renderpass. Does this need more than the depth? if (bIsOcclusionTesting) { RenderOcclusion(RHICmdList); } bool bUseHzbOcclusion = RenderHzb(RHICmdList); SCOPED_GPU_STAT(RHICmdList, HZB); if (bUseHzbOcclusion || bIsOcclusionTesting) { FinishOcclusion(RHICmdList); } if (bIsOcclusionTesting) { FenceOcclusionTests(RHICmdList); } } ServiceLocalQueue(); // End occlusion after base checkSlow(RHICmdList.IsOutsideRenderPass()); if (!bUseGBuffer) { ResolveSceneColor(RHICmdList); } // Shadow and fog after base pass if (!bOcclusionBeforeBasePass) { // Before starting the shadow render, all async task for the shadow Custom data must be completed if (bDoInitViewAftersPrepass && UpdateViewCustomDataEvents.Num() > 0) { QUICK_SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_AsyncUpdateViewCustomData_Wait); FTaskGraphInterface::Get().WaitUntilTasksComplete(UpdateViewCustomDataEvents, ENamedThreads::GetRenderThread()); } RenderShadowDepthMaps(RHICmdList); checkSlow(RHICmdList.IsOutsideRenderPass()); ComputeVolumetricFog(RHICmdList); ServiceLocalQueue(); } // End shadow and fog after base pass checkSlow(RHICmdList.IsOutsideRenderPass()); if(GetCustomDepthPassLocation() == 1) { CSV_SCOPED_TIMING_STAT_EXCLUSIVE(CustomDepthPass); QUICK_SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_CustomDepthPass1); RenderCustomDepthPassAtLocation(RHICmdList, 1); } ServiceLocalQueue(); // If bBasePassCanOutputVelocity is set, basepass fully writes the velocity buffer unless bUseSelectiveBasePassOutputs is enabled. if (bShouldRenderVelocities && (!bBasePassCanOutputVelocity || bUseSelectiveBasePassOutputs)) { // Render the velocities of movable objects RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_Velocity)); RenderVelocities(RHICmdList, SceneContext.SceneVelocity); RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_AfterVelocity)); ServiceLocalQueue(); } #if !UE_BUILD_SHIPPING if (CVarForceBlackVelocityBuffer.GetValueOnRenderThread()) { SceneContext.SceneVelocity = GSystemTextures.BlackDummy; } #endif checkSlow(RHICmdList.IsOutsideRenderPass()); #if RHI_RAYTRACING TRefCountPtr SkyLightRT; TRefCountPtr GlobalIlluminationRT; TRefCountPtr HitDistanceRT; const bool bRayTracingEnabled = IsRayTracingEnabled(); if (bRayTracingEnabled) { for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) { if (Views[ViewIndex].RayTracingRenderMode == ERayTracingRenderMode::PathTracing) { RenderPathTracing(RHICmdList, Views[ViewIndex]); } else if (Views[ViewIndex].RayTracingRenderMode == ERayTracingRenderMode::RayTracingDebug) { RenderRayTracingDebug(RHICmdList, Views[ViewIndex]); } } if (bCanOverlayRayTracingOutput) { // TODO: convert the entire AO and skylight to rendergraph. // SkyLight takes priority over ambient occlusion if (ShouldRenderRayTracingSkyLight(Scene->SkyLight)) { RenderRayTracingSkyLight(RHICmdList, SkyLightRT, HitDistanceRT); } if (ShouldRenderRayTracingGlobalIllumination(Views)) { for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) { RenderRayTracingGlobalIllumination(RHICmdList, Views[ViewIndex], GlobalIlluminationRT, SceneContext.ScreenSpaceAO); } } if (ShouldRenderRayTracingAmbientOcclusion()) { checkSlow(RHICmdList.IsOutsideRenderPass()); checkSlow(RHICmdList.IsOutsideRenderPass()); TRefCountPtr AmbientOcclusionHitDistanceRT; RenderRayTracingAmbientOcclusion(RHICmdList, nullptr, SceneContext.ScreenSpaceAO, AmbientOcclusionHitDistanceRT); int32 DenoiserMode = CVarUseAODenoiser.GetValueOnRenderThread(); if (DenoiserMode != 0) { SCOPED_GPU_STAT(RHICmdList, AmbientOcclusionDenoiser); FRDGBuilder GraphBuilder(RHICmdList); FSceneViewFamilyBlackboard SceneBlackboard; SetupSceneViewFamilyBlackboard(GraphBuilder, &SceneBlackboard); const IScreenSpaceDenoiser* DefaultDenoiser = IScreenSpaceDenoiser::GetDefaultDenoiser(); const IScreenSpaceDenoiser* DenoiserToUse = DenoiserMode == 1 ? DefaultDenoiser : GScreenSpaceDenoiser; IScreenSpaceDenoiser::FAmbientOcclusionRayTracingConfig RayTracingConfig; IScreenSpaceDenoiser::FAmbientOcclusionInputs DenoiserInputs; DenoiserInputs.Mask = GraphBuilder.RegisterExternalTexture(SceneContext.ScreenSpaceAO, TEXT("AOMask")); DenoiserInputs.RayHitDistance = GraphBuilder.RegisterExternalTexture(AmbientOcclusionHitDistanceRT, TEXT("AOHitDistance")); FViewInfo& View = Views[0]; { RDG_EVENT_SCOPE(GraphBuilder, "%s%s(AmbientOcclusion) %dx%d", DenoiserToUse != DefaultDenoiser ? TEXT("ThirdParty ") : TEXT(""), DenoiserToUse->GetDebugName(), View.ViewRect.Width(), View.ViewRect.Height()); IScreenSpaceDenoiser::FAmbientOcclusionOutputs DenoiserOutputs = DenoiserToUse->DenoiseAmbientOcclusion( GraphBuilder, View, &View.PrevViewInfo, SceneBlackboard, DenoiserInputs, RayTracingConfig); GraphBuilder.QueueTextureExtraction(DenoiserOutputs.AmbientOcclusionMask, &SceneContext.ScreenSpaceAO); } GraphBuilder.Execute(); } } } } #endif // RHI_RAYTRACING checkSlow(RHICmdList.IsOutsideRenderPass()); // Copy lighting channels out of stencil before deferred decals which overwrite those values CopyStencilToLightingChannelTexture(RHICmdList); checkSlow(RHICmdList.IsOutsideRenderPass()); if(!IsForwardShadingEnabled(ShaderPlatform)) { GCompositionLighting.GfxWaitForAsyncSSAO(RHICmdList); } else { // Release SSAO texture and HZB texture earlier to free resources, such as FastVRAM. SceneContext.ScreenSpaceAO.SafeRelease(); SceneContext.bScreenSpaceAOIsValid = false; for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) { SCOPED_CONDITIONAL_DRAW_EVENTF(RHICmdList, EventView, Views.Num() > 1, TEXT("View%d"), ViewIndex); FViewInfo& View = Views[ViewIndex]; View.HZB.SafeRelease(); } } checkSlow(RHICmdList.IsOutsideRenderPass()); // Pre-lighting composition lighting stage // e.g. deferred decals, SSAO if (FeatureLevel >= ERHIFeatureLevel::SM4) { CSV_SCOPED_TIMING_STAT_EXCLUSIVE(AfterBasePass); SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_AfterBasePass); GRenderTargetPool.AddPhaseEvent(TEXT("AfterBasePass")); if (!IsForwardShadingEnabled(ShaderPlatform)) { SceneContext.ResolveSceneDepthTexture(RHICmdList, FResolveRect(0, 0, FamilySize.X, FamilySize.Y)); SceneContext.ResolveSceneDepthToAuxiliaryTexture(RHICmdList); } for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) { SCOPED_CONDITIONAL_DRAW_EVENTF(RHICmdList, EventView, Views.Num() > 1, TEXT("View%d"), ViewIndex); Scene->UniformBuffers.UpdateViewUniformBuffer(Views[ViewIndex]); GCompositionLighting.ProcessAfterBasePass(RHICmdList, Views[ViewIndex]); } ServiceLocalQueue(); } // TODO: Could entirely remove this by using STENCIL_SANDBOX_BIT in ShadowRendering.cpp and DistanceFieldSurfaceCacheLighting.cpp if (!IsForwardShadingEnabled(ShaderPlatform)) { // Clear stencil to 0 now that deferred decals are done using what was setup in the base pass // Shadow passes and other users of stencil assume it is cleared to 0 going in FRHIRenderPassInfo RPInfo(SceneContext.GetSceneDepthSurface(), EDepthStencilTargetActions::ClearStencilDontLoadDepth_StoreStencilNotDepth); RPInfo.DepthStencilRenderTarget.ExclusiveDepthStencil = FExclusiveDepthStencil::DepthNop_StencilWrite; RHICmdList.BeginRenderPass(RPInfo, TEXT("ClearStencilFromBasePass")); RHICmdList.EndRenderPass(); RHICmdList.TransitionResource(EResourceTransitionAccess::EReadable, SceneContext.GetSceneDepthSurface()); } checkSlow(RHICmdList.IsOutsideRenderPass()); // Render lighting. if (bRenderDeferredLighting) { CSV_SCOPED_TIMING_STAT_EXCLUSIVE(RenderLighting); SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_Lighting); GRenderTargetPool.AddPhaseEvent(TEXT("Lighting")); // These modulate the scenecolor output from the basepass, which is assumed to be indirect lighting RenderIndirectCapsuleShadows( RHICmdList, SceneContext.GetSceneColorSurface(), SceneContext.bScreenSpaceAOIsValid ? SceneContext.ScreenSpaceAO->GetRenderTargetItem().TargetableTexture : NULL); TRefCountPtr DynamicBentNormalAO; // These modulate the scenecolor output from the basepass, which is assumed to be indirect lighting RenderDFAOAsIndirectShadowing(RHICmdList, SceneContext.SceneVelocity, DynamicBentNormalAO); // Clear the translucent lighting volumes before we accumulate if ((GbEnableAsyncComputeTranslucencyLightingVolumeClear && GSupportsEfficientAsyncCompute) == false) { for(int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex) { ClearTranslucentVolumeLighting(RHICmdList, ViewIndex); } } RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_Lighting)); RenderLights(RHICmdList); RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_AfterLighting)); ServiceLocalQueue(); checkSlow(RHICmdList.IsOutsideRenderPass()); GRenderTargetPool.AddPhaseEvent(TEXT("AfterRenderLights")); for(int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex) { InjectAmbientCubemapTranslucentVolumeLighting(RHICmdList, Views[ViewIndex], ViewIndex); } ServiceLocalQueue(); for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex) { // Filter the translucency lighting volume now that it is complete FilterTranslucentVolumeLighting(RHICmdList, Views[ViewIndex], ViewIndex); } ServiceLocalQueue(); checkSlow(RHICmdList.IsOutsideRenderPass()); // Pre-lighting composition lighting stage // e.g. LPV indirect for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex) { FViewInfo& View = Views[ViewIndex]; if(IsLpvIndirectPassRequired(View)) { SCOPED_CONDITIONAL_DRAW_EVENTF(RHICmdList, EventView,Views.Num() > 1, TEXT("View%d"), ViewIndex); GCompositionLighting.ProcessLpvIndirect(RHICmdList, View); ServiceLocalQueue(); } } checkSlow(RHICmdList.IsOutsideRenderPass()); // Render diffuse sky lighting and reflections that only operate on opaque pixels RenderDeferredReflectionsAndSkyLighting(RHICmdList, DynamicBentNormalAO, SceneContext.SceneVelocity); DynamicBentNormalAO = NULL; // SSS need the SceneColor finalized as an SRV. ResolveSceneColor(RHICmdList); ServiceLocalQueue(); // Post-lighting composition lighting stage // e.g. ScreenSpaceSubsurfaceScattering for(int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex) { SCOPED_CONDITIONAL_DRAW_EVENTF(RHICmdList, EventView,Views.Num() > 1, TEXT("View%d"), ViewIndex); GCompositionLighting.ProcessAfterLighting(RHICmdList, Views[ViewIndex]); } #if RHI_RAYTRACING if (SkyLightRT) { CompositeRayTracingSkyLight(RHICmdList, SkyLightRT, HitDistanceRT); } if (GlobalIlluminationRT) { for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex) { CompositeGlobalIllumination(RHICmdList, Views[ViewIndex], GlobalIlluminationRT); } } #endif // RHI_RAYTRACING ServiceLocalQueue(); } checkSlow(RHICmdList.IsOutsideRenderPass()); FLightShaftsOutput LightShaftOutput; // Draw Lightshafts if (ViewFamily.EngineShowFlags.LightShafts) { SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_RenderLightShaftOcclusion); RenderLightShaftOcclusion(RHICmdList, LightShaftOutput); ServiceLocalQueue(); } checkSlow(RHICmdList.IsOutsideRenderPass()); // Draw atmosphere if (bCanOverlayRayTracingOutput && ShouldRenderAtmosphere(ViewFamily)) { SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_RenderAtmosphere); if (Scene->AtmosphericFog) { // Update RenderFlag based on LightShaftTexture is valid or not if (LightShaftOutput.LightShaftOcclusion) { Scene->AtmosphericFog->RenderFlag &= EAtmosphereRenderFlag::E_LightShaftMask; } else { Scene->AtmosphericFog->RenderFlag |= EAtmosphereRenderFlag::E_DisableLightShaft; } #if WITH_EDITOR if (Scene->bIsEditorScene) { // Precompute Atmospheric Textures Scene->AtmosphericFog->PrecomputeTextures(RHICmdList, Views.GetData(), &ViewFamily); } #endif RenderAtmosphere(RHICmdList, LightShaftOutput); ServiceLocalQueue(); } } checkSlow(RHICmdList.IsOutsideRenderPass()); GRenderTargetPool.AddPhaseEvent(TEXT("Fog")); // Draw fog. if (bCanOverlayRayTracingOutput && ShouldRenderFog(ViewFamily)) { CSV_SCOPED_TIMING_STAT_EXCLUSIVE(RenderFog); SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_RenderFog); RenderFog(RHICmdList, LightShaftOutput); ServiceLocalQueue(); } checkSlow(RHICmdList.IsOutsideRenderPass()); if (RendererModule.HasPostOpaqueExtentions()) { FSceneTexturesUniformParameters SceneTextureParameters; SetupSceneTextureUniformParameters(SceneContext, FeatureLevel, ESceneTextureSetupMode::SceneDepth | ESceneTextureSetupMode::GBuffers, SceneTextureParameters); TUniformBufferRef SceneTextureUniformBuffer = TUniformBufferRef::CreateUniformBufferImmediate(SceneTextureParameters, UniformBuffer_SingleFrame); SceneContext.BeginRenderingSceneColor(RHICmdList, (!IsMetalPlatform(ShaderPlatform) ? ESimpleRenderTargetMode::EUninitializedColorExistingDepth : ESimpleRenderTargetMode::EExistingColorAndDepth)); for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex) { const FViewInfo& View = Views[ViewIndex]; RHICmdList.SetViewport(View.ViewRect.Min.X, View.ViewRect.Min.Y, 0.0f, View.ViewRect.Max.X, View.ViewRect.Max.Y, 1.0f); RendererModule.RenderPostOpaqueExtensions(View, RHICmdList, SceneContext, SceneTextureUniformBuffer); } SceneContext.FinishRenderingSceneColor(RHICmdList); } checkSlow(RHICmdList.IsOutsideRenderPass()); UnbindRenderTargets(RHICmdList); // Notify the FX system that opaque primitives have been rendered and we now have a valid depth buffer. if (Scene->FXSystem && Views.IsValidIndex(0) && bAllowGPUParticleSceneUpdate) { CSV_SCOPED_TIMING_STAT_EXCLUSIVE(RenderOpaqueFX); SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_FXSystem_PostRenderOpaque); FSceneTexturesUniformParameters SceneTextureParameters; SetupSceneTextureUniformParameters(SceneContext, FeatureLevel, ESceneTextureSetupMode::SceneDepth | ESceneTextureSetupMode::GBuffers, SceneTextureParameters); TUniformBufferRef SceneTextureUniformBuffer = TUniformBufferRef::CreateUniformBufferImmediate(SceneTextureParameters, UniformBuffer_SingleFrame); Scene->FXSystem->PostRenderOpaque( RHICmdList, Views[0].ViewUniformBuffer, &FSceneTexturesUniformParameters::StaticStructMetadata, SceneTextureUniformBuffer.GetReference() ); ServiceLocalQueue(); } // No longer needed, release LightShaftOutput.LightShaftOcclusion = NULL; checkSlow(RHICmdList.IsOutsideRenderPass()); GRenderTargetPool.AddPhaseEvent(TEXT("Translucency")); // Draw translucency. if (bCanOverlayRayTracingOutput && ViewFamily.EngineShowFlags.Translucency) { CSV_SCOPED_TIMING_STAT_EXCLUSIVE(RenderTranslucency); SCOPE_CYCLE_COUNTER(STAT_TranslucencyDrawTime); RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_Translucency)); #if RHI_RAYTRACING bool bAnyViewWithRaytracingTranslucency = false; for (int32 ViewIndex = 0, Num = Views.Num(); ViewIndex < Num; ViewIndex++) { const FViewInfo& View = Views[ViewIndex]; //#dxr_todo: multiview case bAnyViewWithRaytracingTranslucency = bAnyViewWithRaytracingTranslucency || (View.FinalPostProcessSettings.TranslucencyType == ETranslucencyType::RayTracing); } int32 RtTranslucencyCvar = CVarRayTracingTranslucency.GetValueOnRenderThread(); int32 bRaytracedTranslucency = RtTranslucencyCvar > -1 ? RtTranslucencyCvar : (bAnyViewWithRaytracingTranslucency? 1 : 0); if (bRayTracingEnabled && bRaytracedTranslucency > 0) { ResolveSceneColor(RHICmdList); RenderRayTracingTranslucency(RHICmdList); } else #endif { // For now there is only one resolve for all translucency passes. This can be changed by enabling the resolve in RenderTranslucency() TRefCountPtr SceneColorCopy; ConditionalResolveSceneColorForTranslucentMaterials(RHICmdList, SceneColorCopy); if (ViewFamily.AllowTranslucencyAfterDOF()) { RenderTranslucency(RHICmdList, ETranslucencyPass::TPT_StandardTranslucency, SceneColorCopy); // Translucency after DOF is rendered now, but stored in the separate translucency RT for later use. RenderTranslucency(RHICmdList, ETranslucencyPass::TPT_TranslucencyAfterDOF, SceneColorCopy); } else // Otherwise render translucent primitives in a single bucket. { RenderTranslucency(RHICmdList, ETranslucencyPass::TPT_AllTranslucency, SceneColorCopy); } ServiceLocalQueue(); static const auto DisableDistortionCVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.DisableDistortion")); const bool bAllowDistortion = DisableDistortionCVar->GetValueOnAnyThread() != 1; if (GetRefractionQuality(ViewFamily) > 0 && bAllowDistortion) { // To apply refraction effect by distorting the scene color. // After non separate translucency as that is considered at scene depth anyway // It allows skybox translucency (set to non separate translucency) to be refracted. RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_RenderDistortion)); RenderDistortion(RHICmdList); ServiceLocalQueue(); } RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_AfterTranslucency)); } } checkSlow(RHICmdList.IsOutsideRenderPass()); if (bCanOverlayRayTracingOutput && ViewFamily.EngineShowFlags.LightShafts) { SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_RenderLightShaftBloom); RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_LightShaftBloom)); RenderLightShaftBloom(RHICmdList); ServiceLocalQueue(); } for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex) { const FViewInfo& View = Views[ViewIndex]; RHICmdList.SetViewport(View.ViewRect.Min.X, View.ViewRect.Min.Y, 0.0f, View.ViewRect.Max.X, View.ViewRect.Max.Y, 1.0f); RendererModule.RenderOverlayExtensions(View, RHICmdList, SceneContext); } if (ViewFamily.EngineShowFlags.VisualizeDistanceFieldAO) { // 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; TRefCountPtr DummyOutput; RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_RenderDistanceFieldLighting)); RenderDistanceFieldLighting(RHICmdList, FDistanceFieldAOParameters(OcclusionMaxDistance), SceneContext.SceneVelocity, DummyOutput, false, ViewFamily.EngineShowFlags.VisualizeDistanceFieldAO); ServiceLocalQueue(); } checkSlow(RHICmdList.IsOutsideRenderPass()); // Draw visualizations just before use to avoid target contamination if (ViewFamily.EngineShowFlags.VisualizeMeshDistanceFields || ViewFamily.EngineShowFlags.VisualizeGlobalDistanceField) { RenderMeshDistanceFieldVisualization(RHICmdList, FDistanceFieldAOParameters(Scene->DefaultMaxDistanceFieldOcclusionDistance)); ServiceLocalQueue(); } if (ViewFamily.EngineShowFlags.StationaryLightOverlap && FeatureLevel >= ERHIFeatureLevel::SM4 && bUseGBuffer) { RenderStationaryLightOverlap(RHICmdList); ServiceLocalQueue(); } // Resolve the scene color for post processing. ResolveSceneColor(RHICmdList); GetRendererModule().RenderPostResolvedSceneColorExtension(RHICmdList, SceneContext); CopySceneCaptureComponentToTarget(RHICmdList); // Finish rendering for each view. if (ViewFamily.bResolveScene) { SCOPED_DRAW_EVENT(RHICmdList, PostProcessing); SCOPED_GPU_STAT(RHICmdList, Postprocessing); SCOPE_CYCLE_COUNTER(STAT_FinishRenderViewTargetTime); RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_PostProcessing)); for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) { SCOPED_CONDITIONAL_DRAW_EVENTF(RHICmdList, EventView, Views.Num() > 1, TEXT("View%d"), ViewIndex); if (ViewFamily.UseDebugViewPS()) { DoDebugViewModePostProcessing(RHICmdList, Views[ViewIndex], SceneContext.SceneVelocity); } else { GPostProcessing.Process(RHICmdList, Views[ ViewIndex ], SceneContext.SceneVelocity); } } // End of frame, we don't need it anymore FSceneRenderTargets::Get(RHICmdList).FreeDownsampledTranslucencyDepth(); // we rendered to it during the frame, seems we haven't made use of it, because it should be released check(!FSceneRenderTargets::Get(RHICmdList).SeparateTranslucencyRT); } else { // Release the original reference on the scene render targets SceneContext.AdjustGBufferRefCount(RHICmdList, -1); } #if WITH_MGPU DoCrossGPUTransfers(RHICmdList, RenderTargetGPUMask); #endif //grab the new transform out of the proxies for next frame SceneContext.SceneVelocity.SafeRelease(); // Invalidate the lighting channels SceneContext.LightingChannels.SafeRelease(); { SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_RenderFinish); RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_RenderFinish)); RenderFinish(RHICmdList); RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_AfterFrame)); } ServiceLocalQueue(); } /** A simple pixel shader used on PC to read scene depth from scene color alpha and write it to a downsized depth buffer. */ class FDownsampleSceneDepthPS : public FGlobalShader { DECLARE_SHADER_TYPE(FDownsampleSceneDepthPS,Global); public: static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM4); } FDownsampleSceneDepthPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer): FGlobalShader(Initializer) { SceneTextureParameters.Bind(Initializer); ProjectionScaleBias.Bind(Initializer.ParameterMap,TEXT("ProjectionScaleBias")); SourceTexelOffsets01.Bind(Initializer.ParameterMap,TEXT("SourceTexelOffsets01")); SourceTexelOffsets23.Bind(Initializer.ParameterMap,TEXT("SourceTexelOffsets23")); UseMaxDepth.Bind(Initializer.ParameterMap, TEXT("UseMaxDepth")); SourceMaxUVParameter.Bind(Initializer.ParameterMap, TEXT("SourceMaxUV")); } FDownsampleSceneDepthPS() {} void SetParameters(FRHICommandList& RHICmdList, const FSceneView& View, bool bUseMaxDepth, FIntPoint ViewMax) { FGlobalShader::SetParameters(RHICmdList, GetPixelShader(), View.ViewUniformBuffer); FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList); // Used to remap view space Z (which is stored in scene color alpha) into post projection z and w so we can write z/w into the downsized depth buffer const FVector2D ProjectionScaleBiasValue(View.ViewMatrices.GetProjectionMatrix().M[2][2], View.ViewMatrices.GetProjectionMatrix().M[3][2]); SetShaderValue(RHICmdList, GetPixelShader(), ProjectionScaleBias, ProjectionScaleBiasValue); SetShaderValue(RHICmdList, GetPixelShader(), UseMaxDepth, (bUseMaxDepth ? 1.0f : 0.0f)); FIntPoint BufferSize = SceneContext.GetBufferSizeXY(); const uint32 DownsampledBufferSizeX = BufferSize.X / SceneContext.GetSmallColorDepthDownsampleFactor(); const uint32 DownsampledBufferSizeY = BufferSize.Y / SceneContext.GetSmallColorDepthDownsampleFactor(); // Offsets of the four full resolution pixels corresponding with a low resolution pixel const FVector4 Offsets01(0.0f, 0.0f, 1.0f / DownsampledBufferSizeX, 0.0f); SetShaderValue(RHICmdList, GetPixelShader(), SourceTexelOffsets01, Offsets01); const FVector4 Offsets23(0.0f, 1.0f / DownsampledBufferSizeY, 1.0f / DownsampledBufferSizeX, 1.0f / DownsampledBufferSizeY); SetShaderValue(RHICmdList, GetPixelShader(), SourceTexelOffsets23, Offsets23); SceneTextureParameters.Set(RHICmdList, GetPixelShader(), View.FeatureLevel, ESceneTextureSetupMode::All); // Set MaxUV, so we won't sample outside of a valid texture region. FVector2D const SourceMaxUV((ViewMax.X - 0.5f) / BufferSize.X, (ViewMax.Y - 0.5f) / BufferSize.Y); SetShaderValue(RHICmdList, GetPixelShader(), SourceMaxUVParameter, SourceMaxUV); } virtual bool Serialize(FArchive& Ar) override { bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar); Ar << ProjectionScaleBias; Ar << SourceTexelOffsets01; Ar << SourceTexelOffsets23; Ar << SceneTextureParameters; Ar << UseMaxDepth; Ar << SourceMaxUVParameter; return bShaderHasOutdatedParameters; } FShaderParameter ProjectionScaleBias; FShaderParameter SourceTexelOffsets01; FShaderParameter SourceTexelOffsets23; FShaderParameter SourceMaxUVParameter; FSceneTextureShaderParameters SceneTextureParameters; FShaderParameter UseMaxDepth; }; IMPLEMENT_SHADER_TYPE(,FDownsampleSceneDepthPS,TEXT("/Engine/Private/DownsampleDepthPixelShader.usf"),TEXT("Main"),SF_Pixel); /** Updates the downsized depth buffer with the current full resolution depth buffer. */ void FDeferredShadingSceneRenderer::UpdateDownsampledDepthSurface(FRHICommandList& RHICmdList) { FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList); if (SceneContext.UseDownsizedOcclusionQueries() && (FeatureLevel >= ERHIFeatureLevel::SM4)) { RHICmdList.TransitionResource( EResourceTransitionAccess::EReadable, SceneContext.GetSceneDepthSurface() ); for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) { const FViewInfo& View = Views[ViewIndex]; DownsampleDepthSurface(RHICmdList, SceneContext.GetSmallDepthSurface(), View, 1.0f / SceneContext.GetSmallColorDepthDownsampleFactor(), true); } } } /** Downsample the scene depth with a specified scale factor to a specified render target */ void FDeferredShadingSceneRenderer::DownsampleDepthSurface(FRHICommandList& RHICmdList, const FTexture2DRHIRef& RenderTarget, const FViewInfo& View, float ScaleFactor, bool bUseMaxDepth) { FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList); FRHIRenderPassInfo RPInfo; RPInfo.DepthStencilRenderTarget.Action = EDepthStencilTargetActions::LoadDepthStencil_StoreDepthStencil; RPInfo.DepthStencilRenderTarget.DepthStencilTarget = RenderTarget; RPInfo.DepthStencilRenderTarget.ExclusiveDepthStencil = FExclusiveDepthStencil::DepthWrite_StencilWrite; RHICmdList.BeginRenderPass(RPInfo, TEXT("DownsampleDepth")); { SCOPED_DRAW_EVENT(RHICmdList, DownsampleDepth); // Set shaders and texture TShaderMapRef ScreenVertexShader(View.ShaderMap); TShaderMapRef PixelShader(View.ShaderMap); extern TGlobalResource GFilterVertexDeclaration; FGraphicsPipelineStateInitializer GraphicsPSOInit; RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit); GraphicsPSOInit.BlendState = TStaticBlendState::GetRHI(); GraphicsPSOInit.RasterizerState = TStaticRasterizerState::GetRHI(); GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState::GetRHI(); GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI; GraphicsPSOInit.BoundShaderState.VertexShaderRHI = GETSAFERHISHADER_VERTEX(*ScreenVertexShader); GraphicsPSOInit.BoundShaderState.PixelShaderRHI = GETSAFERHISHADER_PIXEL(*PixelShader); GraphicsPSOInit.PrimitiveType = PT_TriangleList; SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit); PixelShader->SetParameters(RHICmdList, View, bUseMaxDepth, View.ViewRect.Max); const uint32 DownsampledX = FMath::TruncToInt(View.ViewRect.Min.X * ScaleFactor); const uint32 DownsampledY = FMath::TruncToInt(View.ViewRect.Min.Y * ScaleFactor); const uint32 DownsampledSizeX = FMath::TruncToInt(View.ViewRect.Width() * ScaleFactor); const uint32 DownsampledSizeY = FMath::TruncToInt(View.ViewRect.Height() * ScaleFactor); RHICmdList.SetViewport(DownsampledX, DownsampledY, 0.0f, DownsampledX + DownsampledSizeX, DownsampledY + DownsampledSizeY, 1.0f); DrawRectangle( RHICmdList, 0, 0, DownsampledSizeX, DownsampledSizeY, View.ViewRect.Min.X, View.ViewRect.Min.Y, View.ViewRect.Width(), View.ViewRect.Height(), FIntPoint(DownsampledSizeX, DownsampledSizeY), SceneContext.GetBufferSizeXY(), *ScreenVertexShader, EDRF_UseTriangleOptimization); } RHICmdList.EndRenderPass(); } /** */ class FCopyStencilToLightingChannelsPS : public FGlobalShader { DECLARE_SHADER_TYPE(FCopyStencilToLightingChannelsPS,Global); public: static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM4); } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters,OutEnvironment); OutEnvironment.SetDefine(TEXT("STENCIL_LIGHTING_CHANNELS_SHIFT"), STENCIL_LIGHTING_CHANNELS_BIT_ID); OutEnvironment.SetRenderTargetOutputFormat(0, PF_R16_UINT); } FCopyStencilToLightingChannelsPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer): FGlobalShader(Initializer) { SceneStencilTexture.Bind(Initializer.ParameterMap,TEXT("SceneStencilTexture")); } FCopyStencilToLightingChannelsPS() {} void SetParameters(FRHICommandList& RHICmdList, const FSceneView& View) { FGlobalShader::SetParameters(RHICmdList, GetPixelShader(), View.ViewUniformBuffer); FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList); SetSRVParameter(RHICmdList, GetPixelShader(), SceneStencilTexture, SceneContext.SceneStencilSRV); } virtual bool Serialize(FArchive& Ar) override { bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar); Ar << SceneStencilTexture; return bShaderHasOutdatedParameters; } FShaderResourceParameter SceneStencilTexture; }; IMPLEMENT_SHADER_TYPE(,FCopyStencilToLightingChannelsPS,TEXT("/Engine/Private/DownsampleDepthPixelShader.usf"),TEXT("CopyStencilToLightingChannelsPS"),SF_Pixel); void FDeferredShadingSceneRenderer::CopyStencilToLightingChannelTexture(FRHICommandList& RHICmdList) { bool bAnyViewUsesLightingChannels = false; for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) { const FViewInfo& View = Views[ViewIndex]; bAnyViewUsesLightingChannels = bAnyViewUsesLightingChannels || View.bUsesLightingChannels; } if (bAnyViewUsesLightingChannels) { FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList); SCOPED_DRAW_EVENT(RHICmdList, CopyStencilToLightingChannels); RHICmdList.TransitionResource(EResourceTransitionAccess::EReadable, SceneContext.GetSceneDepthTexture()); SceneContext.AllocateLightingChannelTexture(RHICmdList); // Set the light attenuation surface as the render target, and the scene depth buffer as the depth-stencil surface. FRHIRenderPassInfo RPInfo(SceneContext.LightingChannels->GetRenderTargetItem().TargetableTexture, ERenderTargetActions::Load_Store); TransitionRenderPassTargets(RHICmdList, RPInfo); RHICmdList.BeginRenderPass(RPInfo, TEXT("CopyStencilToLightingChannel")); { FGraphicsPipelineStateInitializer GraphicsPSOInit; RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit); GraphicsPSOInit.BlendState = TStaticBlendState::GetRHI(); GraphicsPSOInit.RasterizerState = TStaticRasterizerState::GetRHI(); GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState::GetRHI(); extern TGlobalResource GFilterVertexDeclaration; GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI; for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) { const FViewInfo& View = Views[ViewIndex]; // Set shaders and texture TShaderMapRef ScreenVertexShader(View.ShaderMap); TShaderMapRef PixelShader(View.ShaderMap); GraphicsPSOInit.BoundShaderState.VertexShaderRHI = GETSAFERHISHADER_VERTEX(*ScreenVertexShader); GraphicsPSOInit.BoundShaderState.PixelShaderRHI = GETSAFERHISHADER_PIXEL(*PixelShader); GraphicsPSOInit.PrimitiveType = PT_TriangleList; SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit); PixelShader->SetParameters(RHICmdList, View); RHICmdList.SetViewport(View.ViewRect.Min.X, View.ViewRect.Min.Y, 0.0f, View.ViewRect.Min.X + View.ViewRect.Width(), View.ViewRect.Min.Y + View.ViewRect.Height(), 1.0f); DrawRectangle( RHICmdList, 0, 0, View.ViewRect.Width(), View.ViewRect.Height(), View.ViewRect.Min.X, View.ViewRect.Min.Y, View.ViewRect.Width(), View.ViewRect.Height(), FIntPoint(View.ViewRect.Width(), View.ViewRect.Height()), SceneContext.GetBufferSizeXY(), *ScreenVertexShader, EDRF_UseTriangleOptimization); } } RHICmdList.EndRenderPass(); RHICmdList.CopyToResolveTarget(SceneContext.LightingChannels->GetRenderTargetItem().TargetableTexture, SceneContext.LightingChannels->GetRenderTargetItem().TargetableTexture, FResolveParams()); } else { FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList); ensure(SceneContext.LightingChannels.IsValid() == false); } } bool FDeferredShadingSceneRenderer::CanOverlayRayTracingOutput(void) const { #if RHI_RAYTRACING // #dxr_todo: what about multi-view case? return (Views[0].RayTracingRenderMode != ERayTracingRenderMode::PathTracing) && (Views[0].RayTracingRenderMode != ERayTracingRenderMode::RayTracingDebug); #else // RHI_RAYTRACING return true; #endif // RHI_RAYTRACING }