// Copyright Epic Games, Inc. All Rights Reserved. #include "PostProcess/PostProcessing.h" #include "PostProcess/PostProcessAA.h" #if WITH_EDITOR #include "PostProcess/PostProcessBufferInspector.h" #endif #include "PostProcess/DiaphragmDOF.h" #include "PostProcess/PostProcessMaterial.h" #include "PostProcess/PostProcessWeightedSampleSum.h" #include "PostProcess/PostProcessBloomSetup.h" #include "PostProcess/PostProcessMobile.h" #include "PostProcess/PostProcessDownsample.h" #include "PostProcess/PostProcessHistogram.h" #include "PostProcess/PostProcessLocalExposure.h" #include "PostProcess/PostProcessVisualizeHDR.h" #include "PostProcess/PostProcessVisualizeLocalExposure.h" #include "PostProcess/VisualizeShadingModels.h" #include "PostProcess/PostProcessSelectionOutline.h" #include "PostProcess/PostProcessVisualizeLevelInstance.h" #include "PostProcess/PostProcessGBufferHints.h" #include "PostProcess/PostProcessVisualizeBuffer.h" #include "PostProcess/PostProcessVisualizeNanite.h" #include "PostProcess/PostProcessEyeAdaptation.h" #include "PostProcess/PostProcessTonemap.h" #include "PostProcess/PostProcessLensFlares.h" #include "PostProcess/PostProcessBokehDOF.h" #include "PostProcess/PostProcessCombineLUTs.h" #include "PostProcess/PostProcessDeviceEncodingOnly.h" #include "PostProcess/TemporalAA.h" #include "PostProcess/PostProcessMotionBlur.h" #include "PostProcess/PostProcessDOF.h" #include "PostProcess/PostProcessUpscale.h" #include "PostProcess/PostProcessHMD.h" #include "PostProcess/AlphaInvert.h" #include "PostProcess/PostProcessVisualizeComplexity.h" #include "PostProcess/PostProcessVisualizeVirtualTexture.h" #if UE_ENABLE_DEBUG_DRAWING #include "PostProcess/PostProcessCompositeDebugPrimitives.h" #endif #if WITH_EDITOR #include "PostProcess/PostProcessCompositeEditorPrimitives.h" #endif #include "PostProcess/PostProcessTestImage.h" #include "PostProcess/PostProcessVisualizeCalibrationMaterial.h" #include "PostProcess/PostProcessFFTBloom.h" #include "PostProcess/PostProcessStreamingAccuracyLegend.h" #include "PostProcess/PostProcessSubsurface.h" #include "PostProcess/VisualizeMotionVectors.h" #include "Rendering/MotionVectorSimulation.h" #include "ShaderPrint.h" #include "DataDrivenShaderPlatformInfo.h" #include "HairStrands/HairStrandsComposition.h" #include "HairStrands/HairStrandsUtils.h" #include "HighResScreenshot.h" #include "IHeadMountedDisplay.h" #include "IXRTrackingSystem.h" #include "DeferredShadingRenderer.h" #include "MobileSeparateTranslucencyPass.h" #include "MobileDistortionPass.h" #include "ScenePrivate.h" #include "SceneTextureParameters.h" #include "PixelShaderUtils.h" #include "ScreenSpaceRayTracing.h" #include "SceneViewExtension.h" #include "FXSystem.h" #include "SkyAtmosphereRendering.h" #include "Substrate/Substrate.h" #include "TemporalUpscaler.h" #include "VirtualShadowMaps/VirtualShadowMapArray.h" #include "Lumen/LumenVisualize.h" #include "RectLightTextureManager.h" #include "IESTextureManager.h" #include "UnrealEngine.h" #include "IlluminanceMeter.h" #include "SparseVolumeTexture/SparseVolumeTextureStreamingVisualize.h" #include "CanvasItem.h" #include "MobileSSR.h" #include "Materials/MaterialRenderProxy.h" #include "CustomRenderPassSceneCapture.h" #include "GPUSkinCache.h" #include "VT/VirtualTextureFeedbackResource.h" #include "VT/VirtualTextureVisualizationData.h" bool IsMobileEyeAdaptationEnabled(const FViewInfo& View); bool IsValidBloomSetupVariation(bool bUseBloom, bool bUseSun, bool bUseDof, bool bUseEyeAdaptation); extern FScreenPassTexture AddVisualizeLightGridPass(FRDGBuilder& GraphBuilder, const FViewInfo& View, FScreenPassTexture ScreenPassSceneColor, FScreenPassTexture SceneDepthTexture); extern bool ShouldVisualizeLightGrid(); namespace { TAutoConsoleVariable CVarDepthOfFieldNearBlurSizeThreshold( TEXT("r.DepthOfField.NearBlurSizeThreshold"), 0.01f, TEXT("Sets the minimum near blur size before the effect is forcably disabled. Currently only affects Gaussian DOF.\n") TEXT(" (default: 0.01)"), ECVF_RenderThreadSafe); TAutoConsoleVariable CVarDepthOfFieldMaxSize( TEXT("r.DepthOfField.MaxSize"), 100.0f, TEXT("Allows to clamp the gaussian depth of field radius (for better performance), default: 100"), ECVF_Scalability | ECVF_RenderThreadSafe); TAutoConsoleVariable CVarBloomApplyLocalExposure( TEXT("r.Bloom.ApplyLocalExposure"), true, TEXT("Whether to apply local exposure when calculating bloom, default: true"), ECVF_Scalability | ECVF_RenderThreadSafe); static bool GPostProcessingPropagateAlpha = false; /** * NOTE (5.5): * r.PostProcessing.PropagateAlpha has been converted back to a boolean. In order to prevent silent failures * with IConsoleManager::Get().FindTConsoleVariableDataInt returning 0 with a boolean cvar set to True, we now use * a FAutoConsoleVariableRef which will warn licensees with typed-access at runtime, see IConsoleObject::AsVariableBool() * or IConsoleObject::AsVariableInt(). However both CVar->GetBool() & CVar->GetInt() will continue to work, assuming > 0 * or EAlphaChannelMode::Type comparisons were used. */ FAutoConsoleVariableRef CVarPostProcessingPropagateAlpha( TEXT("r.PostProcessing.PropagateAlpha"), GPostProcessingPropagateAlpha, TEXT("Enforce alpha in scene color (overriding r.SceneColorFormat if necessary) and propagate it through the renderer's post-processing chain, default: false"), ECVF_RenderThreadSafe); TAutoConsoleVariable CVarPostProcessingPreferCompute( TEXT("r.PostProcessing.PreferCompute"), 0, TEXT("Will use compute shaders for post processing where implementations available."), ECVF_RenderThreadSafe); TAutoConsoleVariable CVarPostProcessingQuarterResolutionDownsample( TEXT("r.PostProcessing.QuarterResolutionDownsample"), 0, TEXT("Uses quarter resolution downsample instead of half resolution to feed into exposure / bloom."), ECVF_RenderThreadSafe); TAutoConsoleVariable CVarDownsampleQuality( TEXT("r.PostProcessing.DownsampleQuality"), 0, TEXT("Defines the quality used for downsampling to half or quarter res the scene color in post processing chain.\n") TEXT(" 0: low quality (default)\n") TEXT(" 1: high quality\n"), ECVF_Scalability | ECVF_RenderThreadSafe); TAutoConsoleVariable CVarDownsampleChainQuality( TEXT("r.PostProcessing.DownsampleChainQuality"), 1, TEXT("Defines the quality used for downsampling to the scene color in scene color chains.\n") TEXT(" 0: low quality\n") TEXT(" 1: high quality (default)\n"), ECVF_Scalability | ECVF_RenderThreadSafe); #if !(UE_BUILD_SHIPPING) TAutoConsoleVariable CVarPostProcessingForceAsyncDispatch( TEXT("r.PostProcessing.ForceAsyncDispatch"), 0, TEXT("Will force asynchronous dispatch for post processing compute shaders where implementations available.\n") TEXT("Only available for testing in non-shipping builds."), ECVF_RenderThreadSafe); #endif #if WITH_EDITOR TAutoConsoleVariable CVarGBufferPicking( TEXT("r.PostProcessing.GBufferPicking"), 0, TEXT("Evaluate GBuffer value for debugging purpose."), ECVF_RenderThreadSafe); #endif #if !(UE_BUILD_SHIPPING) TAutoConsoleVariable CVarUserSceneTextureDebug( TEXT("r.PostProcessing.UserSceneTextureDebug"), 2, TEXT("Enable debug display of post process UserSceneTexture inputs and outputs.\n") TEXT(" 0: disabled\n") TEXT(" 1: enabled\n") TEXT(" 2: enable on error -- missing input or unused output (default). Suppressed by DisableAllScreenMessages.\n") TEXT(" 3: enable only for view with texture visualized through Vis / VisualizeTexture command, to avoid debug clutter in other views.\n"), ECVF_RenderThreadSafe); #endif } #if WITH_EDITOR static void AddGBufferPicking(FRDGBuilder& GraphBuilder, const FViewInfo& View, const TRDGUniformBufferRef& SceneTextures); #endif EDownsampleQuality GetDownsampleQuality(const TAutoConsoleVariable& CVar) { const int32 DownsampleQuality = FMath::Clamp(CVar.GetValueOnRenderThread(), 0, 1); return static_cast(DownsampleQuality); } bool IsPostProcessingWithComputeEnabled(ERHIFeatureLevel::Type FeatureLevel) { // Any thread is used due to FViewInfo initialization. return CVarPostProcessingPreferCompute.GetValueOnAnyThread() && FeatureLevel >= ERHIFeatureLevel::SM5; } bool IsPostProcessingOutputInHDR() { static const auto CVarDumpFramesAsHDR = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.BufferVisualizationDumpFramesAsHDR")); return CVarDumpFramesAsHDR->GetValueOnRenderThread() != 0 || GetHighResScreenshotConfig().bCaptureHDR; } bool IsPostProcessingEnabled(const FViewInfo& View) { if (View.GetFeatureLevel() >= ERHIFeatureLevel::SM5) { return View.Family->EngineShowFlags.PostProcessing && !View.Family->EngineShowFlags.VisualizeDistanceFieldAO && !View.Family->EngineShowFlags.VisualizeShadingModels && !View.Family->EngineShowFlags.VisualizeVolumetricCloudConservativeDensity && !View.Family->EngineShowFlags.VisualizeVolumetricCloudEmptySpaceSkipping && !View.Family->EngineShowFlags.ShaderComplexity; } else { return View.Family->EngineShowFlags.PostProcessing && !View.Family->EngineShowFlags.ShaderComplexity && IsMobileHDR(); } } bool IsPostProcessingWithAlphaChannelSupported() { return CVarPostProcessingPropagateAlpha->GetBool(); } #if DEBUG_POST_PROCESS_VOLUME_ENABLE FScreenPassTexture AddFinalPostProcessDebugInfoPasses(FRDGBuilder& GraphBuilder, const FViewInfo& View, FScreenPassTexture& ScreenPassSceneColor); #endif #if !UE_BUILD_SHIPPING static void AddUserSceneTextureDebugPass(FRDGBuilder& GraphBuilder, const FViewInfo& View, int32 ViewIndex, FScreenPassTexture Output); #endif FDefaultTemporalUpscaler::FOutputs AddThirdPartyTemporalUpscalerPasses( FRDGBuilder& GraphBuilder, const FViewInfo& View, const FDefaultTemporalUpscaler::FInputs& Inputs) { using UE::Renderer::Private::ITemporalUpscaler; const ITemporalUpscaler* UpscalerToUse = View.Family->GetTemporalUpscalerInterface(); check(UpscalerToUse); const TCHAR* UpscalerName = UpscalerToUse->GetDebugName(); // Translate the inputs to the third party temporal upscaler. ITemporalUpscaler::FInputs ThirdPartyInputs; ThirdPartyInputs.OutputViewRect.Min = FIntPoint::ZeroValue; ThirdPartyInputs.OutputViewRect.Max = View.GetSecondaryViewRectSize(); ThirdPartyInputs.TemporalJitterPixels = FVector2f(View.TemporalJitterPixels); ThirdPartyInputs.PreExposure = View.PreExposure; ThirdPartyInputs.SceneColor = Inputs.SceneColor; ThirdPartyInputs.SceneDepth = Inputs.SceneDepth; ThirdPartyInputs.SceneVelocity = Inputs.SceneVelocity; ThirdPartyInputs.EyeAdaptationTexture = AddCopyEyeAdaptationDataToTexturePass(GraphBuilder, View); if (View.PrevViewInfo.ThirdPartyTemporalUpscalerHistory && View.PrevViewInfo.ThirdPartyTemporalUpscalerHistory->GetDebugName() == UpscalerName) { ThirdPartyInputs.PrevHistory = View.PrevViewInfo.ThirdPartyTemporalUpscalerHistory; } // Standard event scope for temporal upscaler to have all profiling information not matter what, // and with explicit detection of third party. RDG_EVENT_SCOPE( GraphBuilder, "ThirdParty %s %dx%d -> %dx%d", UpscalerToUse->GetDebugName(), View.ViewRect.Width(), View.ViewRect.Height(), ThirdPartyInputs.OutputViewRect.Width(), ThirdPartyInputs.OutputViewRect.Height()); ITemporalUpscaler::FOutputs ThirdPartyOutputs = UpscalerToUse->AddPasses( GraphBuilder, View, ThirdPartyInputs); check(ThirdPartyOutputs.FullRes.ViewRect == ThirdPartyInputs.OutputViewRect); check(ThirdPartyOutputs.FullRes.ViewRect.Max.X <= ThirdPartyOutputs.FullRes.Texture->Desc.Extent.X); check(ThirdPartyOutputs.FullRes.ViewRect.Max.Y <= ThirdPartyOutputs.FullRes.Texture->Desc.Extent.Y); check(ThirdPartyOutputs.NewHistory); check(ThirdPartyOutputs.NewHistory->GetDebugName() == UpscalerToUse->GetDebugName()); // Translate the output. FDefaultTemporalUpscaler::FOutputs Outputs; Outputs.FullRes = FScreenPassTextureSlice::CreateFromScreenPassTexture(GraphBuilder, ThirdPartyOutputs.FullRes); // Saves history for next frame. if (!View.bStatePrevViewInfoIsReadOnly) { View.ViewState->PrevFrameViewInfo.ThirdPartyTemporalUpscalerHistory = ThirdPartyOutputs.NewHistory; } // Save output for next frame's SSR if (!View.bStatePrevViewInfoIsReadOnly) { FTemporalAAHistory& OutputHistory = View.ViewState->PrevFrameViewInfo.TemporalAAHistory; GraphBuilder.QueueTextureExtraction(ThirdPartyOutputs.FullRes.Texture, &OutputHistory.RT[0]); OutputHistory.ViewportRect = ThirdPartyOutputs.FullRes.ViewRect; OutputHistory.ReferenceBufferSize = ThirdPartyOutputs.FullRes.Texture->Desc.Extent; } return Outputs; } namespace UE::Renderer::PostProcess { /* * Issue scene view extension pass callbacks, mimicking "AddPostProcessMaterialChain". * The "AddAfterPass" lambdas are used instead for later extension points in the override pass sequence. */ FScreenPassTexture AddSceneViewExtensionPassChain( FRDGBuilder& GraphBuilder, const FViewInfo& View, const FPostProcessMaterialInputs& InputsTemplate, const FPostProcessingPassDelegateArray& Delegates, EPostProcessMaterialInput MaterialInput = EPostProcessMaterialInput::SceneColor) { FScreenPassTextureSlice CurrentInput = InputsTemplate.GetInput(MaterialInput); FScreenPassTexture Outputs; for (int32 DelegateIndex = 0; DelegateIndex < Delegates.Num(); ++DelegateIndex) { FPostProcessMaterialInputs Inputs = InputsTemplate; Inputs.SetInput(MaterialInput, CurrentInput); Outputs = Delegates[DelegateIndex].Execute(GraphBuilder, View, Inputs); CurrentInput = FScreenPassTextureSlice::CreateFromScreenPassTexture(GraphBuilder, Outputs); } if (!Outputs.IsValid()) { Outputs = FScreenPassTexture::CopyFromSlice(GraphBuilder, CurrentInput); } return Outputs; }; } bool ComposeSeparateTranslucencyInTSR(const FViewInfo& View); void AddPostProcessingPasses( FRDGBuilder& GraphBuilder, const FViewInfo& View, int32 ViewIndex, FSceneUniformBuffer& SceneUniformBuffer, bool bAnyLumenActive, EDiffuseIndirectMethod DiffuseIndirectMethod, EReflectionsMethod ReflectionsMethod, const FPostProcessingInputs& Inputs, const Nanite::FRasterResults* NaniteRasterResults, FInstanceCullingManager& InstanceCullingManager, FVirtualShadowMapArray* VirtualShadowMapArray, FLumenSceneFrameTemporaries& LumenFrameTemporaries, const FSceneWithoutWaterTextures& SceneWithoutWaterTextures, FScreenPassTexture TSRFlickeringInput, FRDGTextureRef& InstancedEditorDepthTexture) { RDG_CSV_STAT_EXCLUSIVE_SCOPE(GraphBuilder, RenderPostProcessing); QUICK_SCOPE_CYCLE_COUNTER(STAT_PostProcessing_Process); using namespace UE::Renderer::PostProcess; check(IsInRenderingThread()); #if DO_CHECK || USING_CODE_ANALYSIS check(View.VerifyMembersChecks()); #endif Inputs.Validate(); FScene* Scene = View.Family->Scene->GetRenderScene(); const FIntRect PrimaryViewRect = View.ViewRect; const FSceneTextureParameters SceneTextureParameters = GetSceneTextureParameters(GraphBuilder, Inputs.SceneTextures); const FScreenPassRenderTarget ViewFamilyOutput = FScreenPassRenderTarget::CreateViewFamilyOutput(Inputs.ViewFamilyTexture, View); const FScreenPassRenderTarget ViewFamilyDepthOutput = FScreenPassRenderTarget::CreateViewFamilyOutput(Inputs.ViewFamilyDepthTexture, View); const FScreenPassTexture SceneDepth(SceneTextureParameters.SceneDepthTexture, PrimaryViewRect); const FScreenPassTexture CustomDepth(Inputs.CustomDepthTexture, PrimaryViewRect); const FScreenPassTexture Velocity(SceneTextureParameters.GBufferVelocityTexture, PrimaryViewRect); const FScreenPassTexture BlackDummy(GSystemTextures.GetBlackDummy(GraphBuilder)); FTranslucencyPassResources PostDOFTranslucencyResources = Inputs.TranslucencyViewResourcesMap.Get(ETranslucencyPass::TPT_TranslucencyAfterDOF); const FTranslucencyPassResources& PostMotionBlurTranslucencyResources = Inputs.TranslucencyViewResourcesMap.Get(ETranslucencyPass::TPT_TranslucencyAfterMotionBlur); // Whether should process the alpha channel of the scene color. const bool bProcessSceneColorAlpha = IsPostProcessingWithAlphaChannelSupported(); const EPixelFormat SceneColorFormat = bProcessSceneColorAlpha ? PF_FloatRGBA : PF_FloatR11G11B10; // Scene color is updated incrementally through the post process pipeline. FScreenPassTexture SceneColor((*Inputs.SceneTextures)->SceneColorTexture, PrimaryViewRect); // Assigned before and after the tonemapper. FScreenPassTextureSlice SceneColorBeforeTonemapSlice; FScreenPassTexture SceneColorAfterTonemap; // Unprocessed scene color stores the original input. const FScreenPassTexture OriginalSceneColor = SceneColor; // Default the new eye adaptation to the last one in case it's not generated this frame. const FEyeAdaptationParameters EyeAdaptationParameters = GetEyeAdaptationParameters(View); FRDGBufferRef LastEyeAdaptationBuffer = GetEyeAdaptationBuffer(GraphBuilder, View); FRDGBufferRef EyeAdaptationBuffer = LastEyeAdaptationBuffer; const FIntRect ExposureIlluminanceRect = GetDownscaledRect(PrimaryViewRect, GetAutoExposureIlluminanceDownscaleFactor()); FScreenPassTexture ExposureIlluminance = FScreenPassTexture(Inputs.ExposureIlluminance, ExposureIlluminanceRect); FLocalExposureParameters LocalExposureParameters; // Histogram defaults to black because the histogram eye adaptation pass is used for the manual metering mode. FRDGTextureRef HistogramTexture = BlackDummy.Texture; FRDGTextureRef LocalExposureBilateralGridTexture = nullptr; FRDGTextureRef LocalExposureBlurredLogLumTexture = BlackDummy.Texture; FExposureFusionData ExposureFusionData; FVisualizeTemporalUpscalerInputs VisualizeTemporalUpscalerInputs; const bool bViewDebugMaterialsEnabled = View.RequiresDebugMaterials(); const FEngineShowFlags& EngineShowFlags = View.Family->EngineShowFlags; const bool bVisualizeHDR = EngineShowFlags.VisualizeHDR; const bool bViewFamilyOutputInHDR = View.Family->RenderTarget->GetSceneHDREnabled(); const bool bVisualizeGBufferOverview = IsVisualizeGBufferOverviewEnabled(View); const bool bVisualizeGBufferDumpToFile = IsVisualizeGBufferDumpToFileEnabled(View); const bool bVisualizeGBufferDumpToPIpe = IsVisualizeGBufferDumpToPipeEnabled(View); const bool bOutputInHDR = IsPostProcessingOutputInHDR(); const int32 LumenVisualizeMode = GetLumenVisualizeMode(View); const bool bPostProcessingEnabled = IsPostProcessingEnabled(View); // Temporal Anti-aliasing. Also may perform a temporal upsample from primary to secondary view rect. const EMainTAAPassConfig TAAConfig = GetMainTAAPassConfig(View); const bool bApplyLensDistortion = View.LensDistortionLUT.IsEnabled(); const bool bApplyLensDistortionInTSR = (LensDistortion::GetPassLocation(View) == LensDistortion::EPassLocation::TSR); enum class EPass : uint32 { MotionBlur, PostProcessMaterialBeforeBloom, Tonemap, FXAA, PostProcessMaterialAfterTonemapping, VisualizeLumenScene, VisualizeDepthOfField, VisualizeStationaryLightOverlap, VisualizeLightCulling, VisualizePostProcessStack, VisualizeSubstrate, VisualizeLightGrid, VisualizeSkyAtmosphere, VisualizeSkyLightIlluminanceMeter, VisualizeLightFunctionAtlas, VisualizeLevelInstance, VisualizeVirtualShadowMaps_PreEditorPrimitives, SelectionOutline, EditorPrimitive, VisualizeVirtualShadowMaps_PostEditorPrimitives, VisualizeVirtualTexture, VisualizeShadingModels, VisualizeGBufferHints, VisualizeSubsurface, VisualizeGBufferOverview, VisualizeLumenSceneOverview, VisualizeHDR, VisualizeLocalExposure, VisualizeMotionVectors, VisualizeTemporalUpscaler, PixelInspector, HMDDistortion, HighResolutionScreenshotMask, #if UE_ENABLE_DEBUG_DRAWING DebugPrimitive, #endif PrimaryUpscale, SecondaryUpscale, AlphaInvert, MAX }; const auto TranslatePass = [](ISceneViewExtension::EPostProcessingPass Pass) -> EPass { switch (Pass) { case ISceneViewExtension::EPostProcessingPass::MotionBlur : return EPass::MotionBlur; case ISceneViewExtension::EPostProcessingPass::Tonemap : return EPass::Tonemap; case ISceneViewExtension::EPostProcessingPass::FXAA : return EPass::FXAA; case ISceneViewExtension::EPostProcessingPass::VisualizeDepthOfField : return EPass::VisualizeDepthOfField; default: check(false); return EPass::MAX; }; }; const TCHAR* PassNames[] = { TEXT("MotionBlur"), TEXT("PostProcessMaterial (SceneColorBeforeBloom)"), TEXT("Tonemap"), TEXT("FXAA"), TEXT("PostProcessMaterial (SceneColorAfterTonemapping)"), TEXT("VisualizeLumenScene"), TEXT("VisualizeDepthOfField"), TEXT("VisualizeStationaryLightOverlap"), TEXT("VisualizeLightCulling"), TEXT("VisualizePostProcessStack"), TEXT("VisualizeSubstrate"), TEXT("VisualizeLightGrid"), TEXT("VisualizeSkyAtmosphere"), TEXT("VisualizeSkyLightIlluminanceMeter"), TEXT("VisualizeLightFunctionAtlas"), TEXT("VisualizeLevelInstance"), TEXT("VisualizeVirtualShadowMaps_PreEditorPrimitives"), TEXT("SelectionOutline"), TEXT("EditorPrimitive"), TEXT("VisualizeVirtualShadowMaps_PostEditorPrimitives"), TEXT("VisualizeVirtualTexture"), TEXT("VisualizeShadingModels"), TEXT("VisualizeGBufferHints"), TEXT("VisualizeSubsurface"), TEXT("VisualizeGBufferOverview"), TEXT("VisualizeLumenSceneOverview"), TEXT("VisualizeHDR"), TEXT("VisualizeLocalExposure"), TEXT("VisualizeMotionVectors"), TEXT("VisualizeTemporalUpscaler"), TEXT("PixelInspector"), TEXT("HMDDistortion"), TEXT("HighResolutionScreenshotMask"), #if UE_ENABLE_DEBUG_DRAWING TEXT("DebugPrimitive"), #endif TEXT("PrimaryUpscale"), TEXT("SecondaryUpscale"), TEXT("AlphaInvert") }; static_assert(static_cast(EPass::MAX) == UE_ARRAY_COUNT(PassNames), "EPass does not match PassNames."); TOverridePassSequence PassSequence(ViewFamilyOutput); PassSequence.SetNames(PassNames, UE_ARRAY_COUNT(PassNames)); PassSequence.SetEnabled(EPass::VisualizeStationaryLightOverlap, EngineShowFlags.StationaryLightOverlap); PassSequence.SetEnabled(EPass::VisualizeLightCulling, EngineShowFlags.VisualizeLightCulling); #if DEBUG_POST_PROCESS_VOLUME_ENABLE PassSequence.SetEnabled(EPass::VisualizePostProcessStack, EngineShowFlags.VisualizePostProcessStack); #else PassSequence.SetEnabled(EPass::VisualizePostProcessStack, false); #endif PassSequence.SetEnabled(EPass::VisualizeLumenScene, LumenVisualizeMode >= 0 && LumenVisualizeMode != VISUALIZE_MODE_OVERVIEW && LumenVisualizeMode != VISUALIZE_MODE_PERFORMANCE_OVERVIEW && bPostProcessingEnabled); PassSequence.SetEnabled(EPass::VisualizeSubstrate, Substrate::ShouldRenderSubstrateDebugPasses(View)); PassSequence.SetEnabled(EPass::VisualizeLightGrid, ShouldVisualizeLightGrid()); #if WITH_EDITOR PassSequence.SetEnabled(EPass::VisualizeSkyAtmosphere, Scene&& View.Family && View.Family->EngineShowFlags.VisualizeSkyAtmosphere && ShouldRenderSkyAtmosphereDebugPasses(Scene, View.Family->EngineShowFlags)); PassSequence.SetEnabled(EPass::VisualizeSkyLightIlluminanceMeter, Scene&& Scene->SkyLight && View.Family && View.Family->EngineShowFlags.VisualizeSkyLightIlluminance); PassSequence.SetEnabled(EPass::VisualizeLightFunctionAtlas, Scene && Scene->LightFunctionAtlasSceneData.GetLightFunctionAtlasEnabled() && View.Family && View.Family->EngineShowFlags.VisualizeLightFunctionAtlas); PassSequence.SetEnabled(EPass::VisualizeLevelInstance, GIsEditor && EngineShowFlags.EditingLevelInstance && EngineShowFlags.VisualizeLevelInstanceEditing && !bVisualizeHDR); PassSequence.SetEnabled(EPass::SelectionOutline, GIsEditor && EngineShowFlags.Selection && EngineShowFlags.SelectionOutline && !EngineShowFlags.Wireframe && !bVisualizeHDR); PassSequence.SetEnabled(EPass::EditorPrimitive, FSceneRenderer::ShouldCompositeEditorPrimitives(View)); #else PassSequence.SetEnabled(EPass::VisualizeSkyAtmosphere, false); PassSequence.SetEnabled(EPass::VisualizeSkyLightIlluminanceMeter, false); PassSequence.SetEnabled(EPass::VisualizeLightFunctionAtlas, false); PassSequence.SetEnabled(EPass::VisualizeLevelInstance, false); PassSequence.SetEnabled(EPass::SelectionOutline, false); PassSequence.SetEnabled(EPass::EditorPrimitive, false); #endif #if WITH_EDITOR || !UE_BUILD_SHIPPING PassSequence.SetEnabled(EPass::VisualizeVirtualShadowMaps_PreEditorPrimitives, EngineShowFlags.VisualizeVirtualShadowMap && VirtualShadowMapArray != nullptr); PassSequence.SetEnabled(EPass::VisualizeVirtualShadowMaps_PostEditorPrimitives, EngineShowFlags.VisualizeVirtualShadowMap && VirtualShadowMapArray != nullptr); #else PassSequence.SetEnabled(EPass::VisualizeVirtualShadowMaps_PreEditorPrimitives, false); PassSequence.SetEnabled(EPass::VisualizeVirtualShadowMaps_PostEditorPrimitives, false); #endif PassSequence.SetEnabled(EPass::VisualizeVirtualTexture, EngineShowFlags.VisualizeVirtualTexture && bViewDebugMaterialsEnabled); PassSequence.SetEnabled(EPass::VisualizeShadingModels, EngineShowFlags.VisualizeShadingModels); PassSequence.SetEnabled(EPass::VisualizeGBufferHints, EngineShowFlags.GBufferHints); PassSequence.SetEnabled(EPass::VisualizeSubsurface, EngineShowFlags.VisualizeSSS); PassSequence.SetEnabled(EPass::VisualizeGBufferOverview, bVisualizeGBufferOverview || bVisualizeGBufferDumpToFile || bVisualizeGBufferDumpToPIpe); PassSequence.SetEnabled(EPass::VisualizeLumenSceneOverview, (LumenVisualizeMode == VISUALIZE_MODE_OVERVIEW || LumenVisualizeMode == VISUALIZE_MODE_PERFORMANCE_OVERVIEW) && bPostProcessingEnabled); PassSequence.SetEnabled(EPass::VisualizeHDR, EngineShowFlags.VisualizeHDR); PassSequence.SetEnabled(EPass::VisualizeMotionVectors, EngineShowFlags.VisualizeMotionVectors || EngineShowFlags.VisualizeReprojection); PassSequence.SetEnabled(EPass::VisualizeTemporalUpscaler, EngineShowFlags.VisualizeTemporalUpscaler); #if WITH_EDITOR PassSequence.SetEnabled(EPass::PixelInspector, View.bUsePixelInspector); #else PassSequence.SetEnabled(EPass::PixelInspector, false); #endif PassSequence.SetEnabled(EPass::HMDDistortion, EngineShowFlags.StereoRendering && EngineShowFlags.HMDDistortion); PassSequence.SetEnabled(EPass::HighResolutionScreenshotMask, IsHighResolutionScreenshotMaskEnabled(View)); #if UE_ENABLE_DEBUG_DRAWING PassSequence.SetEnabled(EPass::DebugPrimitive, FSceneRenderer::ShouldCompositeDebugPrimitivesInPostProcess(View)); #endif PassSequence.SetEnabled(EPass::PrimaryUpscale, (bApplyLensDistortion && !bApplyLensDistortionInTSR) || (View.PrimaryScreenPercentageMethod == EPrimaryScreenPercentageMethod::SpatialUpscale && PrimaryViewRect.Size() != View.GetSecondaryViewRectSize())); PassSequence.SetEnabled(EPass::SecondaryUpscale, View.RequiresSecondaryUpscale() || View.Family->GetSecondarySpatialUpscalerInterface() != nullptr); PassSequence.SetEnabled(EPass::AlphaInvert, EngineShowFlags.AlphaInvert && !PassSequence.IsEnabled(EPass::PrimaryUpscale)); // The primary upscale does an alpha invert, so if that is active we do not run the AlphaInvert pass (which would undo the invert). const auto GetPostProcessMaterialInputs = [&](FScreenPassTexture InSceneColor) { FPostProcessMaterialInputs PostProcessMaterialInputs; PostProcessMaterialInputs.SetInput(GraphBuilder, EPostProcessMaterialInput::SceneColor, InSceneColor); FIntRect ViewRect{ 0, 0, 1, 1 }; if (Inputs.PathTracingResources.bPostProcessEnabled) { const FPathTracingResources& PathTracingResources = Inputs.PathTracingResources; ViewRect = InSceneColor.ViewRect; PostProcessMaterialInputs.SetPathTracingInput(EPathTracingPostProcessMaterialInput::Radiance, FScreenPassTexture(PathTracingResources.Radiance, ViewRect)); PostProcessMaterialInputs.SetPathTracingInput(EPathTracingPostProcessMaterialInput::DenoisedRadiance, FScreenPassTexture(PathTracingResources.DenoisedRadiance, ViewRect)); PostProcessMaterialInputs.SetPathTracingInput(EPathTracingPostProcessMaterialInput::Albedo, FScreenPassTexture(PathTracingResources.Albedo, ViewRect)); PostProcessMaterialInputs.SetPathTracingInput(EPathTracingPostProcessMaterialInput::Normal, FScreenPassTexture(PathTracingResources.Normal, ViewRect)); PostProcessMaterialInputs.SetPathTracingInput(EPathTracingPostProcessMaterialInput::Variance, FScreenPassTexture(PathTracingResources.Variance, ViewRect)); } if (PostDOFTranslucencyResources.IsValid()) { ViewRect = PostDOFTranslucencyResources.ViewRect; } PostProcessMaterialInputs.SetInput(GraphBuilder, EPostProcessMaterialInput::SeparateTranslucency, FScreenPassTexture(PostDOFTranslucencyResources.GetColorForRead(GraphBuilder), ViewRect)); PostProcessMaterialInputs.SetInput(GraphBuilder, EPostProcessMaterialInput::Velocity, Velocity); PostProcessMaterialInputs.SceneTextures = GetSceneTextureShaderParameters(Inputs.SceneTextures); PostProcessMaterialInputs.CustomDepthTexture = CustomDepth.Texture; PostProcessMaterialInputs.bManualStencilTest = Inputs.bSeparateCustomStencil; PostProcessMaterialInputs.SceneWithoutWaterTextures = &SceneWithoutWaterTextures; return PostProcessMaterialInputs; }; const auto AddAfterPass = [&](EPass InPass, FScreenPassTexture InSceneColor) -> FScreenPassTexture { // In some cases (e.g. OCIO color conversion) we want View Extensions to be able to add extra custom post processing after the pass. FPostProcessingPassDelegateArray& PassCallbacks = PassSequence.GetAfterPassCallbacks(InPass); if (PassCallbacks.Num()) { FPostProcessMaterialInputs InOutPostProcessAfterPassInputs = GetPostProcessMaterialInputs(InSceneColor); for (int32 AfterPassCallbackIndex = 0; AfterPassCallbackIndex < PassCallbacks.Num(); AfterPassCallbackIndex++) { InOutPostProcessAfterPassInputs.SetInput(GraphBuilder, EPostProcessMaterialInput::SceneColor, InSceneColor); FAfterPassCallbackDelegate& AfterPassCallback = PassCallbacks[AfterPassCallbackIndex]; PassSequence.AcceptOverrideIfLastPass(InPass, InOutPostProcessAfterPassInputs.OverrideOutput, AfterPassCallbackIndex); InSceneColor = AfterPassCallback.Execute(GraphBuilder, View, InOutPostProcessAfterPassInputs); } } return MoveTemp(InSceneColor); }; const auto AddAfterPassForSceneColorSlice = [&](EPass InPass, const FScreenPassTextureSlice& InSceneColor) -> FScreenPassTextureSlice { FPostProcessingPassDelegateArray& PassCallbacks = PassSequence.GetAfterPassCallbacks(InPass); if (PassCallbacks.Num()) { FScreenPassTexture SceneColor = FScreenPassTexture::CopyFromSlice(GraphBuilder, InSceneColor); return FScreenPassTextureSlice::CreateFromScreenPassTexture(GraphBuilder, AddAfterPass(InPass, SceneColor)); } return InSceneColor; }; if (bPostProcessingEnabled) { const bool bPrimaryView = IStereoRendering::IsAPrimaryView(View); const bool bHasViewState = View.ViewState != nullptr; const bool bDepthOfFieldEnabled = DiaphragmDOF::IsEnabled(View); const bool bVisualizeDepthOfField = bDepthOfFieldEnabled && EngineShowFlags.VisualizeDOF; const bool bVisualizeMotionBlur = IsVisualizeMotionBlurEnabled(View); const bool bVisualizeTSR = IsVisualizeTSREnabled(View); const EAutoExposureMethod AutoExposureMethod = GetAutoExposureMethod(View); const EAntiAliasingMethod AntiAliasingMethod = !bVisualizeDepthOfField ? View.AntiAliasingMethod : AAM_None; const EDownsampleQuality DownsampleQuality = GetDownsampleQuality(CVarDownsampleQuality); const EDownsampleQuality DownsampleChainQuality = GetDownsampleQuality(CVarDownsampleChainQuality); const EPixelFormat DownsampleOverrideFormat = PF_FloatRGB; // Previous transforms are nonsensical on camera cuts, unless motion vector simulation is enabled (providing FrameN+1 transforms to FrameN+0) const bool bMotionBlurValid = FMotionVectorSimulation::IsEnabled() || (!View.bCameraCut && !View.bPrevTransformsReset); // Motion blur gets replaced by the visualization pass. const bool bMotionBlurEnabled = !bVisualizeMotionBlur && IsMotionBlurEnabled(View) && bMotionBlurValid && !bVisualizeTSR; // Skip tonemapping for visualizers which overwrite the HDR scene color. const bool bTonemapEnabled = !bVisualizeMotionBlur; const bool bTonemapOutputInHDR = View.Family->SceneCaptureSource == SCS_FinalColorHDR || View.Family->SceneCaptureSource == SCS_FinalToneCurveHDR || bOutputInHDR || bViewFamilyOutputInHDR; // We don't test for the EyeAdaptation engine show flag here. If disabled, the auto exposure pass is still executes but performs a clamp. const bool bEyeAdaptationEnabled = // Skip for transient views. bHasViewState && View.HasEyeAdaptationViewState() && // Skip for secondary views in a stereo setup. bPrimaryView; const bool bHistogramEnabled = // Force the histogram on when we are visualizing HDR. bVisualizeHDR || // Skip if not using histogram eye adaptation. (bEyeAdaptationEnabled && AutoExposureMethod == EAutoExposureMethod::AEM_Histogram && // Skip if we don't have any exposure range to generate (eye adaptation will clamp). View.FinalPostProcessSettings.AutoExposureMinBrightness < View.FinalPostProcessSettings.AutoExposureMaxBrightness); const bool bLocalExposureEnabled = EngineShowFlags.VisualizeLocalExposure || !FMath::IsNearlyEqual(View.FinalPostProcessSettings.LocalExposureHighlightContrastScale, 1.0f) || !FMath::IsNearlyEqual(View.FinalPostProcessSettings.LocalExposureShadowContrastScale, 1.0f) || View.FinalPostProcessSettings.LocalExposureHighlightContrastCurve || View.FinalPostProcessSettings.LocalExposureShadowContrastCurve || !FMath::IsNearlyEqual(View.FinalPostProcessSettings.LocalExposureDetailStrength, 1.0f); const bool bBloomEnabled = View.FinalPostProcessSettings.BloomIntensity > 0.0f && !bVisualizeTSR; // Whether separate translucency is composed in TSR. bool bComposeSeparateTranslucencyInTSR = PostDOFTranslucencyResources.IsValid() && TAAConfig == EMainTAAPassConfig::TSR && ComposeSeparateTranslucencyInTSR(View); const FIntPoint PostTAAViewSize = (View.PrimaryScreenPercentageMethod == EPrimaryScreenPercentageMethod::TemporalUpscale && TAAConfig != EMainTAAPassConfig::Disabled) ? View.GetSecondaryViewRectSize() : View.ViewRect.Size(); const FPostProcessMaterialChain PostProcessMaterialBeforeBloomChain = GetPostProcessMaterialChain(View, BL_SceneColorBeforeBloom); const FPostProcessMaterialChain PostProcessMaterialAfterTonemappingChain = GetPostProcessMaterialChain(View, BL_SceneColorAfterTonemapping); PassSequence.SetEnabled(EPass::MotionBlur, bVisualizeMotionBlur || bMotionBlurEnabled); PassSequence.SetEnabled(EPass::PostProcessMaterialBeforeBloom, PostProcessMaterialBeforeBloomChain.Num() != 0); PassSequence.SetEnabled(EPass::Tonemap, bTonemapEnabled); PassSequence.SetEnabled(EPass::FXAA, AntiAliasingMethod == AAM_FXAA); PassSequence.SetEnabled(EPass::PostProcessMaterialAfterTonemapping, PostProcessMaterialAfterTonemappingChain.Num() != 0); PassSequence.SetEnabled(EPass::VisualizeDepthOfField, bVisualizeDepthOfField); PassSequence.SetEnabled(EPass::VisualizeLocalExposure, EngineShowFlags.VisualizeLocalExposure); static_assert(static_cast(EPass::MotionBlur) == 0); constexpr int32 FirstAfterPass = static_cast(ISceneViewExtension::EPostProcessingPass::MotionBlur); // Scene view extension delegates that precede the override pass sequence are to be called directly. TStaticArray(FirstAfterPass)> SceneViewExtensionDelegates; for (const TSharedRef& ViewExtension : View.Family->ViewExtensions) { for (int32 SceneViewPassId = 0; SceneViewPassId < FirstAfterPass; SceneViewPassId++) { const ISceneViewExtension::EPostProcessingPass SceneViewPass = static_cast(SceneViewPassId); const bool bIsEnabled = (SceneViewPass == ISceneViewExtension::EPostProcessingPass::ReplacingTonemapper) ? PassSequence.IsEnabled(EPass::Tonemap) : true; ViewExtension->SubscribeToPostProcessingPass(SceneViewPass, View, SceneViewExtensionDelegates[SceneViewPassId], bIsEnabled); } for (int32 SceneViewPassId = FirstAfterPass; SceneViewPassId < static_cast(ISceneViewExtension::EPostProcessingPass::MAX); SceneViewPassId++) { const ISceneViewExtension::EPostProcessingPass SceneViewPass = static_cast(SceneViewPassId); const EPass PostProcessingPass = TranslatePass(SceneViewPass); ViewExtension->SubscribeToPostProcessingPass( SceneViewPass, View, PassSequence.GetAfterPassCallbacks(PostProcessingPass), PassSequence.IsEnabled(PostProcessingPass)); } } PassSequence.Finalize(); const bool bLensFlareEnabled = bBloomEnabled && IsLensFlaresEnabled(View); const bool bFFTBloomEnabled = bBloomEnabled && IsFFTBloomEnabled(View); const bool bBasicEyeAdaptationEnabled = bEyeAdaptationEnabled && (AutoExposureMethod == EAutoExposureMethod::AEM_Basic); const bool bLocalExposureBlurredLum = bLocalExposureEnabled && View.FinalPostProcessSettings.LocalExposureMethod == ELocalExposureMethod::Bilateral && View.FinalPostProcessSettings.LocalExposureBlurredLuminanceBlend > 0.0f; const bool bProcessQuarterResolution = CVarPostProcessingQuarterResolutionDownsample.GetValueOnRenderThread() == 1; const bool bProcessEighthResolution = CVarPostProcessingQuarterResolutionDownsample.GetValueOnRenderThread() == 2; const bool bMotionBlurNeedsHalfResInput = PassSequence.IsEnabled(EPass::MotionBlur) && DoesMotionBlurNeedsHalfResInput() && !bVisualizeMotionBlur; const float FFTBloomResolutionFraction = GetFFTBloomResolutionFraction(PostTAAViewSize); const bool bProduceSceneColorChain = ( bBasicEyeAdaptationEnabled || (bBloomEnabled && !bFFTBloomEnabled) || (bLensFlareEnabled && bFFTBloomEnabled) || bLocalExposureBlurredLum); extern int32 GSSRHalfResSceneColor; const bool bNeedBeforeBloomHalfRes = (!bProcessQuarterResolution && !bProcessEighthResolution) || (bFFTBloomEnabled && FFTBloomResolutionFraction > 0.25f && FFTBloomResolutionFraction <= 0.5f) || (ReflectionsMethod == EReflectionsMethod::SSR && !View.bStatePrevViewInfoIsReadOnly && GSSRHalfResSceneColor); const bool bNeedBeforeBloomQuarterRes = bProcessQuarterResolution || (bFFTBloomEnabled && FFTBloomResolutionFraction > 0.125f && FFTBloomResolutionFraction <= 0.25f); const bool bNeedBeforeBloomEighthRes = bProcessEighthResolution || (bFFTBloomEnabled && FFTBloomResolutionFraction <= 0.125f); const FPostProcessMaterialChain MaterialChainSceneColorBeforeDOF = GetPostProcessMaterialChain(View, BL_SceneColorBeforeDOF); const FPostProcessMaterialChain MaterialChainSceneColorAfterDOF = GetPostProcessMaterialChain(View, BL_SceneColorAfterDOF); const FPostProcessMaterialChain MaterialChainTranslucencyAfterDOF = GetPostProcessMaterialChain(View, BL_TranslucencyAfterDOF); // Scene view extension delegates - BeforeDOF if (SceneViewExtensionDelegates[static_cast(ISceneViewExtension::EPostProcessingPass::BeforeDOF)].Num()) { SceneColor = AddSceneViewExtensionPassChain(GraphBuilder, View, GetPostProcessMaterialInputs(SceneColor), SceneViewExtensionDelegates[static_cast(ISceneViewExtension::EPostProcessingPass::BeforeDOF)]); } // Post Process Material Chain - BL_SceneColorBeforeDOF if (MaterialChainSceneColorBeforeDOF.Num()) { SceneColor = AddPostProcessMaterialChain(GraphBuilder, View, ViewIndex, GetPostProcessMaterialInputs(SceneColor), MaterialChainSceneColorBeforeDOF); } // Diaphragm Depth of Field bool bSceneColorHasPostDOFTranslucency = false; { FRDGTextureRef InputSceneColorTexture = SceneColor.Texture; if (bDepthOfFieldEnabled) { FTranslucencyPassResources DummyTranslucency; bool bComposeTranslucency = PostDOFTranslucencyResources.IsValid() && !bComposeSeparateTranslucencyInTSR && MaterialChainTranslucencyAfterDOF.Num() == 0; if (DiaphragmDOF::AddPasses( GraphBuilder, SceneTextureParameters, View, InputSceneColorTexture, bComposeTranslucency ? PostDOFTranslucencyResources : DummyTranslucency, SceneColor.Texture)) { bSceneColorHasPostDOFTranslucency = bComposeTranslucency; } } if (GetHairStrandsComposition() == EHairStrandsCompositionType::AfterSeparateTranslucent) { RenderHairComposition(GraphBuilder, View, SceneColor.Texture, SceneDepth.Texture, Velocity.Texture); } } // Scene view extension delegates - AfterDOF if (SceneViewExtensionDelegates[static_cast(ISceneViewExtension::EPostProcessingPass::AfterDOF)].Num()) { SceneColor = AddSceneViewExtensionPassChain(GraphBuilder, View, GetPostProcessMaterialInputs(SceneColor), SceneViewExtensionDelegates[static_cast(ISceneViewExtension::EPostProcessingPass::AfterDOF)]); } // Post Process Material Chain - BL_SceneColorAfterDOF if (MaterialChainSceneColorAfterDOF.Num()) { SceneColor = AddPostProcessMaterialChain(GraphBuilder, View, ViewIndex, GetPostProcessMaterialInputs(SceneColor), MaterialChainSceneColorAfterDOF); } // Post Process Material Chain - BL_TranslucencyAfterDOF if (bSceneColorHasPostDOFTranslucency) { ensure(MaterialChainTranslucencyAfterDOF.Num() == 0); ensure(!bComposeSeparateTranslucencyInTSR); } else if (PostDOFTranslucencyResources.IsValid()) { if (SceneViewExtensionDelegates[static_cast(ISceneViewExtension::EPostProcessingPass::TranslucencyAfterDOF)].Num()) { FScreenPassTexture PostDOFTranslucency = AddSceneViewExtensionPassChain( GraphBuilder, View, GetPostProcessMaterialInputs(SceneColor), SceneViewExtensionDelegates[static_cast(ISceneViewExtension::EPostProcessingPass::TranslucencyAfterDOF)], EPostProcessMaterialInput::SeparateTranslucency); PostDOFTranslucencyResources.ColorTexture = PostDOFTranslucency.Texture; ensure(PostDOFTranslucencyResources.ViewRect == PostDOFTranslucency.ViewRect); } if (MaterialChainTranslucencyAfterDOF.Num()) { FScreenPassTexture PostDOFTranslucency = AddPostProcessMaterialChain( GraphBuilder, View, ViewIndex, GetPostProcessMaterialInputs(SceneColor), MaterialChainTranslucencyAfterDOF, EPostProcessMaterialInput::SeparateTranslucency); PostDOFTranslucencyResources.ColorTexture = PostDOFTranslucency.Texture; ensure(PostDOFTranslucencyResources.ViewRect == PostDOFTranslucency.ViewRect); } // DOF passes were not added, therefore need to compose Separate translucency manually. if (!bSceneColorHasPostDOFTranslucency) { FTranslucencyComposition TranslucencyComposition; TranslucencyComposition.Operation = FTranslucencyComposition::EOperation::ComposeToNewSceneColor; TranslucencyComposition.bApplyModulateOnly = bComposeSeparateTranslucencyInTSR; TranslucencyComposition.SceneColor = FScreenPassTextureSlice::CreateFromScreenPassTexture(GraphBuilder, SceneColor); TranslucencyComposition.SceneDepth = SceneDepth; TranslucencyComposition.OutputViewport = FScreenPassTextureViewport(SceneColor); TranslucencyComposition.OutputPixelFormat = SceneColorFormat; SceneColor = TranslucencyComposition.AddPass( GraphBuilder, View, PostDOFTranslucencyResources); bSceneColorHasPostDOFTranslucency = !TranslucencyComposition.bApplyModulateOnly; } } else { bSceneColorHasPostDOFTranslucency = true; } ensure(bSceneColorHasPostDOFTranslucency != bComposeSeparateTranslucencyInTSR); // Allows for the scene color to be the slice of an array between temporal upscaler and tonemaper. FScreenPassTextureSlice SceneColorSlice = FScreenPassTextureSlice::CreateFromScreenPassTexture(GraphBuilder, SceneColor); SceneColor = FScreenPassTexture(); FScreenPassTextureSlice HalfResSceneColor; FScreenPassTextureSlice QuarterResSceneColor; FScreenPassTextureSlice EighthResSceneColor; FVelocityFlattenTextures VelocityFlattenTextures; if (TAAConfig != EMainTAAPassConfig::Disabled) { FDefaultTemporalUpscaler::FInputs UpscalerPassInputs; UpscalerPassInputs.SceneColor = FScreenPassTexture(SceneColorSlice); UpscalerPassInputs.SceneDepth = FScreenPassTexture(SceneDepth.Texture, View.ViewRect); UpscalerPassInputs.SceneVelocity = FScreenPassTexture(Velocity.Texture, View.ViewRect); if (PassSequence.IsEnabled(EPass::MotionBlur)) { if (bVisualizeMotionBlur) { // NOP } else { UpscalerPassInputs.bGenerateOutputMip1 = bMotionBlurNeedsHalfResInput; UpscalerPassInputs.bGenerateVelocityFlattenTextures = FVelocityFlattenTextures::AllowExternal(View) && !bVisualizeMotionBlur && !bApplyLensDistortionInTSR; } } else if (PostProcessMaterialBeforeBloomChain.Num() > 0) { // NOP } else { UpscalerPassInputs.bGenerateSceneColorHalfRes = bNeedBeforeBloomHalfRes && DownsampleQuality == EDownsampleQuality::Low; UpscalerPassInputs.bGenerateSceneColorQuarterRes = bNeedBeforeBloomQuarterRes && DownsampleQuality == EDownsampleQuality::Low; UpscalerPassInputs.bGenerateSceneColorEighthRes = bNeedBeforeBloomEighthRes && DownsampleQuality == EDownsampleQuality::Low; } UpscalerPassInputs.bAllowFullResSlice = PassSequence.IsEnabled(EPass::MotionBlur) || PassSequence.IsEnabled(EPass::Tonemap); UpscalerPassInputs.DownsampleOverrideFormat = DownsampleOverrideFormat; UpscalerPassInputs.PostDOFTranslucencyResources = PostDOFTranslucencyResources; UpscalerPassInputs.FlickeringInputTexture = TSRFlickeringInput; if (bApplyLensDistortionInTSR) { UpscalerPassInputs.LensDistortionLUT = View.LensDistortionLUT; } check(UpscalerPassInputs.SceneColor.ViewRect == View.ViewRect); FDefaultTemporalUpscaler::FOutputs Outputs; if (TAAConfig == EMainTAAPassConfig::TSR) { Outputs = AddMainTemporalSuperResolutionPasses( GraphBuilder, View, UpscalerPassInputs); } else if (TAAConfig == EMainTAAPassConfig::TAA) { Outputs = AddGen4MainTemporalAAPasses( GraphBuilder, View, UpscalerPassInputs); } else if (TAAConfig == EMainTAAPassConfig::ThirdParty) { Outputs = AddThirdPartyTemporalUpscalerPasses( GraphBuilder, View, UpscalerPassInputs); } else { unimplemented(); } SceneColorSlice = Outputs.FullRes; HalfResSceneColor = Outputs.HalfRes; QuarterResSceneColor = Outputs.QuarterRes; EighthResSceneColor = Outputs.EighthRes; VelocityFlattenTextures = Outputs.VelocityFlattenTextures; if (PassSequence.IsEnabled(EPass::VisualizeTemporalUpscaler)) { VisualizeTemporalUpscalerInputs.TAAConfig = TAAConfig; VisualizeTemporalUpscalerInputs.UpscalerUsed = View.Family->GetTemporalUpscalerInterface(); VisualizeTemporalUpscalerInputs.Inputs = UpscalerPassInputs; VisualizeTemporalUpscalerInputs.Outputs = Outputs; } } else if (ReflectionsMethod == EReflectionsMethod::SSR) { // If we need SSR, and TAA is enabled, then AddTemporalAAPass() has already handled the scene history. // If we need SSR, and TAA is not enable, then we just need to extract the history. if (!View.bStatePrevViewInfoIsReadOnly) { check(View.ViewState); FTemporalAAHistory& OutputHistory = View.ViewState->PrevFrameViewInfo.TemporalAAHistory; GraphBuilder.QueueTextureExtraction(SceneColorSlice.TextureSRV->Desc.Texture, &OutputHistory.RT[0]); // For SSR, we still fill up the rest of the OutputHistory data using shared math from FTAAPassParameters. FTAAPassParameters TAAInputs(View); TAAInputs.SceneColorInput = SceneColorSlice.TextureSRV->Desc.Texture; TAAInputs.SetupViewRect(View); OutputHistory.ViewportRect = TAAInputs.OutputViewRect; OutputHistory.ReferenceBufferSize = TAAInputs.GetOutputExtent() * TAAInputs.ResolutionDivisor; } } ensure(SceneColorSlice.ViewRect.Size() == PostTAAViewSize); // SVE/Post Process Material Chain - SSR Input if (View.ViewState && !View.bStatePrevViewInfoIsReadOnly) { FScreenPassTexture PassOutput; FPostProcessMaterialInputs PassInputs; const FPostProcessMaterialChain MaterialChain = GetPostProcessMaterialChain(View, BL_SSRInput); if (SceneViewExtensionDelegates[static_cast(ISceneViewExtension::EPostProcessingPass::SSRInput)].Num()) { PassInputs = GetPostProcessMaterialInputs(FScreenPassTexture::CopyFromSlice(GraphBuilder, SceneColorSlice)); PassOutput = AddSceneViewExtensionPassChain(GraphBuilder, View, PassInputs, SceneViewExtensionDelegates[static_cast(ISceneViewExtension::EPostProcessingPass::SSRInput)]); } if (MaterialChain.Num()) { PassInputs = GetPostProcessMaterialInputs(PassOutput.IsValid() ? PassOutput : FScreenPassTexture::CopyFromSlice(GraphBuilder, SceneColorSlice)); PassOutput = AddPostProcessMaterialChain(GraphBuilder, View, ViewIndex, PassInputs, MaterialChain); } if (PassOutput.IsValid()) { // Save off SSR post process output for the next frame. GraphBuilder.QueueTextureExtraction(PassOutput.Texture, &View.ViewState->PrevFrameViewInfo.CustomSSRInput.RT[0]); View.ViewState->PrevFrameViewInfo.CustomSSRInput.ViewportRect = PassOutput.ViewRect; View.ViewState->PrevFrameViewInfo.CustomSSRInput.ReferenceBufferSize = PassOutput.Texture->Desc.Extent; } } if (PassSequence.IsEnabled(EPass::MotionBlur)) { FMotionBlurInputs PassInputs; PassSequence.AcceptOverrideIfLastPass(EPass::MotionBlur, PassInputs.OverrideOutput); PassInputs.bOutputHalfRes = PostProcessMaterialBeforeBloomChain.Num() == 0 && bNeedBeforeBloomHalfRes && DownsampleQuality == EDownsampleQuality::Low; PassInputs.bOutputQuarterRes = (bNeedBeforeBloomQuarterRes || bNeedBeforeBloomEighthRes) && DownsampleQuality == EDownsampleQuality::Low; PassInputs.SceneColor = SceneColorSlice; PassInputs.SceneDepth = SceneDepth; PassInputs.SceneVelocity = Velocity; PassInputs.PostMotionBlurTranslucency = PostMotionBlurTranslucencyResources; PassInputs.Quality = GetMotionBlurQuality(); PassInputs.Filter = GetMotionBlurFilter(); PassInputs.VelocityFlattenTextures = VelocityFlattenTextures; if (bApplyLensDistortionInTSR) { PassInputs.LensDistortionLUT = View.LensDistortionLUT; } // Motion blur visualization replaces motion blur when enabled. if (bVisualizeMotionBlur) { SceneColorSlice = AddVisualizeMotionBlurPass(GraphBuilder, View, PassInputs); } else { FMotionBlurOutputs PassOutputs = AddMotionBlurPass(GraphBuilder, View, PassInputs); SceneColorSlice = PassOutputs.FullRes; HalfResSceneColor = FScreenPassTextureSlice::CreateFromScreenPassTexture(GraphBuilder, PassOutputs.HalfRes); QuarterResSceneColor = FScreenPassTextureSlice::CreateFromScreenPassTexture(GraphBuilder, PassOutputs.QuarterRes); } } else if (PostMotionBlurTranslucencyResources.IsValid()) { // Compose Post-MotionBlur translucency in a new scene color to ensure it's not writing out in TAA's output that is also the history. FTranslucencyComposition TranslucencyComposition; TranslucencyComposition.Operation = FTranslucencyComposition::EOperation::ComposeToNewSceneColor; TranslucencyComposition.SceneColor = SceneColorSlice; TranslucencyComposition.OutputViewport = FScreenPassTextureViewport(SceneColorSlice); TranslucencyComposition.OutputPixelFormat = SceneColorFormat; if (bApplyLensDistortionInTSR) { TranslucencyComposition.LensDistortionLUT = View.LensDistortionLUT; } SceneColorSlice = FScreenPassTextureSlice::CreateFromScreenPassTexture(GraphBuilder, TranslucencyComposition.AddPass( GraphBuilder, View, PostMotionBlurTranslucencyResources)); } { FScreenPassTextureSlice NewSceneColorSlice = AddAfterPassForSceneColorSlice(EPass::MotionBlur, SceneColorSlice); // Invalidate half and quarter res. if (NewSceneColorSlice != SceneColorSlice) { HalfResSceneColor = FScreenPassTextureSlice(); QuarterResSceneColor = FScreenPassTextureSlice(); EighthResSceneColor = FScreenPassTextureSlice(); } SceneColorSlice = NewSceneColorSlice; } // Post Process Material Chain - Before Bloom if (PassSequence.IsEnabled(EPass::PostProcessMaterialBeforeBloom)) { FPostProcessMaterialInputs PostProcessMaterialInputs = GetPostProcessMaterialInputs(FScreenPassTexture()); PassSequence.AcceptOverrideIfLastPass(EPass::PostProcessMaterialBeforeBloom, PostProcessMaterialInputs.OverrideOutput); PostProcessMaterialInputs.SetInput(EPostProcessMaterialInput::SceneColor, SceneColorSlice); SceneColorSlice = FScreenPassTextureSlice::CreateFromScreenPassTexture(GraphBuilder, AddPostProcessMaterialChain(GraphBuilder, View, ViewIndex, PostProcessMaterialInputs, PostProcessMaterialBeforeBloomChain)); } // Generate before bloom lower res scene color if they have not been generated. { if ((bNeedBeforeBloomHalfRes && !HalfResSceneColor.IsValid()) || (bNeedBeforeBloomQuarterRes && !QuarterResSceneColor.IsValid() && !HalfResSceneColor.IsValid()) || (bNeedBeforeBloomEighthRes && !EighthResSceneColor.IsValid() && !QuarterResSceneColor.IsValid() && !HalfResSceneColor.IsValid())) { FDownsamplePassInputs PassInputs; PassInputs.Name = TEXT("PostProcessing.SceneColor.HalfRes"); PassInputs.SceneColor = SceneColorSlice; PassInputs.Quality = DownsampleQuality; PassInputs.FormatOverride = DownsampleOverrideFormat; HalfResSceneColor = FScreenPassTextureSlice::CreateFromScreenPassTexture(GraphBuilder, AddDownsamplePass(GraphBuilder, View, PassInputs)); } if ((bNeedBeforeBloomQuarterRes && !QuarterResSceneColor.IsValid()) || (bNeedBeforeBloomEighthRes && !EighthResSceneColor.IsValid() && !QuarterResSceneColor.IsValid())) { FDownsamplePassInputs PassInputs; PassInputs.Name = TEXT("PostProcessing.SceneColor.QuarterRes"); PassInputs.SceneColor = HalfResSceneColor; PassInputs.Quality = DownsampleQuality; QuarterResSceneColor = FScreenPassTextureSlice::CreateFromScreenPassTexture(GraphBuilder, AddDownsamplePass(GraphBuilder, View, PassInputs)); } if (bNeedBeforeBloomEighthRes && !EighthResSceneColor.IsValid()) { FDownsamplePassInputs PassInputs; PassInputs.Name = TEXT("PostProcessing.SceneColor.EighthRes"); PassInputs.SceneColor = QuarterResSceneColor; PassInputs.Quality = DownsampleQuality; EighthResSceneColor = FScreenPassTextureSlice::CreateFromScreenPassTexture(GraphBuilder, AddDownsamplePass(GraphBuilder, View, PassInputs)); } } // Store half res scene color in the history if (ReflectionsMethod == EReflectionsMethod::SSR && !View.bStatePrevViewInfoIsReadOnly && GSSRHalfResSceneColor && HalfResSceneColor.IsValid()) { check(View.ViewState); GraphBuilder.QueueTextureExtraction(HalfResSceneColor.TextureSRV->Desc.Texture, &View.ViewState->PrevFrameViewInfo.HalfResTemporalAAHistory); } { FScreenPassTextureSlice LocalExposureSceneColor = bProcessEighthResolution ? EighthResSceneColor : (bProcessQuarterResolution ? QuarterResSceneColor : HalfResSceneColor); if (bLocalExposureEnabled && View.FinalPostProcessSettings.LocalExposureMethod == ELocalExposureMethod::Bilateral) { LocalExposureBilateralGridTexture = AddLocalExposurePass( GraphBuilder, View, EyeAdaptationParameters, LocalExposureSceneColor); } LocalExposureParameters = GetLocalExposureParameters(View, LocalExposureSceneColor.ViewRect.Size(), EyeAdaptationParameters); } if (bHistogramEnabled) { FScreenPassTextureSlice HistogramSceneColor = bProcessEighthResolution ? EighthResSceneColor : (bProcessQuarterResolution ? QuarterResSceneColor : HalfResSceneColor); if (IsAutoExposureUsingIlluminanceEnabled(View)) { if (ExposureIlluminance.IsValid()) { HistogramSceneColor = FScreenPassTextureSlice::CreateFromScreenPassTexture(GraphBuilder, ExposureIlluminance); } else { HistogramSceneColor = FScreenPassTextureSlice::CreateFromScreenPassTexture(GraphBuilder, OriginalSceneColor); } } // Optionally generate eye adaptation from the entire set of view rects. Rects must combine to form a contiguous rect! if (View.bEyeAdaptationAllViewPixels && View.Family->Views.Num() > 1) { FIntRect EyeAdaptationRect = View.Family->Views[0]->UnconstrainedViewRect; for (int32 OtherViewIndex = 1; OtherViewIndex < View.Family->Views.Num(); OtherViewIndex++) { EyeAdaptationRect.Union(View.Family->Views[OtherViewIndex]->UnconstrainedViewRect); } HistogramSceneColor = FScreenPassTextureSlice::CreateFromScreenPassTexture(GraphBuilder, FScreenPassTexture(OriginalSceneColor.Texture, EyeAdaptationRect)); } HistogramTexture = AddHistogramPass( GraphBuilder, View, EyeAdaptationParameters, HistogramSceneColor, SceneTextureParameters, LastEyeAdaptationBuffer); } FTextureDownsampleChain SceneDownsampleChain; if (bProduceSceneColorChain) { const bool bLogLumaInAlpha = bBasicEyeAdaptationEnabled; SceneDownsampleChain.Init( GraphBuilder, View, EyeAdaptationParameters, bProcessEighthResolution ? EighthResSceneColor : (bProcessQuarterResolution ? QuarterResSceneColor : HalfResSceneColor), DownsampleChainQuality, 6, bLogLumaInAlpha, TEXT("Scene"), bProcessEighthResolution ? 3 : (bProcessQuarterResolution ? 2 : 1)); } if (bLocalExposureBlurredLum) { const uint32 BlurredLumMip = bProcessEighthResolution ? 2 : (bProcessQuarterResolution ? 3 : 4); LocalExposureBlurredLogLumTexture = AddLocalExposureBlurredLogLuminancePass( GraphBuilder, View, EyeAdaptationParameters, SceneDownsampleChain.GetTexture(BlurredLumMip)); } if (bBasicEyeAdaptationEnabled) { // Use the alpha channel in the last downsample (smallest) to compute eye adaptations values. EyeAdaptationBuffer = AddBasicEyeAdaptationPass( GraphBuilder, View, EyeAdaptationParameters, LocalExposureParameters, SceneDownsampleChain.GetLastTexture(), LastEyeAdaptationBuffer, bLocalExposureEnabled && View.FinalPostProcessSettings.LocalExposureMethod == ELocalExposureMethod::Bilateral); } // Add histogram eye adaptation pass even if no histogram exists to support the manual clamping mode. else if (bEyeAdaptationEnabled) { EyeAdaptationBuffer = AddHistogramEyeAdaptationPass( GraphBuilder, View, EyeAdaptationParameters, LocalExposureParameters, HistogramTexture, bLocalExposureEnabled && View.FinalPostProcessSettings.LocalExposureMethod == ELocalExposureMethod::Bilateral); } if (bLocalExposureEnabled && View.FinalPostProcessSettings.LocalExposureMethod == ELocalExposureMethod::Fusion) { ExposureFusionData = AddLocalExposureFusionPass( GraphBuilder, View, EyeAdaptationParameters, EyeAdaptationBuffer, LocalExposureParameters, //bProcessQuarterResolution ? QuarterResSceneColor : HalfResSceneColor); SceneColorSlice); } FScreenPassTexture Bloom; FRDGBufferRef SceneColorApplyParameters = nullptr; if (bBloomEnabled) { const FTextureDownsampleChain* LensFlareSceneDownsampleChain; FTextureDownsampleChain BloomDownsampleChain; if (bFFTBloomEnabled) { LensFlareSceneDownsampleChain = &SceneDownsampleChain; float InputResolutionFraction; FScreenPassTextureSlice InputSceneColor; if (FFTBloomResolutionFraction <= 0.125f) { InputSceneColor = EighthResSceneColor; InputResolutionFraction = 0.125f; } else if (FFTBloomResolutionFraction <= 0.25f) { InputSceneColor = QuarterResSceneColor; InputResolutionFraction = 0.25f; } else if (FFTBloomResolutionFraction <= 0.5f) { InputSceneColor = HalfResSceneColor; InputResolutionFraction = 0.5f; } else { InputSceneColor = SceneColorSlice; InputResolutionFraction = 1.0f; } FFFTBloomOutput Outputs = AddFFTBloomPass( GraphBuilder, View, InputSceneColor, InputResolutionFraction, EyeAdaptationParameters, EyeAdaptationBuffer, LocalExposureParameters, CVarBloomApplyLocalExposure.GetValueOnRenderThread() ? LocalExposureBilateralGridTexture : nullptr, LocalExposureBlurredLogLumTexture); Bloom = Outputs.BloomTexture; SceneColorApplyParameters = Outputs.SceneColorApplyParameters; } else { const bool bApplyLocalExposureToBloom = CVarBloomApplyLocalExposure.GetValueOnRenderThread() && View.FinalPostProcessSettings.LocalExposureMethod == ELocalExposureMethod::Bilateral && LocalExposureBilateralGridTexture != nullptr; const bool bBloomSetupRequiredEnabled = View.FinalPostProcessSettings.BloomThreshold > -1.0f || bApplyLocalExposureToBloom; // Reuse the main scene downsample chain if setup isn't required for gaussian bloom. if (SceneDownsampleChain.IsInitialized() && !bBloomSetupRequiredEnabled) { LensFlareSceneDownsampleChain = &SceneDownsampleChain; } else { FScreenPassTextureSlice DownsampleInput = bProcessEighthResolution ? EighthResSceneColor : (bProcessQuarterResolution ? QuarterResSceneColor : HalfResSceneColor); if (bBloomSetupRequiredEnabled) { const float BloomThreshold = View.FinalPostProcessSettings.BloomThreshold; FBloomSetupInputs SetupPassInputs; SetupPassInputs.SceneColor = DownsampleInput; SetupPassInputs.EyeAdaptationBuffer = EyeAdaptationBuffer; SetupPassInputs.EyeAdaptationParameters = &EyeAdaptationParameters; SetupPassInputs.Threshold = BloomThreshold; if (bApplyLocalExposureToBloom) { SetupPassInputs.LocalExposureParameters = &LocalExposureParameters; SetupPassInputs.LocalExposureTexture = LocalExposureBilateralGridTexture; SetupPassInputs.BlurredLogLuminanceTexture = LocalExposureBlurredLogLumTexture; } DownsampleInput = FScreenPassTextureSlice::CreateFromScreenPassTexture(GraphBuilder, AddBloomSetupPass(GraphBuilder, View, SetupPassInputs)); } const bool bLogLumaInAlpha = false; BloomDownsampleChain.Init(GraphBuilder, View, EyeAdaptationParameters, DownsampleInput, DownsampleChainQuality, (uint32)EBloomQuality::MAX, bLogLumaInAlpha, nullptr, bProcessEighthResolution ? 3 : (bProcessQuarterResolution ? 2 : 1)); LensFlareSceneDownsampleChain = &BloomDownsampleChain; } Bloom = AddGaussianBloomPasses(GraphBuilder, View, LensFlareSceneDownsampleChain); } if (bLensFlareEnabled) { const ELensFlareQuality LensFlareQuality = GetLensFlareQuality(); const uint32 LensFlareDownsampleStageIndex = static_cast(ELensFlareQuality::MAX) - static_cast(LensFlareQuality) - 1; Bloom = AddLensFlaresPass(GraphBuilder, View, Bloom, LensFlareSceneDownsampleChain->GetTexture(LensFlareDownsampleStageIndex), LensFlareSceneDownsampleChain->GetFirstTexture()); } } SceneColorBeforeTonemapSlice = SceneColorSlice; if (PassSequence.IsEnabled(EPass::Tonemap)) { const FPostProcessingPassDelegateArray& ReplacingTonemapperDelegates = SceneViewExtensionDelegates[static_cast(ISceneViewExtension::EPostProcessingPass::ReplacingTonemapper)]; const FPostProcessMaterialChain MaterialChain = GetPostProcessMaterialChain(View, BL_ReplacingTonemapper); // GPU Skin Cache for next frame can overlap with the tone-mapping pass. if (FGPUSkinCache* GPUSkinCache = Scene->GetGPUSkinCache(); GPUSkinCache && View.IsLastInFamily()) { GPUSkinCache->AddAsyncComputeSignal(GraphBuilder); } auto GetReplaceTonemapperInputs = [&]() -> FPostProcessMaterialInputs { FPostProcessMaterialInputs PassInputs; PassSequence.AcceptOverrideIfLastPass(EPass::Tonemap, PassInputs.OverrideOutput); PassInputs.SetInput(GraphBuilder, EPostProcessMaterialInput::SceneColor, FScreenPassTexture::CopyFromSlice(GraphBuilder, SceneColorSlice)); PassInputs.SetInput(GraphBuilder, EPostProcessMaterialInput::CombinedBloom, Bloom); PassInputs.SceneTextures = GetSceneTextureShaderParameters(Inputs.SceneTextures); PassInputs.CustomDepthTexture = CustomDepth.Texture; PassInputs.bManualStencilTest = Inputs.bSeparateCustomStencil; return PassInputs; }; if(ReplacingTonemapperDelegates.Num()) { const FAfterPassCallbackDelegate& HighestPriorityDelegate = ReplacingTonemapperDelegates[0]; SceneColor = HighestPriorityDelegate.Execute(GraphBuilder, View, GetReplaceTonemapperInputs()); } else if (MaterialChain.Num()) { const UMaterialInterface* HighestPriorityMaterial = MaterialChain[0]; SceneColor = AddPostProcessMaterialPass(GraphBuilder, View, GetReplaceTonemapperInputs(), HighestPriorityMaterial); } else { FRDGTextureRef ColorGradingTexture = nullptr; if (bPrimaryView) { ColorGradingTexture = AddCombineLUTPass(GraphBuilder, View); } // We can re-use the color grading texture from the primary view. else if (View.GetTonemappingLUT()) { ColorGradingTexture = TryRegisterExternalTexture(GraphBuilder, View.GetTonemappingLUT()); } else { const FViewInfo* PrimaryView = static_cast(View.Family->Views[0]); ColorGradingTexture = TryRegisterExternalTexture(GraphBuilder, PrimaryView->GetTonemappingLUT()); } FTonemapInputs PassInputs; PassSequence.AcceptOverrideIfLastPass(EPass::Tonemap, PassInputs.OverrideOutput); PassInputs.SceneColor = SceneColorSlice; PassInputs.Bloom = Bloom; PassInputs.SceneColorApplyParamaters = SceneColorApplyParameters; PassInputs.LocalExposureBilateralGridTexture = LocalExposureBilateralGridTexture; PassInputs.BlurredLogLuminanceTexture = LocalExposureBlurredLogLumTexture; PassInputs.ExposureFusion = FScreenPassTextureSlice::CreateFromScreenPassTexture(GraphBuilder, ExposureFusionData.Result); PassInputs.LocalExposureParameters = &LocalExposureParameters; PassInputs.EyeAdaptationParameters = &EyeAdaptationParameters; PassInputs.EyeAdaptationBuffer = EyeAdaptationBuffer; PassInputs.ColorGradingTexture = ColorGradingTexture; PassInputs.bWriteAlphaChannel = AntiAliasingMethod == AAM_FXAA || bProcessSceneColorAlpha; PassInputs.bOutputInHDR = bTonemapOutputInHDR; SceneColor = AddTonemapPass(GraphBuilder, View, PassInputs); } } else { SceneColor = FScreenPassTexture(SceneColorSlice); } SceneColor = AddAfterPass(EPass::Tonemap, SceneColor); SceneColorAfterTonemap = SceneColor; if (PassSequence.IsEnabled(EPass::FXAA)) { FFXAAInputs PassInputs; PassSequence.AcceptOverrideIfLastPass(EPass::FXAA, PassInputs.OverrideOutput); PassInputs.SceneColor = SceneColor; PassInputs.Quality = GetFXAAQuality(); SceneColor = AddFXAAPass(GraphBuilder, View, PassInputs); } SceneColor = AddAfterPass(EPass::FXAA, SceneColor); // Post Process Material Chain - After Tonemapping if (PassSequence.IsEnabled(EPass::PostProcessMaterialAfterTonemapping)) { FPostProcessMaterialInputs PassInputs = GetPostProcessMaterialInputs(SceneColor); PassSequence.AcceptOverrideIfLastPass(EPass::PostProcessMaterialAfterTonemapping, PassInputs.OverrideOutput); PassInputs.SetInput(GraphBuilder, EPostProcessMaterialInput::PreTonemapHDRColor, FScreenPassTexture::CopyFromSlice(GraphBuilder, SceneColorBeforeTonemapSlice)); PassInputs.SetInput(GraphBuilder, EPostProcessMaterialInput::PostTonemapHDRColor, SceneColorAfterTonemap); PassInputs.SceneTextures = GetSceneTextureShaderParameters(Inputs.SceneTextures); SceneColor = AddPostProcessMaterialChain(GraphBuilder, View, ViewIndex, PassInputs, PostProcessMaterialAfterTonemappingChain); } if (PassSequence.IsEnabled(EPass::VisualizeLumenScene)) { FVisualizeLumenSceneInputs PassInputs; PassSequence.AcceptOverrideIfLastPass(EPass::VisualizeLumenScene, PassInputs.OverrideOutput); PassInputs.SceneColor = SceneColor; PassInputs.SceneDepth = SceneDepth; PassInputs.ColorGradingTexture = TryRegisterExternalTexture(GraphBuilder, View.GetTonemappingLUT()); PassInputs.EyeAdaptationBuffer = GetEyeAdaptationBuffer(GraphBuilder, View); PassInputs.SceneTextures.SceneTextures = Inputs.SceneTextures; SceneColor = AddVisualizeLumenScenePass(GraphBuilder, View, bAnyLumenActive, DiffuseIndirectMethod, PassInputs, LumenFrameTemporaries); } if (PassSequence.IsEnabled(EPass::VisualizeDepthOfField)) { FVisualizeDOFInputs PassInputs; PassSequence.AcceptOverrideIfLastPass(EPass::VisualizeDepthOfField, PassInputs.OverrideOutput); PassInputs.SceneColor = SceneColor; PassInputs.SceneDepth = SceneDepth; SceneColor = AddVisualizeDOFPass(GraphBuilder, View, PassInputs); } SceneColor = AddAfterPass(EPass::VisualizeDepthOfField, SceneColor); } // Minimal PostProcessing - Separate translucency composition and gamma-correction only. else { PassSequence.SetEnabled(EPass::MotionBlur, false); PassSequence.SetEnabled(EPass::PostProcessMaterialBeforeBloom, false); PassSequence.SetEnabled(EPass::Tonemap, true); PassSequence.SetEnabled(EPass::FXAA, false); PassSequence.SetEnabled(EPass::PostProcessMaterialAfterTonemapping, false); PassSequence.SetEnabled(EPass::VisualizeDepthOfField, false); PassSequence.SetEnabled(EPass::VisualizeLocalExposure, false); PassSequence.Finalize(); // Compose separate translucency passes { FTranslucencyComposition TranslucencyComposition; TranslucencyComposition.Operation = FTranslucencyComposition::EOperation::ComposeToNewSceneColor; TranslucencyComposition.OutputViewport = FScreenPassTextureViewport(SceneColor); TranslucencyComposition.OutputPixelFormat = SceneColorFormat; if (PostDOFTranslucencyResources.IsValid()) { TranslucencyComposition.SceneColor = FScreenPassTextureSlice::CreateFromScreenPassTexture(GraphBuilder, SceneColor); SceneColor = TranslucencyComposition.AddPass( GraphBuilder, View, PostDOFTranslucencyResources); } if (PostMotionBlurTranslucencyResources.IsValid()) { TranslucencyComposition.SceneColor = FScreenPassTextureSlice::CreateFromScreenPassTexture(GraphBuilder, SceneColor); SceneColor = TranslucencyComposition.AddPass( GraphBuilder, View, PostMotionBlurTranslucencyResources); } } SceneColorBeforeTonemapSlice = FScreenPassTextureSlice::CreateFromScreenPassTexture(GraphBuilder, SceneColor); if (PassSequence.IsEnabled(EPass::Tonemap)) { FTonemapInputs PassInputs; PassSequence.AcceptOverrideIfLastPass(EPass::Tonemap, PassInputs.OverrideOutput); PassInputs.SceneColor = FScreenPassTextureSlice::CreateFromScreenPassTexture(GraphBuilder, SceneColor); PassInputs.EyeAdaptationParameters = &EyeAdaptationParameters; PassInputs.EyeAdaptationBuffer = EyeAdaptationBuffer; PassInputs.bOutputInHDR = bViewFamilyOutputInHDR; PassInputs.bGammaOnly = true; SceneColor = AddTonemapPass(GraphBuilder, View, PassInputs); } SceneColor = AddAfterPass(EPass::Tonemap, SceneColor); SceneColorAfterTonemap = SceneColor; } if (PassSequence.IsEnabled(EPass::VisualizeStationaryLightOverlap)) { ensureMsgf(View.PrimaryScreenPercentageMethod != EPrimaryScreenPercentageMethod::TemporalUpscale, TEXT("TAAU should be disabled when visualizing stationary light overlap.")); FVisualizeComplexityInputs PassInputs; PassSequence.AcceptOverrideIfLastPass(EPass::VisualizeStationaryLightOverlap, PassInputs.OverrideOutput); PassInputs.SceneColor = OriginalSceneColor; PassInputs.Colors = GEngine->StationaryLightOverlapColors; PassInputs.ColorSamplingMethod = FVisualizeComplexityInputs::EColorSamplingMethod::Ramp; PassInputs.bDrawLegend = true; SceneColor = AddVisualizeComplexityPass(GraphBuilder, View, PassInputs); } if (PassSequence.IsEnabled(EPass::VisualizeLightCulling)) { ensureMsgf(View.PrimaryScreenPercentageMethod != EPrimaryScreenPercentageMethod::TemporalUpscale, TEXT("TAAU should be disabled when visualizing light culling.")); // 0.1f comes from the values used in LightAccumulator_GetResult const float ComplexityScale = 1.0f / (float)(GEngine->LightComplexityColors.Num() - 1) / 0.1f; FVisualizeComplexityInputs PassInputs; PassSequence.AcceptOverrideIfLastPass(EPass::VisualizeLightCulling, PassInputs.OverrideOutput); PassInputs.SceneColor = OriginalSceneColor; PassInputs.Colors = GEngine->LightComplexityColors; PassInputs.ColorSamplingMethod = FVisualizeComplexityInputs::EColorSamplingMethod::Linear; PassInputs.ComplexityScale = ComplexityScale; PassInputs.bDrawLegend = true; SceneColor = AddVisualizeComplexityPass(GraphBuilder, View, PassInputs); } #if DEBUG_POST_PROCESS_VOLUME_ENABLE if (PassSequence.IsEnabled(EPass::VisualizePostProcessStack)) { FScreenPassRenderTarget OverrideOutput; PassSequence.AcceptOverrideIfLastPass(EPass::VisualizePostProcessStack, OverrideOutput); OverrideOutput = OverrideOutput.IsValid() ? OverrideOutput : FScreenPassRenderTarget::CreateFromInput(GraphBuilder, SceneColor, View.GetOverwriteLoadAction(), TEXT("VisualizePostProcessStack")); SceneColor = AddFinalPostProcessDebugInfoPasses(GraphBuilder, View, OverrideOutput); } #endif if (PassSequence.IsEnabled(EPass::VisualizeSubstrate)) { FScreenPassRenderTarget OverrideOutput; PassSequence.AcceptOverrideIfLastPass(EPass::VisualizeSubstrate, OverrideOutput); FScreenPassTexture DebugColorOutput = Substrate::AddSubstrateDebugPasses(GraphBuilder, View, SceneColor); if (OverrideOutput.IsValid()) { AddDrawTexturePass(GraphBuilder, View, DebugColorOutput, OverrideOutput); SceneColor = OverrideOutput; } else { SceneColor = DebugColorOutput; } } if (PassSequence.IsEnabled(EPass::VisualizeLightGrid)) { FScreenPassRenderTarget OverrideOutput; PassSequence.AcceptOverrideIfLastPass(EPass::VisualizeLightGrid, OverrideOutput); SceneColor = AddVisualizeLightGridPass(GraphBuilder, View, SceneColor, SceneDepth); } #if WITH_EDITOR if (PassSequence.IsEnabled(EPass::VisualizeSkyAtmosphere)) { FScreenPassRenderTarget OverrideOutput; PassSequence.AcceptOverrideIfLastPass(EPass::VisualizeSkyAtmosphere, OverrideOutput); SceneColor = AddSkyAtmosphereDebugPasses(GraphBuilder, Scene, *View.Family, View, SceneColor); } if (PassSequence.IsEnabled(EPass::VisualizeSkyLightIlluminanceMeter)) { FScreenPassRenderTarget OverrideOutput; PassSequence.AcceptOverrideIfLastPass(EPass::VisualizeSkyLightIlluminanceMeter, OverrideOutput); SceneColor = ProcessAndRenderIlluminanceMeter(GraphBuilder, View, SceneColor); } if (PassSequence.IsEnabled(EPass::VisualizeLightFunctionAtlas)) { FScreenPassRenderTarget OverrideOutput; PassSequence.AcceptOverrideIfLastPass(EPass::VisualizeLightFunctionAtlas, OverrideOutput); if (Scene->LightFunctionAtlasSceneData.GetLightFunctionAtlas()) { SceneColor = Scene->LightFunctionAtlasSceneData.GetLightFunctionAtlas()->AddDebugVisualizationPasses(GraphBuilder, View, SceneColor); } } if (PassSequence.IsEnabled(EPass::VisualizeLevelInstance)) { FVisualizeLevelInstanceInputs PassInputs; PassSequence.AcceptOverrideIfLastPass(EPass::VisualizeLevelInstance, PassInputs.OverrideOutput); PassInputs.SceneColor = SceneColor; PassInputs.SceneDepth = SceneDepth; PassInputs.SceneTextures.SceneTextures = Inputs.SceneTextures; SceneColor = AddVisualizeLevelInstancePass(GraphBuilder, View, SceneUniformBuffer, PassInputs, NaniteRasterResults); } #endif //WITH_EDITOR if (PassSequence.IsEnabled(EPass::VisualizeVirtualShadowMaps_PreEditorPrimitives)) { FScreenPassRenderTarget OverrideOutput; PassSequence.AcceptOverrideIfLastPass(EPass::VisualizeVirtualShadowMaps_PreEditorPrimitives, OverrideOutput); SceneColor = VirtualShadowMapArray->AddVisualizePass(GraphBuilder, View, ViewIndex, EVSMVisualizationPostPass::PreEditorPrimitives, SceneColor, OverrideOutput); } #if WITH_EDITOR || !UE_BUILD_SHIPPING if (EngineShowFlags.VisualizeNanite && NaniteRasterResults != nullptr) { AddVisualizeNanitePass(GraphBuilder, View, SceneColor, *NaniteRasterResults); } #endif #if WITH_EDITOR if (PassSequence.IsEnabled(EPass::SelectionOutline)) { FSelectionOutlineInputs PassInputs; PassSequence.AcceptOverrideIfLastPass(EPass::SelectionOutline, PassInputs.OverrideOutput); PassInputs.SceneColor = SceneColor; PassInputs.SceneDepth = SceneDepth; PassInputs.SceneTextures.SceneTextures = Inputs.SceneTextures; if (bApplyLensDistortionInTSR) { PassInputs.LensDistortionLUT = View.LensDistortionLUT; } SceneColor = AddSelectionOutlinePass(GraphBuilder, View, SceneUniformBuffer, PassInputs, NaniteRasterResults, InstancedEditorDepthTexture); } if (PassSequence.IsEnabled(EPass::EditorPrimitive)) { FCompositePrimitiveInputs PassInputs; if (PassSequence.AcceptOverrideIfLastPass(EPass::EditorPrimitive, PassInputs.OverrideOutput)) { PassInputs.OverrideDepthOutput = ViewFamilyDepthOutput; } PassInputs.SceneColor = SceneColor; PassInputs.SceneDepth = SceneDepth; PassInputs.BasePassType = FCompositePrimitiveInputs::EBasePassType::Deferred; if (bApplyLensDistortionInTSR) { PassInputs.LensDistortionLUT = View.LensDistortionLUT; } SceneColor = AddEditorPrimitivePass(GraphBuilder, View, PassInputs, InstanceCullingManager); } #endif //WITH_EDITOR if (PassSequence.IsEnabled(EPass::VisualizeVirtualShadowMaps_PostEditorPrimitives)) { FScreenPassRenderTarget OverrideOutput; PassSequence.AcceptOverrideIfLastPass(EPass::VisualizeVirtualShadowMaps_PostEditorPrimitives, OverrideOutput); SceneColor = VirtualShadowMapArray->AddVisualizePass(GraphBuilder, View, ViewIndex, EVSMVisualizationPostPass::PostEditorPrimitives, SceneColor, OverrideOutput); } if (PassSequence.IsEnabled(EPass::VisualizeVirtualTexture)) { if (FRDGBuffer* DebugBuffer = VirtualTexture::ResolveExtendedDebugBuffer(GraphBuilder)) { FVisualizeVirtualTextureInputs PassInputs; PassSequence.AcceptOverrideIfLastPass(EPass::VisualizeVirtualTexture, PassInputs.OverrideOutput); PassInputs.SceneColor = SceneColor; PassInputs.DebugBuffer = DebugBuffer; PassInputs.ModeName = GetVirtualTextureVisualizationData().GetActiveMode(View); PassInputs.Colors = GEngine->ShaderComplexityColors; SceneColor = AddVisualizeVirtualTexturePass(GraphBuilder, View, PassInputs); } } if (PassSequence.IsEnabled(EPass::VisualizeShadingModels)) { ensureMsgf(View.PrimaryScreenPercentageMethod != EPrimaryScreenPercentageMethod::TemporalUpscale, TEXT("TAAU should be disabled when visualizing shading models.")); FVisualizeShadingModelInputs PassInputs; PassSequence.AcceptOverrideIfLastPass(EPass::VisualizeShadingModels, PassInputs.OverrideOutput); PassInputs.SceneColor = SceneColor; PassInputs.SceneTextures = Inputs.SceneTextures; SceneColor = AddVisualizeShadingModelPass(GraphBuilder, View, PassInputs); } if (PassSequence.IsEnabled(EPass::VisualizeGBufferHints)) { ensureMsgf(View.PrimaryScreenPercentageMethod != EPrimaryScreenPercentageMethod::TemporalUpscale, TEXT("TAAU should be disabled when visualizing gbuffer hints.")); FVisualizeGBufferHintsInputs PassInputs; PassSequence.AcceptOverrideIfLastPass(EPass::VisualizeGBufferHints, PassInputs.OverrideOutput); PassInputs.SceneColor = SceneColor; PassInputs.OriginalSceneColor = OriginalSceneColor; PassInputs.SceneTextures = Inputs.SceneTextures; SceneColor = AddVisualizeGBufferHintsPass(GraphBuilder, View, PassInputs); } if (PassSequence.IsEnabled(EPass::VisualizeSubsurface)) { ensureMsgf(View.PrimaryScreenPercentageMethod != EPrimaryScreenPercentageMethod::TemporalUpscale, TEXT("TAAU should be disabled when visualizing subsurface.")); FVisualizeSubsurfaceInputs PassInputs; PassSequence.AcceptOverrideIfLastPass(EPass::VisualizeSubsurface, PassInputs.OverrideOutput); PassInputs.SceneColor = SceneColor; PassInputs.SceneTextures = Inputs.SceneTextures; SceneColor = AddVisualizeSubsurfacePass(GraphBuilder, View, PassInputs); } if (PassSequence.IsEnabled(EPass::VisualizeGBufferOverview)) { FVisualizeGBufferOverviewInputs PassInputs; PassSequence.AcceptOverrideIfLastPass(EPass::VisualizeGBufferOverview, PassInputs.OverrideOutput); PassInputs.SceneColor = SceneColor; PassInputs.SceneColorBeforeTonemap = FScreenPassTexture::CopyFromSlice(GraphBuilder, SceneColorBeforeTonemapSlice); PassInputs.SceneColorAfterTonemap = SceneColorAfterTonemap; FIntRect ViewRect{ 0, 0, 1, 1 }; if (PostDOFTranslucencyResources.IsValid()) { ViewRect = PostDOFTranslucencyResources.ViewRect; } PassInputs.SeparateTranslucency = FScreenPassTexture(PostDOFTranslucencyResources.GetColorForRead(GraphBuilder), ViewRect); // TODO PassInputs.Velocity = Velocity; PassInputs.SceneTextures = GetSceneTextureShaderParameters(Inputs.SceneTextures); PassInputs.bOverview = bVisualizeGBufferOverview; PassInputs.bDumpToFile = bVisualizeGBufferDumpToFile; PassInputs.bOutputInHDR = bOutputInHDR; PassInputs.PathTracingResources = &Inputs.PathTracingResources; SceneColor = AddVisualizeGBufferOverviewPass(GraphBuilder, View, PassInputs); } if (PassSequence.IsEnabled(EPass::VisualizeLumenSceneOverview)) { FVisualizeLumenSceneInputs PassInputs; PassSequence.AcceptOverrideIfLastPass(EPass::VisualizeLumenSceneOverview, PassInputs.OverrideOutput); PassInputs.SceneColor = SceneColor; PassInputs.SceneDepth = SceneDepth; PassInputs.ColorGradingTexture = TryRegisterExternalTexture(GraphBuilder, View.GetTonemappingLUT()); PassInputs.EyeAdaptationBuffer = GetEyeAdaptationBuffer(GraphBuilder, View); PassInputs.SceneTextures.SceneTextures = Inputs.SceneTextures; SceneColor = AddVisualizeLumenScenePass(GraphBuilder, View, bAnyLumenActive, DiffuseIndirectMethod, PassInputs, LumenFrameTemporaries); } if (PassSequence.IsEnabled(EPass::VisualizeHDR)) { FVisualizeHDRInputs PassInputs; PassSequence.AcceptOverrideIfLastPass(EPass::VisualizeHDR, PassInputs.OverrideOutput); PassInputs.SceneColor = SceneColor; PassInputs.SceneColorBeforeTonemap = FScreenPassTexture::CopyFromSlice(GraphBuilder, SceneColorBeforeTonemapSlice); PassInputs.Luminance = ExposureIlluminance; PassInputs.HistogramTexture = HistogramTexture; PassInputs.EyeAdaptationBuffer = GetEyeAdaptationBuffer(GraphBuilder, View); PassInputs.EyeAdaptationParameters = &EyeAdaptationParameters; SceneColor = AddVisualizeHDRPass(GraphBuilder, View, PassInputs); } if (PassSequence.IsEnabled(EPass::VisualizeLocalExposure)) { FVisualizeLocalExposureInputs PassInputs; PassSequence.AcceptOverrideIfLastPass(EPass::VisualizeLocalExposure, PassInputs.OverrideOutput); PassInputs.SceneColor = SceneColor; PassInputs.HDRSceneColor = FScreenPassTexture::CopyFromSlice(GraphBuilder, SceneColorBeforeTonemapSlice); PassInputs.LumBilateralGridTexture = LocalExposureBilateralGridTexture; PassInputs.BlurredLumTexture = LocalExposureBlurredLogLumTexture; PassInputs.ExposureFusionData = View.FinalPostProcessSettings.LocalExposureMethod == ELocalExposureMethod::Fusion ? &ExposureFusionData : nullptr; PassInputs.LocalExposureParameters = &LocalExposureParameters; PassInputs.EyeAdaptationBuffer = GetEyeAdaptationBuffer(GraphBuilder, View); PassInputs.EyeAdaptationParameters = &EyeAdaptationParameters; SceneColor = AddVisualizeLocalExposurePass(GraphBuilder, View, PassInputs); } if (PassSequence.IsEnabled(EPass::VisualizeMotionVectors)) { FVisualizeMotionVectorsInputs PassInputs; PassSequence.AcceptOverrideIfLastPass(EPass::VisualizeMotionVectors, PassInputs.OverrideOutput); PassInputs.SceneColor = SceneColor; PassInputs.SceneDepth = SceneDepth; PassInputs.SceneVelocity = Velocity; if (bApplyLensDistortionInTSR) { PassInputs.LensDistortionLUT = View.LensDistortionLUT; } SceneColor = AddVisualizeMotionVectorsPass(GraphBuilder, View, PassInputs, EVisualizeMotionVectors::ReprojectionAlignment); } if (PassSequence.IsEnabled(EPass::VisualizeTemporalUpscaler)) { PassSequence.AcceptOverrideIfLastPass(EPass::VisualizeTemporalUpscaler, VisualizeTemporalUpscalerInputs.OverrideOutput); VisualizeTemporalUpscalerInputs.SceneColor = SceneColor; SceneColor = AddVisualizeTemporalUpscalerPass(GraphBuilder, View, VisualizeTemporalUpscalerInputs); } #if WITH_EDITOR if (PassSequence.IsEnabled(EPass::PixelInspector)) { FPixelInspectorInputs PassInputs; PassSequence.AcceptOverrideIfLastPass(EPass::PixelInspector, PassInputs.OverrideOutput); PassInputs.SceneColor = SceneColor; PassInputs.SceneColorBeforeTonemap = FScreenPassTexture::CopyFromSlice(GraphBuilder, SceneColorBeforeTonemapSlice); PassInputs.OriginalSceneColor = OriginalSceneColor; SceneColor = AddPixelInspectorPass(GraphBuilder, View, PassInputs); } #endif if (PassSequence.IsEnabled(EPass::HMDDistortion)) { FHMDDistortionInputs PassInputs; PassSequence.AcceptOverrideIfLastPass(EPass::HMDDistortion, PassInputs.OverrideOutput); PassInputs.SceneColor = SceneColor; SceneColor = AddHMDDistortionPass(GraphBuilder, View, PassInputs); } if (PassSequence.IsEnabled(EPass::HighResolutionScreenshotMask)) { FHighResolutionScreenshotMaskInputs PassInputs; PassSequence.AcceptOverrideIfLastPass(EPass::HighResolutionScreenshotMask, PassInputs.OverrideOutput); PassInputs.SceneTextures = GetSceneTextureShaderParameters(Inputs.SceneTextures); PassInputs.SceneColor = SceneColor; PassInputs.Material = View.FinalPostProcessSettings.HighResScreenshotMaterial; PassInputs.MaskMaterial = View.FinalPostProcessSettings.HighResScreenshotMaskMaterial; PassInputs.CaptureRegionMaterial = View.FinalPostProcessSettings.HighResScreenshotCaptureRegionMaterial; SceneColor = AddHighResolutionScreenshotMaskPass(GraphBuilder, View, PassInputs); } #if UE_ENABLE_DEBUG_DRAWING if (PassSequence.IsEnabled(EPass::DebugPrimitive)) //Create new debug pass sequence { FCompositePrimitiveInputs PassInputs; PassSequence.AcceptOverrideIfLastPass(EPass::DebugPrimitive, PassInputs.OverrideOutput); PassInputs.SceneColor = SceneColor; PassInputs.SceneDepth = SceneDepth; SceneColor = AddDebugPrimitivePass(GraphBuilder, View, PassInputs); } #endif if (PassSequence.IsEnabled(EPass::PrimaryUpscale)) { ISpatialUpscaler::FInputs PassInputs; PassSequence.AcceptOverrideIfLastPass(EPass::PrimaryUpscale, PassInputs.OverrideOutput); PassInputs.SceneColor = SceneColor; PassInputs.Stage = PassSequence.IsEnabled(EPass::SecondaryUpscale) ? EUpscaleStage::PrimaryToSecondary : EUpscaleStage::PrimaryToOutput; const ISpatialUpscaler* CustomUpscaler = View.Family ? View.Family->GetPrimarySpatialUpscalerInterface() : nullptr; if (CustomUpscaler) { RDG_EVENT_SCOPE( GraphBuilder, "ThirdParty PrimaryUpscale %s %dx%d -> %dx%d", CustomUpscaler->GetDebugName(), SceneColor.ViewRect.Width(), SceneColor.ViewRect.Height(), View.GetSecondaryViewRectSize().X, View.GetSecondaryViewRectSize().Y); SceneColor = CustomUpscaler->AddPasses(GraphBuilder, View, PassInputs); if (PassSequence.IsLastPass(EPass::PrimaryUpscale)) { check(SceneColor == ViewFamilyOutput); } else { check(SceneColor.ViewRect.Size() == View.GetSecondaryViewRectSize()); } } else { EUpscaleMethod Method = GetUpscaleMethod(); SceneColor = ISpatialUpscaler::AddDefaultUpscalePass(GraphBuilder, View, PassInputs, Method, View.LensDistortionLUT); } } if (PassSequence.IsEnabled(EPass::SecondaryUpscale)) { ISpatialUpscaler::FInputs PassInputs; PassSequence.AcceptOverrideIfLastPass(EPass::SecondaryUpscale, PassInputs.OverrideOutput); PassInputs.SceneColor = SceneColor; PassInputs.Stage = EUpscaleStage::SecondaryToOutput; const ISpatialUpscaler* CustomUpscaler = View.Family ? View.Family->GetSecondarySpatialUpscalerInterface() : nullptr; if (CustomUpscaler) { RDG_EVENT_SCOPE( GraphBuilder, "ThirdParty SecondaryUpscale %s %dx%d -> %dx%d", CustomUpscaler->GetDebugName(), SceneColor.ViewRect.Width(), SceneColor.ViewRect.Height(), View.UnscaledViewRect.Width(), View.UnscaledViewRect.Height()); SceneColor = CustomUpscaler->AddPasses(GraphBuilder, View, PassInputs); check(SceneColor == ViewFamilyOutput); } else { EUpscaleMethod Method = View.Family && View.Family->SecondaryScreenPercentageMethod == ESecondaryScreenPercentageMethod::LowerPixelDensitySimulation ? EUpscaleMethod::SmoothStep : EUpscaleMethod::Nearest; SceneColor = ISpatialUpscaler::AddDefaultUpscalePass(GraphBuilder, View, PassInputs, Method); } } #if WITH_EDITOR || !UE_BUILD_SHIPPING { // Draw debug stuff directly onto the back buffer RDG_EVENT_SCOPE(GraphBuilder, "Debug Drawing"); if (EngineShowFlags.TestImage) { AddTestImagePass(GraphBuilder, View, SceneColor); } #if WITH_EDITOR if (CVarGBufferPicking.GetValueOnRenderThread()) { AddGBufferPicking(GraphBuilder, View, Inputs.SceneTextures); } #endif { RectLightAtlas::AddDebugPass(GraphBuilder, View, SceneColor.Texture); IESAtlas::AddDebugPass(GraphBuilder, View, SceneColor.Texture); } // Piggy back off of OnScreenDebug to avoid having to create a new show flag just for this simple debug visualization. Otherwise it might render into certain thumbnails. // In the future it might be worth it to introduce a show flag? if (EngineShowFlags.OnScreenDebug) { UE::SVT::AddStreamingDebugPass(GraphBuilder, View, SceneColor); } if (ShaderPrint::IsEnabled(View.ShaderPrintData)) { ShaderPrint::DrawView(GraphBuilder, View, SceneColor, SceneDepth); } if (View.Family && View.Family->Scene) { if (FFXSystemInterface* FXSystem = View.Family->Scene->GetFXSystem()) { FXSystem->DrawSceneDebug_RenderThread(GraphBuilder, (const FSceneView&)View, SceneColor.Texture, SceneDepth.Texture); } } } #endif #if !UE_BUILD_SHIPPING AddUserSceneTextureDebugPass(GraphBuilder, View, ViewIndex, SceneColor); #endif if (PassSequence.IsEnabled(EPass::AlphaInvert)) { AlphaInvert::FAlphaInvertInputs PassInputs; PassSequence.AcceptOverrideIfLastPass(EPass::AlphaInvert, PassInputs.OverrideOutput); PassInputs.SceneColor = SceneColor; SceneColor = AlphaInvert::AddAlphaInvertPass(GraphBuilder, View, PassInputs); } } void AddDebugViewPostProcessingPasses(FRDGBuilder& GraphBuilder, const FViewInfo& View, FSceneUniformBuffer &SceneUniformBuffer, const FPostProcessingInputs& Inputs, const Nanite::FRasterResults* NaniteRasterResults) { RDG_CSV_STAT_EXCLUSIVE_SCOPE(GraphBuilder, RenderPostProcessing); QUICK_SCOPE_CYCLE_COUNTER(STAT_PostProcessing_Process); check(IsInRenderingThread()); #if DO_CHECK || USING_CODE_ANALYSIS check(View.VerifyMembersChecks()); #endif Inputs.Validate(); const FIntRect PrimaryViewRect = View.ViewRect; const FSceneTextureParameters SceneTextureParameters = GetSceneTextureParameters(GraphBuilder, Inputs.SceneTextures); const FScreenPassRenderTarget ViewFamilyOutput = FScreenPassRenderTarget::CreateViewFamilyOutput(Inputs.ViewFamilyTexture, View); const FScreenPassRenderTarget ViewFamilyDepthOutput = FScreenPassRenderTarget::CreateViewFamilyOutput(Inputs.ViewFamilyDepthTexture, View); const FScreenPassTexture SceneDepth(SceneTextureParameters.SceneDepthTexture, PrimaryViewRect); FScreenPassTexture SceneColor((*Inputs.SceneTextures)->SceneColorTexture, PrimaryViewRect); // Some view modes do not actually output a color so they should not be tonemapped. const bool bTonemapAfter = View.Family->EngineShowFlags.RayTracingDebug || View.Family->EngineShowFlags.VisualizeGPUSkinCache; const bool bTonemapBefore = !bTonemapAfter && !View.Family->EngineShowFlags.ShaderComplexity; const bool bViewFamilyOutputInHDR = View.Family->RenderTarget->GetSceneHDREnabled(); enum class EPass : uint32 { Visualize, TonemapAfter, SelectionOutline, PrimaryUpscale, SecondaryUpscale, MAX }; const TCHAR* PassNames[] = { TEXT("Visualize"), TEXT("TonemapAfter"), TEXT("SelectionOutline"), TEXT("PrimaryUpscale"), TEXT("SecondaryUpscale") }; static_assert(static_cast(EPass::MAX) == UE_ARRAY_COUNT(PassNames), "EPass does not match PassNames."); TOverridePassSequence PassSequence(ViewFamilyOutput); PassSequence.SetNames(PassNames, UE_ARRAY_COUNT(PassNames)); PassSequence.SetEnabled(EPass::Visualize, true); PassSequence.SetEnabled(EPass::TonemapAfter, bTonemapAfter); PassSequence.SetEnabled(EPass::SelectionOutline, GIsEditor); PassSequence.SetEnabled(EPass::PrimaryUpscale, View.ViewRect.Size() != View.GetSecondaryViewRectSize() && View.PrimaryScreenPercentageMethod != EPrimaryScreenPercentageMethod::TemporalUpscale); PassSequence.SetEnabled(EPass::SecondaryUpscale, View.RequiresSecondaryUpscale() || View.Family->GetSecondarySpatialUpscalerInterface() != nullptr); PassSequence.Finalize(); const FEyeAdaptationParameters EyeAdaptationParameters = GetEyeAdaptationParameters(View); if (bTonemapBefore) { FTonemapInputs PassInputs; PassInputs.SceneColor = FScreenPassTextureSlice::CreateFromScreenPassTexture(GraphBuilder, SceneColor); PassInputs.bOutputInHDR = bViewFamilyOutputInHDR; PassInputs.bGammaOnly = true; PassInputs.EyeAdaptationParameters = &EyeAdaptationParameters; SceneColor = AddTonemapPass(GraphBuilder, View, PassInputs); } check(PassSequence.IsEnabled(EPass::Visualize)); { FScreenPassRenderTarget OverrideOutput; PassSequence.AcceptOverrideIfLastPass(EPass::Visualize, OverrideOutput); switch (View.Family->GetDebugViewShaderMode()) { case DVSM_QuadComplexity: { float ComplexityScale = 1.f / (float)(GEngine->QuadComplexityColors.Num() - 1) / NormalizedQuadComplexityValue; // .1f comes from the values used in LightAccumulator_GetResult FVisualizeComplexityInputs PassInputs; PassInputs.OverrideOutput = OverrideOutput; PassInputs.SceneColor = SceneColor; PassInputs.Colors = GEngine->QuadComplexityColors; PassInputs.ColorSamplingMethod = FVisualizeComplexityInputs::EColorSamplingMethod::Stair; PassInputs.ComplexityScale = ComplexityScale; PassInputs.bDrawLegend = true; SceneColor = AddVisualizeComplexityPass(GraphBuilder, View, PassInputs); break; } case DVSM_ShaderComplexity: case DVSM_ShaderComplexityContainedQuadOverhead: case DVSM_ShaderComplexityBleedingQuadOverhead: case DVSM_LWCComplexity: { FVisualizeComplexityInputs PassInputs; PassInputs.OverrideOutput = OverrideOutput; PassInputs.SceneColor = SceneColor; PassInputs.Colors = GEngine->ShaderComplexityColors; PassInputs.ColorSamplingMethod = FVisualizeComplexityInputs::EColorSamplingMethod::Ramp; PassInputs.ComplexityScale = 1.0f; PassInputs.bDrawLegend = true; SceneColor = AddVisualizeComplexityPass(GraphBuilder, View, PassInputs); break; } case DVSM_PrimitiveDistanceAccuracy: case DVSM_MeshUVDensityAccuracy: case DVSM_MaterialTextureScaleAccuracy: case DVSM_RequiredTextureResolution: { FStreamingAccuracyLegendInputs PassInputs; PassInputs.OverrideOutput = OverrideOutput; PassInputs.SceneColor = SceneColor; PassInputs.Colors = GEngine->StreamingAccuracyColors; SceneColor = AddStreamingAccuracyLegendPass(GraphBuilder, View, PassInputs); break; } case DVSM_VisualizeGPUSkinCache: { FTAAPassParameters Parameters(View); Parameters.SceneDepthTexture = SceneTextureParameters.SceneDepthTexture; Parameters.SceneVelocityTexture = SceneTextureParameters.GBufferVelocityTexture; Parameters.SceneColorInput = SceneColor.Texture; Parameters.Pass = View.PrimaryScreenPercentageMethod == EPrimaryScreenPercentageMethod::TemporalUpscale ? ETAAPassConfig::MainUpsampling : ETAAPassConfig::Main; Parameters.SetupViewRect(View); const FTemporalAAHistory& InputHistory = View.PrevViewInfo.TemporalAAHistory; FTemporalAAHistory* OutputHistory = &View.ViewState->PrevFrameViewInfo.TemporalAAHistory; FTAAOutputs Outputs = AddTemporalAAPass(GraphBuilder, View, Parameters, InputHistory, OutputHistory); SceneColor.Texture = Outputs.SceneColor; SceneColor.ViewRect = Parameters.OutputViewRect; break; } case DVSM_LODColoration: break; default: ensure(false); break; } } if (PassSequence.IsEnabled(EPass::TonemapAfter)) { FTonemapInputs PassInputs; PassSequence.AcceptOverrideIfLastPass(EPass::TonemapAfter, PassInputs.OverrideOutput); PassInputs.SceneColor = FScreenPassTextureSlice::CreateFromScreenPassTexture(GraphBuilder, SceneColor); PassInputs.bOutputInHDR = bViewFamilyOutputInHDR; PassInputs.bGammaOnly = true; // Do eye adaptation in ray tracing debug modes to match raster buffer visualization modes PassInputs.EyeAdaptationParameters = &EyeAdaptationParameters; PassInputs.EyeAdaptationBuffer = GetEyeAdaptationBuffer(GraphBuilder, View); SceneColor = AddTonemapPass(GraphBuilder, View, PassInputs); } #if WITH_EDITOR if (PassSequence.IsEnabled(EPass::SelectionOutline)) { FSelectionOutlineInputs PassInputs; PassSequence.AcceptOverrideIfLastPass(EPass::SelectionOutline, PassInputs.OverrideOutput); PassInputs.SceneColor = SceneColor; PassInputs.SceneDepth = SceneDepth; PassInputs.SceneTextures.SceneTextures = Inputs.SceneTextures; FRDGTextureRef DummyStencilTexture = nullptr; SceneColor = AddSelectionOutlinePass(GraphBuilder, View, SceneUniformBuffer, PassInputs, NaniteRasterResults, DummyStencilTexture); } #endif if (PassSequence.IsEnabled(EPass::PrimaryUpscale)) { ISpatialUpscaler::FInputs PassInputs; PassSequence.AcceptOverrideIfLastPass(EPass::PrimaryUpscale, PassInputs.OverrideOutput); PassInputs.SceneColor = SceneColor; PassInputs.Stage = PassSequence.IsEnabled(EPass::SecondaryUpscale) ? EUpscaleStage::PrimaryToSecondary : EUpscaleStage::PrimaryToOutput; if (const ISpatialUpscaler* CustomUpscaler = View.Family->GetPrimarySpatialUpscalerInterface()) { RDG_EVENT_SCOPE( GraphBuilder, "ThirdParty PrimaryUpscale %s %dx%d -> %dx%d", CustomUpscaler->GetDebugName(), SceneColor.ViewRect.Width(), SceneColor.ViewRect.Height(), View.GetSecondaryViewRectSize().X, View.GetSecondaryViewRectSize().Y); SceneColor = CustomUpscaler->AddPasses(GraphBuilder, View, PassInputs); if (PassSequence.IsLastPass(EPass::PrimaryUpscale)) { check(SceneColor == ViewFamilyOutput); } else { check(SceneColor.ViewRect.Size() == View.GetSecondaryViewRectSize()); } } else { EUpscaleMethod Method = GetUpscaleMethod(); SceneColor = ISpatialUpscaler::AddDefaultUpscalePass(GraphBuilder, View, PassInputs, Method); } } if (PassSequence.IsEnabled(EPass::SecondaryUpscale)) { ISpatialUpscaler::FInputs PassInputs; PassSequence.AcceptOverrideIfLastPass(EPass::SecondaryUpscale, PassInputs.OverrideOutput); PassInputs.SceneColor = SceneColor; PassInputs.Stage = EUpscaleStage::SecondaryToOutput; if (const ISpatialUpscaler* CustomUpscaler = View.Family->GetSecondarySpatialUpscalerInterface()) { RDG_EVENT_SCOPE( GraphBuilder, "ThirdParty SecondaryUpscale %s %dx%d -> %dx%d", CustomUpscaler->GetDebugName(), SceneColor.ViewRect.Width(), SceneColor.ViewRect.Height(), View.UnscaledViewRect.Width(), View.UnscaledViewRect.Height()); SceneColor = CustomUpscaler->AddPasses(GraphBuilder, View, PassInputs); check(SceneColor == ViewFamilyOutput); } else { EUpscaleMethod Method = View.Family->SecondaryScreenPercentageMethod == ESecondaryScreenPercentageMethod::LowerPixelDensitySimulation ? EUpscaleMethod::SmoothStep : EUpscaleMethod::Nearest; SceneColor = ISpatialUpscaler::AddDefaultUpscalePass(GraphBuilder, View, PassInputs, Method); } } } void AddVisualizeCalibrationMaterialPostProcessingPasses(FRDGBuilder& GraphBuilder, const FViewInfo& View, const FPostProcessingInputs& Inputs, const UMaterialInterface* InMaterialInterface) { RDG_CSV_STAT_EXCLUSIVE_SCOPE(GraphBuilder, RenderPostProcessing); QUICK_SCOPE_CYCLE_COUNTER(STAT_PostProcessing_Process); check(IsInRenderingThread()); #if DO_CHECK || USING_CODE_ANALYSIS check(View.VerifyMembersChecks()); #endif check(InMaterialInterface); Inputs.Validate(); const FIntRect PrimaryViewRect = View.ViewRect; const FSceneTextureParameters& SceneTextures = GetSceneTextureParameters(GraphBuilder, Inputs.SceneTextures); const FScreenPassRenderTarget ViewFamilyOutput = FScreenPassRenderTarget::CreateViewFamilyOutput(Inputs.ViewFamilyTexture, View); // Scene color is updated incrementally through the post process pipeline. FScreenPassTexture SceneColor((*Inputs.SceneTextures)->SceneColorTexture, PrimaryViewRect); const FEngineShowFlags& EngineShowFlags = View.Family->EngineShowFlags; const bool bVisualizeHDR = EngineShowFlags.VisualizeHDR; const bool bViewFamilyOutputInHDR = View.Family->RenderTarget->GetSceneHDREnabled(); const bool bOutputInHDR = IsPostProcessingOutputInHDR(); // Post Process Material - Before Color Correction FPostProcessMaterialInputs PostProcessMaterialInputs; PostProcessMaterialInputs.SetInput(GraphBuilder, EPostProcessMaterialInput::SceneColor, SceneColor); PostProcessMaterialInputs.SceneTextures = GetSceneTextureShaderParameters(Inputs.SceneTextures); SceneColor = AddPostProcessMaterialPass(GraphBuilder, View, PostProcessMaterialInputs, InMaterialInterface); // Replace tonemapper with device encoding only pass, which converts the scene color to device-specific color. FDeviceEncodingOnlyInputs PassInputs; PassInputs.OverrideOutput = ViewFamilyOutput; PassInputs.SceneColor = SceneColor; PassInputs.bOutputInHDR = bViewFamilyOutputInHDR; SceneColor = AddDeviceEncodingOnlyPass(GraphBuilder, View, PassInputs); } /////////////////////////////////////////////////////////////////////////// // Mobile Post Processing ////////////////////////////////////////////////////////////////////////// static bool IsGaussianActive(const FViewInfo& View) { float FarSize = View.FinalPostProcessSettings.DepthOfFieldFarBlurSize; float NearSize = View.FinalPostProcessSettings.DepthOfFieldNearBlurSize; float MaxSize = CVarDepthOfFieldMaxSize.GetValueOnRenderThread(); FarSize = FMath::Min(FarSize, MaxSize); NearSize = FMath::Min(NearSize, MaxSize); const float CVarThreshold = CVarDepthOfFieldNearBlurSizeThreshold.GetValueOnRenderThread(); if ((FarSize < 0.01f) && (NearSize < CVarThreshold)) { return false; } return true; } void AddMobilePostProcessingPasses(FRDGBuilder& GraphBuilder, FScene* Scene, const FViewInfo& View, int32 ViewIndex, FSceneUniformBuffer &SceneUniformBuffer, const FMobilePostProcessingInputs& Inputs, FInstanceCullingManager& InstanceCullingManager) { RDG_CSV_STAT_EXCLUSIVE_SCOPE(GraphBuilder, RenderPostProcessing); QUICK_SCOPE_CYCLE_COUNTER(STAT_PostProcessing_Process); using namespace UE::Renderer::PostProcess; check(IsInRenderingThread()); Inputs.Validate(); const FIntRect FinalOutputViewRect = View.ViewRect; const FScreenPassRenderTarget ViewFamilyOutput = FScreenPassRenderTarget::CreateViewFamilyOutput(Inputs.ViewFamilyTexture, View); const FScreenPassRenderTarget ViewFamilyDepthOutput = FScreenPassRenderTarget::CreateViewFamilyOutput(Inputs.ViewFamilyDepthTexture, View); const FScreenPassTexture SceneDepth((*Inputs.SceneTextures)->SceneDepthTexture, FinalOutputViewRect); const FScreenPassTexture CustomDepth((*Inputs.SceneTextures)->CustomDepthTexture, FinalOutputViewRect); const FScreenPassTexture Velocity((*Inputs.SceneTextures)->SceneVelocityTexture, FinalOutputViewRect); const FScreenPassTexture BlackAlphaOneDummy(GSystemTextures.GetBlackAlphaOneDummy(GraphBuilder)); // Scene color is updated incrementally through the post process pipeline. FScreenPassTexture SceneColor((*Inputs.SceneTextures)->SceneColorTexture, FinalOutputViewRect); FScreenPassTexture SceneDepthAux((*Inputs.SceneTextures)->SceneDepthAuxTexture, FinalOutputViewRect); // Default the new eye adaptation to the last one in case it's not generated this frame. const FEyeAdaptationParameters EyeAdaptationParameters = GetEyeAdaptationParameters(View); FRDGBufferRef LastEyeAdaptationBuffer = GetEyeAdaptationBuffer(GraphBuilder, View); enum class EPass : uint32 { Distortion, SunMask, BloomSetup, DepthOfField, Bloom, EyeAdaptation, SunMerge, SeparateTranslucency, TAA, Tonemap, FXAA, PostProcessMaterialAfterTonemapping, HighResolutionScreenshotMask, SelectionOutline, EditorPrimitive, #if UE_ENABLE_DEBUG_DRAWING DebugPrimitive, #endif PrimaryUpscale, SecondaryUpscale, Visualize, VisualizeLightGrid, HMDDistortion, MAX }; // Mobile unsupported passes return EPass::MAX const auto TranslatePass = [](ISceneViewExtension::EPostProcessingPass Pass) -> EPass { switch (Pass) { case ISceneViewExtension::EPostProcessingPass::MotionBlur: return EPass::MAX; case ISceneViewExtension::EPostProcessingPass::Tonemap: return EPass::Tonemap; case ISceneViewExtension::EPostProcessingPass::FXAA: return EPass::FXAA; case ISceneViewExtension::EPostProcessingPass::VisualizeDepthOfField: return EPass::MAX; default: check(false); return EPass::MAX; }; }; static const TCHAR* PassNames[] = { TEXT("Distortion"), TEXT("SunMask"), TEXT("BloomSetup"), TEXT("DepthOfField"), TEXT("Bloom"), TEXT("EyeAdaptation"), TEXT("SunMerge"), TEXT("SeparateTranslucency"), TEXT("TAA"), TEXT("Tonemap"), TEXT("PostProcessMaterial (AfterTonemapping)"), TEXT("FXAA"), TEXT("HighResolutionScreenshotMask"), TEXT("SelectionOutline"), TEXT("EditorPrimitive"), #if UE_ENABLE_DEBUG_DRAWING TEXT("DebugPrimitive"), #endif TEXT("PrimaryUpscale"), TEXT("SecondaryUpscale"), TEXT("Visualize"), TEXT("VisualizeLightGrid"), TEXT("HMDDistortion") }; static_assert(static_cast(EPass::MAX) == UE_ARRAY_COUNT(PassNames), "EPass does not match PassNames."); TOverridePassSequence PassSequence(ViewFamilyOutput); PassSequence.SetNames(PassNames, UE_ARRAY_COUNT(PassNames)); // This page: https://udn.epicgames.com/Three/RenderingOverview#Rendering%20state%20defaults // describes what state a pass can expect and to what state it need to be set back. // All post processing is happening on the render thread side. All passes can access FinalPostProcessSettings and all // view settings. Those are copies for the RT then never get access by the main thread again. // Pointers to other structures might be unsafe to touch. const EDebugViewShaderMode DebugViewShaderMode = View.Family->GetDebugViewShaderMode(); FScreenPassTexture BloomOutput; FScreenPassTexture DofOutput; FScreenPassTexture PostProcessSunShaftAndDof; const EAutoExposureMethod AutoExposureMethod = GetAutoExposureMethod(View); const bool bUseEyeAdaptation = IsMobileEyeAdaptationEnabled(View); const bool bIsPostProcessingEnabled = IsPostProcessingEnabled(View); //The input scene color has been encoded to non-linear space and needs to decode somewhere if MSAA enabled on Metal platform bool bMetalMSAAHDRDecode = GSupportsShaderFramebufferFetch && IsMetalMobilePlatform(View.GetShaderPlatform()) && GetDefaultMSAACount(ERHIFeatureLevel::ES3_1) > 1; // add the passes we want to add to the graph (commenting a line means the pass is not inserted into the graph) --------- // HQ gaussian bool bUseDof = GetMobileDepthOfFieldScale(View) > 0.0f && View.Family->EngineShowFlags.DepthOfField && !View.Family->EngineShowFlags.VisualizeDOF; bool bUseMobileDof = bUseDof && !View.FinalPostProcessSettings.bMobileHQGaussian; // Do not use the tonemapper if output texture is sRGB cause the conversion will be performed by HW. const bool bIsOutputTexsRGB = EnumHasAnyFlags(Inputs.ViewFamilyTexture->Desc.Flags, TexCreate_SRGB); bool bUseToneMapper = !View.Family->EngineShowFlags.ShaderComplexity && (IsMobileHDR() || (IsMobileColorsRGB() && !bIsOutputTexsRGB)); bool bUseHighResolutionScreenshotMask = IsHighResolutionScreenshotMaskEnabled(View); bool bShouldPrimaryUpscale = (View.PrimaryScreenPercentageMethod == EPrimaryScreenPercentageMethod::SpatialUpscale && View.UnscaledViewRect != View.ViewRect) || View.LensDistortionLUT.IsEnabled(); bShouldPrimaryUpscale |= View.Family->GetPrimarySpatialUpscalerInterface() != nullptr; PassSequence.SetEnabled(EPass::Tonemap, bUseToneMapper); PassSequence.SetEnabled(EPass::HighResolutionScreenshotMask, bUseHighResolutionScreenshotMask); #if WITH_EDITOR PassSequence.SetEnabled(EPass::SelectionOutline, GIsEditor && View.Family->EngineShowFlags.Selection && View.Family->EngineShowFlags.SelectionOutline && !View.Family->EngineShowFlags.Wireframe); PassSequence.SetEnabled(EPass::EditorPrimitive, FSceneRenderer::ShouldCompositeEditorPrimitives(View)); #else PassSequence.SetEnabled(EPass::SelectionOutline, false); PassSequence.SetEnabled(EPass::EditorPrimitive, false); #endif #if UE_ENABLE_DEBUG_DRAWING PassSequence.SetEnabled(EPass::DebugPrimitive, FSceneRenderer::ShouldCompositeDebugPrimitivesInPostProcess(View)); #endif PassSequence.SetEnabled(EPass::PrimaryUpscale, bShouldPrimaryUpscale); PassSequence.SetEnabled(EPass::SecondaryUpscale, View.Family->GetSecondarySpatialUpscalerInterface() != nullptr); PassSequence.SetEnabled(EPass::Visualize, View.Family->EngineShowFlags.ShaderComplexity); PassSequence.SetEnabled(EPass::VisualizeLightGrid, ShouldVisualizeLightGrid()); PassSequence.SetEnabled(EPass::HMDDistortion, View.Family->EngineShowFlags.StereoRendering && View.Family->EngineShowFlags.HMDDistortion); // TODO: Add support for EPass::AlphaInvert if the AlphaInvert show flag is enabled, but EPass::PrimaryUpscale is disabled const auto GetPostProcessMaterialInputs = [&](FScreenPassTexture InSceneColor) { FPostProcessMaterialInputs PostProcessMaterialInputs; PostProcessMaterialInputs.SetInput(GraphBuilder, EPostProcessMaterialInput::SceneColor, InSceneColor); PostProcessMaterialInputs.SetInput(GraphBuilder, EPostProcessMaterialInput::Velocity, Velocity); PostProcessMaterialInputs.SceneTextures = GetSceneTextureShaderParameters(Inputs.SceneTextures); PostProcessMaterialInputs.CustomDepthTexture = CustomDepth.Texture; return PostProcessMaterialInputs; }; const auto AddAfterPass = [&](EPass InPass, FScreenPassTexture InSceneColor) -> FScreenPassTexture { // In some cases (e.g. OCIO color conversion) we want View Extensions to be able to add extra custom post processing after the pass. FPostProcessingPassDelegateArray& PassCallbacks = PassSequence.GetAfterPassCallbacks(InPass); if (PassCallbacks.Num()) { FPostProcessMaterialInputs InOutPostProcessAfterPassInputs = GetPostProcessMaterialInputs(InSceneColor); for (int32 AfterPassCallbackIndex = 0; AfterPassCallbackIndex < PassCallbacks.Num(); AfterPassCallbackIndex++) { InOutPostProcessAfterPassInputs.SetInput(GraphBuilder, EPostProcessMaterialInput::SceneColor, InSceneColor); FAfterPassCallbackDelegate& AfterPassCallback = PassCallbacks[AfterPassCallbackIndex]; PassSequence.AcceptOverrideIfLastPass(InPass, InOutPostProcessAfterPassInputs.OverrideOutput, AfterPassCallbackIndex); InSceneColor = AfterPassCallback.Execute(GraphBuilder, View, InOutPostProcessAfterPassInputs); } } return MoveTemp(InSceneColor); }; // Always evaluate custom post processes // The scene color will be decoded at the first post-process material and output linear color space for the following passes // bMetalMSAAHDRDecode will be set to false if there is any post-process material exist auto AddPostProcessMaterialPass = [&GraphBuilder, &View, ViewIndex, &Inputs, &SceneColor, &CustomDepth, &bMetalMSAAHDRDecode, &PassSequence, &BloomOutput, &BlackAlphaOneDummy](EBlendableLocation BlendableLocation) { FPostProcessMaterialInputs PostProcessMaterialInputs; if (BlendableLocation == BL_SceneColorAfterTonemapping && PassSequence.IsEnabled(EPass::PostProcessMaterialAfterTonemapping)) { PassSequence.AcceptOverrideIfLastPass(EPass::PostProcessMaterialAfterTonemapping, PostProcessMaterialInputs.OverrideOutput); } if(BlendableLocation == BL_ReplacingTonemapper && PassSequence.IsEnabled(EPass::Tonemap)) { PassSequence.AcceptOverrideIfLastPass(EPass::Tonemap, PostProcessMaterialInputs.OverrideOutput); } PostProcessMaterialInputs.SetInput(GraphBuilder, EPostProcessMaterialInput::SceneColor, SceneColor); if (BlendableLocation == BL_ReplacingTonemapper && PassSequence.IsEnabled(EPass::Tonemap)) { if (!BloomOutput.IsValid()) { BloomOutput = BlackAlphaOneDummy; } PostProcessMaterialInputs.SetInput(GraphBuilder, EPostProcessMaterialInput::CombinedBloom, BloomOutput); } PostProcessMaterialInputs.CustomDepthTexture = CustomDepth.Texture; PostProcessMaterialInputs.bMetalMSAAHDRDecode = bMetalMSAAHDRDecode; PostProcessMaterialInputs.SceneTextures = GetSceneTextureShaderParameters(Inputs.SceneTextures); const FPostProcessMaterialChain MaterialChain = GetPostProcessMaterialChain(View, BlendableLocation); if (MaterialChain.Num()) { SceneColor = AddPostProcessMaterialChain(GraphBuilder, View, ViewIndex, PostProcessMaterialInputs, MaterialChain); // For solid material, we decode the input color and output the linear color // For blend material, we force it rendering to an intermediate render target and decode there bMetalMSAAHDRDecode = false; } }; constexpr int32 FirstAfterPass = static_cast(ISceneViewExtension::EPostProcessingPass::MotionBlur); // Scene view extension delegates that precede the override pass sequence are to be called directly. TStaticArray(FirstAfterPass)> SceneViewExtensionDelegates; if (bIsPostProcessingEnabled) { bool bUseSun = View.MobileLightShaft.IsSet(); bool bUseBloom = View.FinalPostProcessSettings.BloomIntensity > 0.0f; bool bUseBasicEyeAdaptation = bUseEyeAdaptation && (AutoExposureMethod == EAutoExposureMethod::AEM_Basic); bool bUseHistogramEyeAdaptation = bUseEyeAdaptation && (AutoExposureMethod == EAutoExposureMethod::AEM_Histogram) && // Skip if we don't have any exposure range to generate (eye adaptation will clamp). View.FinalPostProcessSettings.AutoExposureMinBrightness < View.FinalPostProcessSettings.AutoExposureMaxBrightness; bool bUseTAA = View.AntiAliasingMethod == AAM_TemporalAA; ensure(View.AntiAliasingMethod != AAM_TSR); bool bUseDistortion = IsMobileDistortionActive(View); bool bUseSeparateTranslucency = IsMobileSeparateTranslucencyActive(View); const FPostProcessMaterialChain PostProcessMaterialAfterTonemappingChain = GetPostProcessMaterialChain(View, BL_SceneColorAfterTonemapping); PassSequence.SetEnabled(EPass::Distortion, bUseDistortion); PassSequence.SetEnabled(EPass::SunMask, bUseSun || bUseDof); PassSequence.SetEnabled(EPass::BloomSetup, bUseSun || bUseMobileDof || bUseBloom || bUseBasicEyeAdaptation || bUseHistogramEyeAdaptation); PassSequence.SetEnabled(EPass::DepthOfField, bUseDof); PassSequence.SetEnabled(EPass::Bloom, bUseBloom); PassSequence.SetEnabled(EPass::EyeAdaptation, bUseEyeAdaptation); PassSequence.SetEnabled(EPass::SunMerge, bUseBloom || bUseSun); PassSequence.SetEnabled(EPass::SeparateTranslucency, bUseSeparateTranslucency); PassSequence.SetEnabled(EPass::TAA, bUseTAA); PassSequence.SetEnabled(EPass::FXAA, View.AntiAliasingMethod == AAM_FXAA); PassSequence.SetEnabled(EPass::PostProcessMaterialAfterTonemapping, PostProcessMaterialAfterTonemappingChain.Num() != 0); for (const TSharedRef& ViewExtension : View.Family->ViewExtensions) { for (int32 SceneViewPassId = 0; SceneViewPassId < FirstAfterPass; SceneViewPassId++) { const ISceneViewExtension::EPostProcessingPass SceneViewPass = static_cast(SceneViewPassId); const bool bIsEnabled = (SceneViewPass == ISceneViewExtension::EPostProcessingPass::ReplacingTonemapper) ? PassSequence.IsEnabled(EPass::Tonemap) : true; ViewExtension->SubscribeToPostProcessingPass(SceneViewPass, View, SceneViewExtensionDelegates[SceneViewPassId], bIsEnabled); } for (int32 SceneViewPassId = FirstAfterPass; SceneViewPassId < static_cast(ISceneViewExtension::EPostProcessingPass::MAX); SceneViewPassId++) { const ISceneViewExtension::EPostProcessingPass SceneViewPass = static_cast(SceneViewPassId); const EPass PostProcessingPass = TranslatePass(SceneViewPass); if (PostProcessingPass != EPass::MAX) { ViewExtension->SubscribeToPostProcessingPass( SceneViewPass, View, PassSequence.GetAfterPassCallbacks(PostProcessingPass), PassSequence.IsEnabled(PostProcessingPass)); } } } PassSequence.Finalize(); if (PassSequence.IsEnabled(EPass::Distortion)) { PassSequence.AcceptPass(EPass::Distortion); FMobileDistortionAccumulateInputs DistortionAccumulateInputs; DistortionAccumulateInputs.SceneColor = SceneColor; FMobileDistortionAccumulateOutputs DistortionAccumulateOutputs = AddMobileDistortionAccumulatePass(GraphBuilder, Scene, View, DistortionAccumulateInputs); FMobileDistortionMergeInputs DistortionMergeInputs; DistortionMergeInputs.SceneColor = SceneColor; DistortionMergeInputs.DistortionAccumulate = DistortionAccumulateOutputs.DistortionAccumulate; SceneColor = AddMobileDistortionMergePass(GraphBuilder, View, DistortionMergeInputs); } if (SceneViewExtensionDelegates[static_cast(ISceneViewExtension::EPostProcessingPass::BeforeDOF)].Num()) { SceneColor = AddSceneViewExtensionPassChain(GraphBuilder, View, GetPostProcessMaterialInputs(SceneColor), SceneViewExtensionDelegates[static_cast(ISceneViewExtension::EPostProcessingPass::BeforeDOF)]); } AddPostProcessMaterialPass(BL_SceneColorBeforeDOF); // Optional fixed pass processes if (PassSequence.IsEnabled(EPass::SunMask)) { PassSequence.AcceptPass(EPass::SunMask); bool bUseDepthTexture = !MobileRequiresSceneDepthAux(View.GetShaderPlatform()) || IsMobileDeferredShadingEnabled(View.GetShaderPlatform()); FMobileSunMaskInputs SunMaskInputs; SunMaskInputs.bUseDepthTexture = bUseDepthTexture; SunMaskInputs.bUseDof = bUseDof; SunMaskInputs.bUseMetalMSAAHDRDecode = bMetalMSAAHDRDecode; SunMaskInputs.bUseSun = bUseSun; SunMaskInputs.SceneColor = SceneColor; SunMaskInputs.SceneTextures = Inputs.SceneTextures; // Convert depth to {circle of confusion, sun shaft intensity} FMobileSunMaskOutputs SunMaskOutputs = AddMobileSunMaskPass(GraphBuilder, View, SunMaskInputs); PostProcessSunShaftAndDof = SunMaskOutputs.SunMask; // The scene color will be decoded after sun mask pass and output to linear color space for following passes if sun shaft enabled // set bMetalMSAAHDRDecode to false if sun shaft enabled if (bMetalMSAAHDRDecode && bUseSun) { SceneColor = SunMaskOutputs.SceneColor; bMetalMSAAHDRDecode = false; } //@todo Ronin sunmask pass isnt clipping to image only. } FMobileBloomSetupOutputs BloomSetupOutputs; if (PassSequence.IsEnabled(EPass::BloomSetup)) { PassSequence.AcceptPass(EPass::BloomSetup); bool bHasEyeAdaptationPass = (bUseBasicEyeAdaptation || bUseHistogramEyeAdaptation); FMobileBloomSetupInputs BloomSetupInputs; BloomSetupInputs.bUseBloom = bUseBloom; BloomSetupInputs.bUseDof = bUseMobileDof; BloomSetupInputs.bUseEyeAdaptation = bHasEyeAdaptationPass; BloomSetupInputs.bUseMetalMSAAHDRDecode = bMetalMSAAHDRDecode; BloomSetupInputs.bUseSun = bUseSun; BloomSetupInputs.SceneColor = SceneColor; BloomSetupInputs.SunShaftAndDof = PostProcessSunShaftAndDof; BloomSetupOutputs = AddMobileBloomSetupPass(GraphBuilder, View, EyeAdaptationParameters, BloomSetupInputs); } if (PassSequence.IsEnabled(EPass::DepthOfField)) { PassSequence.AcceptPass(EPass::DepthOfField); if (bUseMobileDof) { // Near dilation circle of confusion size. // Samples at 1/16 area, writes to 1/16 area. FMobileDofNearInputs DofNearInputs; DofNearInputs.BloomSetup_SunShaftAndDof = BloomSetupOutputs.SunShaftAndDof; DofNearInputs.bUseSun = bUseSun; FMobileDofNearOutputs DofNearOutputs = AddMobileDofNearPass(GraphBuilder, View, DofNearInputs); // DOF downsample pass. // Samples at full resolution, writes to 1/4 area. FMobileDofDownInputs DofDownInputs; DofDownInputs.bUseSun = bUseSun; DofDownInputs.DofNear = DofNearOutputs.DofNear; DofDownInputs.SceneColor = SceneColor; DofDownInputs.SunShaftAndDof = PostProcessSunShaftAndDof; FMobileDofDownOutputs DofDownOutputs = AddMobileDofDownPass(GraphBuilder, View, DofDownInputs); // DOF blur pass. // Samples at 1/4 area, writes to 1/4 area. FMobileDofBlurInputs DofBlurInputs; DofBlurInputs.DofDown = DofDownOutputs.DofDown; DofBlurInputs.DofNear = DofNearOutputs.DofNear; FMobileDofBlurOutputs DofBlurOutputs = AddMobileDofBlurPass(GraphBuilder, View, DofBlurInputs); DofOutput = DofBlurOutputs.DofBlur; FMobileIntegrateDofInputs IntegrateDofInputs; IntegrateDofInputs.DofBlur = DofBlurOutputs.DofBlur; IntegrateDofInputs.SceneColor = SceneColor; IntegrateDofInputs.SunShaftAndDof = PostProcessSunShaftAndDof; SceneColor = AddMobileIntegrateDofPass(GraphBuilder, View, IntegrateDofInputs); } else { bool bDepthOfField = IsGaussianActive(View); if (bDepthOfField) { float FarSize = View.FinalPostProcessSettings.DepthOfFieldFarBlurSize; float NearSize = View.FinalPostProcessSettings.DepthOfFieldNearBlurSize; const float MaxSize = CVarDepthOfFieldMaxSize.GetValueOnRenderThread(); FarSize = FMath::Min(FarSize, MaxSize); NearSize = FMath::Min(NearSize, MaxSize); const bool bFar = FarSize >= 0.01f; const bool bNear = NearSize >= CVarDepthOfFieldNearBlurSizeThreshold.GetValueOnRenderThread(); const bool bCombinedNearFarPass = bFar && bNear; if (bFar || bNear) { // AddGaussianDofBlurPass produces a blurred image from setup or potentially from taa result. auto AddGaussianDofBlurPass = [&GraphBuilder, &View](FScreenPassTexture& DOFSetup, bool bFarPass, float KernelSizePercent) { const TCHAR* BlurDebugX = bFarPass ? TEXT("FarDOFBlurX") : TEXT("NearDOFBlurX"); const TCHAR* BlurDebugY = bFarPass ? TEXT("FarDOFBlurY") : TEXT("NearDOFBlurY"); FGaussianBlurInputs GaussianBlurInputs; GaussianBlurInputs.NameX = BlurDebugX; GaussianBlurInputs.NameY = BlurDebugY; GaussianBlurInputs.Filter = FScreenPassTextureSlice::CreateFromScreenPassTexture(GraphBuilder, DOFSetup); GaussianBlurInputs.TintColor = FLinearColor::White; GaussianBlurInputs.CrossCenterWeight = FVector2f::ZeroVector; GaussianBlurInputs.KernelSizePercent = KernelSizePercent; return AddGaussianBlurPass(GraphBuilder, View, GaussianBlurInputs); }; FMobileDofSetupInputs DofSetupInputs; DofSetupInputs.bFarBlur = bFar; DofSetupInputs.bNearBlur = bNear; DofSetupInputs.SceneColor = SceneColor; DofSetupInputs.SunShaftAndDof = PostProcessSunShaftAndDof; FMobileDofSetupOutputs DofSetupOutputs = AddMobileDofSetupPass(GraphBuilder, View, DofSetupInputs); FScreenPassTexture DofFarBlur, DofNearBlur; if (bFar) { DofFarBlur = AddGaussianDofBlurPass(DofSetupOutputs.DofSetupFar, true, FarSize); } if (bNear) { DofNearBlur = AddGaussianDofBlurPass(DofSetupOutputs.DofSetupNear, false, NearSize); } FMobileDofRecombineInputs DofRecombineInputs; DofRecombineInputs.bFarBlur = bFar; DofRecombineInputs.bNearBlur = bNear; DofRecombineInputs.DofFarBlur = DofFarBlur; DofRecombineInputs.DofNearBlur = DofNearBlur; DofRecombineInputs.SceneColor = SceneColor; DofRecombineInputs.SunShaftAndDof = PostProcessSunShaftAndDof; SceneColor = AddMobileDofRecombinePass(GraphBuilder, View, DofRecombineInputs); } } } } // Bloom. FScreenPassTexture BloomUpOutputs; if (PassSequence.IsEnabled(EPass::Bloom)) { PassSequence.AcceptPass(EPass::Bloom); auto AddBloomDownPass = [&GraphBuilder, &View](const FScreenPassTexture& BloomDownSource, float BloomDownScale) { FMobileBloomDownInputs BloomDownInputs; BloomDownInputs.BloomDownScale = BloomDownScale; BloomDownInputs.BloomDownSource = BloomDownSource; return AddMobileBloomDownPass(GraphBuilder, View, BloomDownInputs); }; const float BloomDownScale = 0.66f * 4.0f; const int MaxPasses = 6; const EBloomQuality BloomQuality = GetBloomQuality(); const int NumDownsamplePasses = BloomQuality == EBloomQuality::Q1 ? 4 : BloomQuality == EBloomQuality::Q2 ? 5 : 6; FScreenPassTexture PostProcessDownsample_Bloom[MaxPasses]; for (int32 i = 0; i < NumDownsamplePasses; ++i) { PostProcessDownsample_Bloom[i] = AddBloomDownPass(i == 0 ? BloomSetupOutputs.Bloom : PostProcessDownsample_Bloom[i - 1], BloomDownScale); } const FFinalPostProcessSettings& Settings = View.FinalPostProcessSettings; auto AddBloomUpPass = [&GraphBuilder, &View](FScreenPassTexture& BloomUpSourceA, FScreenPassTexture& BloomUpSourceB, float BloomSourceScale, const FVector4f& TintA, const FVector4f& TintB) { FMobileBloomUpInputs BloomUpInputs; BloomUpInputs.BloomUpSourceA = BloomUpSourceA; BloomUpInputs.BloomUpSourceB = BloomUpSourceB; BloomUpInputs.ScaleAB = FVector2D(BloomSourceScale, BloomSourceScale); BloomUpInputs.TintA = TintA; BloomUpInputs.TintB = TintB; return AddMobileBloomUpPass(GraphBuilder, View, BloomUpInputs); }; const float BloomUpScale = 0.66f * 2.0f; const FLinearColor Tint[]{ Settings.Bloom1Tint, Settings.Bloom2Tint, Settings.Bloom3Tint, Settings.Bloom4Tint, Settings.Bloom5Tint, Settings.Bloom6Tint }; // Upsample by 2 { const int IndexA = NumDownsamplePasses - 2; const int IndexB = NumDownsamplePasses - 1; FVector4f TintA{ Tint[IndexA], 0.0f }; FVector4f TintB{ Tint[IndexB], 0.0f }; TintA *= Settings.BloomIntensity; TintB *= Settings.BloomIntensity; BloomUpOutputs = AddBloomUpPass(PostProcessDownsample_Bloom[IndexA], PostProcessDownsample_Bloom[IndexB], BloomUpScale, TintA, TintB); } for (int Index = NumDownsamplePasses - 3; Index > 0; --Index) { // Upsample by 2 FVector4f TintA{ Tint[Index], 0.0f }; TintA *= Settings.BloomIntensity; FVector4f TintB = FVector4f(1.0f, 1.0f, 1.0f, 0.0f); BloomUpOutputs = AddBloomUpPass(PostProcessDownsample_Bloom[Index], BloomUpOutputs, BloomUpScale, TintA, TintB); } // Upsample by 2 { FVector4f TintA = FVector4f(Settings.Bloom2Tint.R, Settings.Bloom2Tint.G, Settings.Bloom2Tint.B, 0.0f); TintA *= Settings.BloomIntensity; // Scaling Bloom2 by extra factor to match filter area difference between PC default and mobile. TintA *= 0.5; FVector4f TintB = FVector4f(1.0f, 1.0f, 1.0f, 0.0f); BloomUpOutputs = AddBloomUpPass(PostProcessDownsample_Bloom[0], BloomUpOutputs, BloomUpScale, TintA, TintB); } if (IsLensFlaresEnabled(View)) { const ELensFlareQuality LensFlareQuality = GetLensFlareQuality(); const uint32 LensFlareDownsampleStageIndex = static_cast(ELensFlareQuality::MAX) - static_cast(LensFlareQuality) - 1; BloomUpOutputs = AddLensFlaresPass(GraphBuilder, View, BloomUpOutputs, FScreenPassTextureSlice::CreateFromScreenPassTexture(GraphBuilder, PostProcessDownsample_Bloom[LensFlareDownsampleStageIndex]), FScreenPassTextureSlice::CreateFromScreenPassTexture(GraphBuilder, PostProcessDownsample_Bloom[0])); } } if (PassSequence.IsEnabled(EPass::EyeAdaptation)) { PassSequence.AcceptPass(EPass::EyeAdaptation); FMobileEyeAdaptationSetupInputs EyeAdaptationSetupInputs; EyeAdaptationSetupInputs.bUseBasicEyeAdaptation = bUseBasicEyeAdaptation; EyeAdaptationSetupInputs.bUseHistogramEyeAdaptation = bUseHistogramEyeAdaptation; EyeAdaptationSetupInputs.BloomSetup_EyeAdaptation = FScreenPassTexture(TryRegisterExternalTexture(GraphBuilder, View.PrevViewInfo.MobileBloomSetup_EyeAdaptation)); if (!EyeAdaptationSetupInputs.BloomSetup_EyeAdaptation.IsValid()) { EyeAdaptationSetupInputs.BloomSetup_EyeAdaptation = BloomSetupOutputs.EyeAdaptation; } FMobileEyeAdaptationSetupOutputs EyeAdaptationSetupOutputs = AddMobileEyeAdaptationSetupPass(GraphBuilder, View, EyeAdaptationParameters, EyeAdaptationSetupInputs); FMobileEyeAdaptationInputs EyeAdaptationInputs; EyeAdaptationInputs.bUseBasicEyeAdaptation = bUseBasicEyeAdaptation; EyeAdaptationInputs.bUseHistogramEyeAdaptation = bUseHistogramEyeAdaptation; EyeAdaptationInputs.EyeAdaptationSetupSRV = EyeAdaptationSetupOutputs.EyeAdaptationSetupSRV; EyeAdaptationInputs.EyeAdaptationBuffer = LastEyeAdaptationBuffer; AddMobileEyeAdaptationPass(GraphBuilder, View, EyeAdaptationParameters, EyeAdaptationInputs); if ((bUseBasicEyeAdaptation || bUseHistogramEyeAdaptation) && View.ViewState && !View.bStatePrevViewInfoIsReadOnly) { GraphBuilder.QueueTextureExtraction(BloomSetupOutputs.EyeAdaptation.Texture, &View.ViewState->PrevFrameViewInfo.MobileBloomSetup_EyeAdaptation); } } if (PassSequence.IsEnabled(EPass::SunMerge)) { PassSequence.AcceptPass(EPass::SunMerge); FScreenPassTexture SunBlurOutputs; if (bUseSun) { FMobileSunAlphaInputs SunAlphaInputs; SunAlphaInputs.BloomSetup_SunShaftAndDof = BloomSetupOutputs.SunShaftAndDof; SunAlphaInputs.bUseMobileDof = bUseMobileDof; FScreenPassTexture SunAlphaOutputs = AddMobileSunAlphaPass(GraphBuilder, View, SunAlphaInputs); FMobileSunBlurInputs SunBlurInputs; SunBlurInputs.SunAlpha = SunAlphaOutputs; SunBlurOutputs = AddMobileSunBlurPass(GraphBuilder, View, SunBlurInputs); } FMobileSunMergeInputs SunMergeInputs; SunMergeInputs.BloomSetup_Bloom = BloomSetupOutputs.Bloom; SunMergeInputs.BloomUp = BloomUpOutputs; SunMergeInputs.SunBlur = SunBlurOutputs; SunMergeInputs.bUseBloom = bUseBloom; SunMergeInputs.bUseSun = bUseSun; BloomOutput = AddMobileSunMergePass(GraphBuilder, View, SunMergeInputs); } // mobile separate translucency if (PassSequence.IsEnabled(EPass::SeparateTranslucency)) { PassSequence.AcceptPass(EPass::SeparateTranslucency); FMobileSeparateTranslucencyInputs SeparateTranslucencyInputs; SeparateTranslucencyInputs.SceneColor = SceneColor; SeparateTranslucencyInputs.SceneDepthAux = SceneDepthAux; SeparateTranslucencyInputs.SceneDepth = SceneDepth; AddMobileSeparateTranslucencyPass(GraphBuilder, Scene, View, SeparateTranslucencyInputs); } if (SceneViewExtensionDelegates[static_cast(ISceneViewExtension::EPostProcessingPass::AfterDOF)].Num()) { SceneColor = AddSceneViewExtensionPassChain(GraphBuilder, View, GetPostProcessMaterialInputs(SceneColor), SceneViewExtensionDelegates[static_cast(ISceneViewExtension::EPostProcessingPass::AfterDOF)]); } AddPostProcessMaterialPass(BL_SceneColorAfterDOF); // Temporal Anti-aliasing. Also may perform a temporal upsample from primary to secondary view rect. if (PassSequence.IsEnabled(EPass::TAA)) { PassSequence.AcceptPass(EPass::TAA); EMainTAAPassConfig TAAConfig = GetMainTAAPassConfig(View); checkSlow(TAAConfig != EMainTAAPassConfig::Disabled); FDefaultTemporalUpscaler::FInputs UpscalerPassInputs{}; UpscalerPassInputs.SceneColor = FScreenPassTexture(SceneColor.Texture, View.ViewRect); UpscalerPassInputs.SceneDepth = FScreenPassTexture(SceneDepth.Texture, View.ViewRect); UpscalerPassInputs.SceneVelocity = FScreenPassTexture(Velocity.Texture, View.ViewRect); FDefaultTemporalUpscaler::FOutputs Outputs; if (TAAConfig == EMainTAAPassConfig::TAA) { Outputs = AddGen4MainTemporalAAPasses( GraphBuilder, View, UpscalerPassInputs); } else if (TAAConfig == EMainTAAPassConfig::ThirdParty) { Outputs = AddThirdPartyTemporalUpscalerPasses( GraphBuilder, View, UpscalerPassInputs); } else { unimplemented(); } SceneColor = FScreenPassTexture(Outputs.FullRes); } else if (IsMobileSSREnabled(View)) { // If we need SSR, and TAA is enabled, then AddTemporalAAPass() has already handled the scene history. // If we need SSR, and TAA is not enabled, then we just need to extract the history. if (!View.bStatePrevViewInfoIsReadOnly) { check(View.ViewState); FTemporalAAHistory& OutputHistory = View.ViewState->PrevFrameViewInfo.TemporalAAHistory; GraphBuilder.QueueTextureExtraction(SceneColor.Texture, &OutputHistory.RT[0]); // For SSR, we still fill up the rest of the OutputHistory data using shared math from FTAAPassParameters. FTAAPassParameters TAAInputs(View); TAAInputs.SceneColorInput = SceneColor.Texture; TAAInputs.SetupViewRect(View); OutputHistory.ViewportRect = TAAInputs.OutputViewRect; OutputHistory.ReferenceBufferSize = TAAInputs.GetOutputExtent() * TAAInputs.ResolutionDivisor; } } } else { PassSequence.SetEnabled(EPass::Distortion, false); PassSequence.SetEnabled(EPass::SunMask, false); PassSequence.SetEnabled(EPass::BloomSetup, false); PassSequence.SetEnabled(EPass::DepthOfField, false); PassSequence.SetEnabled(EPass::Bloom, false); PassSequence.SetEnabled(EPass::EyeAdaptation, false); PassSequence.SetEnabled(EPass::SunMerge, false); PassSequence.SetEnabled(EPass::SeparateTranslucency, false); PassSequence.SetEnabled(EPass::TAA, false); PassSequence.SetEnabled(EPass::FXAA, false); PassSequence.SetEnabled(EPass::PostProcessMaterialAfterTonemapping, false); PassSequence.Finalize(); } AddPostProcessMaterialPass(BL_SceneColorBeforeBloom); if (PassSequence.IsEnabled(EPass::Tonemap)) { const FPostProcessMaterialChain MaterialChain = GetPostProcessMaterialChain(View, BL_ReplacingTonemapper); if (MaterialChain.Num()) { AddPostProcessMaterialPass(BL_ReplacingTonemapper); } else { bool bHDRTonemapperOutput = false; if (!BloomOutput.IsValid()) { BloomOutput = BlackAlphaOneDummy; } bool bDoGammaOnly = !IsMobileHDR(); FRDGTextureRef ColorGradingTexture = nullptr; if (IStereoRendering::IsAPrimaryView(View) && !bDoGammaOnly) { ColorGradingTexture = AddCombineLUTPass(GraphBuilder, View); } // We can re-use the color grading texture from the primary view. else if (View.GetTonemappingLUT()) { ColorGradingTexture = TryRegisterExternalTexture(GraphBuilder, View.GetTonemappingLUT()); } else { const FViewInfo* PrimaryView = static_cast(View.Family->Views[0]); ColorGradingTexture = TryRegisterExternalTexture(GraphBuilder, PrimaryView->GetTonemappingLUT()); } FTonemapInputs TonemapperInputs; PassSequence.AcceptOverrideIfLastPass(EPass::Tonemap, TonemapperInputs.OverrideOutput); // This is the view family render target. if (TonemapperInputs.OverrideOutput.Texture) { FIntRect OutputViewRect; if (View.PrimaryScreenPercentageMethod == EPrimaryScreenPercentageMethod::RawOutput) { OutputViewRect = View.ViewRect; } else { OutputViewRect = View.UnscaledViewRect; } ERenderTargetLoadAction OutputLoadAction = View.IsFirstInFamily() ? ERenderTargetLoadAction::EClear : ERenderTargetLoadAction::ELoad; TonemapperInputs.OverrideOutput.ViewRect = OutputViewRect; TonemapperInputs.OverrideOutput.LoadAction = OutputLoadAction; TonemapperInputs.OverrideOutput.UpdateVisualizeTextureExtent(); } TonemapperInputs.SceneColor = FScreenPassTextureSlice::CreateFromScreenPassTexture(GraphBuilder, SceneColor); TonemapperInputs.Bloom = BloomOutput; TonemapperInputs.EyeAdaptationParameters = &EyeAdaptationParameters; TonemapperInputs.ColorGradingTexture = ColorGradingTexture; TonemapperInputs.bWriteAlphaChannel = View.AntiAliasingMethod == AAM_FXAA || IsPostProcessingWithAlphaChannelSupported() || bUseMobileDof || IsMobilePropagateAlphaEnabled(View.GetShaderPlatform()); TonemapperInputs.bOutputInHDR = bHDRTonemapperOutput; TonemapperInputs.bGammaOnly = bDoGammaOnly; TonemapperInputs.bMetalMSAAHDRDecode = bMetalMSAAHDRDecode; TonemapperInputs.EyeAdaptationBuffer = bUseEyeAdaptation ? LastEyeAdaptationBuffer : nullptr; SceneColor = AddTonemapPass(GraphBuilder, View, TonemapperInputs); } //The output color should been decoded to linear space after tone mapper apparently bMetalMSAAHDRDecode = false; } SceneColor = AddAfterPass(EPass::Tonemap, SceneColor); if (IsPostProcessingEnabled(View)) { if (PassSequence.IsEnabled(EPass::FXAA)) { FFXAAInputs PassInputs; PassSequence.AcceptOverrideIfLastPass(EPass::FXAA, PassInputs.OverrideOutput); PassInputs.SceneColor = SceneColor; PassInputs.Quality = GetFXAAQuality(); SceneColor = AddFXAAPass(GraphBuilder, View, PassInputs); } SceneColor = AddAfterPass(EPass::FXAA, SceneColor); if (PassSequence.IsEnabled(EPass::PostProcessMaterialAfterTonemapping)) { AddPostProcessMaterialPass(BL_SceneColorAfterTonemapping); } } if (PassSequence.IsEnabled(EPass::HighResolutionScreenshotMask)) { FHighResolutionScreenshotMaskInputs HighResolutionScreenshotMaskInputs; HighResolutionScreenshotMaskInputs.SceneColor = SceneColor; HighResolutionScreenshotMaskInputs.SceneTextures = GetSceneTextureShaderParameters(Inputs.SceneTextures); HighResolutionScreenshotMaskInputs.Material = View.FinalPostProcessSettings.HighResScreenshotMaterial; HighResolutionScreenshotMaskInputs.MaskMaterial = View.FinalPostProcessSettings.HighResScreenshotMaskMaterial; HighResolutionScreenshotMaskInputs.CaptureRegionMaterial = View.FinalPostProcessSettings.HighResScreenshotCaptureRegionMaterial; PassSequence.AcceptOverrideIfLastPass(EPass::HighResolutionScreenshotMask, HighResolutionScreenshotMaskInputs.OverrideOutput); HighResolutionScreenshotMaskInputs.OverrideOutput.LoadAction = View.IsFirstInFamily() ? ERenderTargetLoadAction::EClear : ERenderTargetLoadAction::ELoad; SceneColor = AddHighResolutionScreenshotMaskPass(GraphBuilder, View, HighResolutionScreenshotMaskInputs); } #if WITH_EDITOR // Show the selection outline if it is in the editor and we aren't in wireframe // If the engine is in demo mode and game view is on we also do not show the selection outline if (PassSequence.IsEnabled(EPass::SelectionOutline)) { FSelectionOutlineInputs PassInputs; PassSequence.AcceptOverrideIfLastPass(EPass::SelectionOutline, PassInputs.OverrideOutput); PassInputs.SceneColor = SceneColor; PassInputs.SceneDepth = SceneDepth; PassInputs.SceneTextures = GetSceneTextureShaderParameters(Inputs.SceneTextures); PassInputs.OverrideOutput.LoadAction = View.IsFirstInFamily() ? ERenderTargetLoadAction::EClear : ERenderTargetLoadAction::ELoad; // TODO: Nanite - pipe through results FRDGTextureRef DummyStencilTexture = nullptr; SceneColor = AddSelectionOutlinePass(GraphBuilder, View, SceneUniformBuffer, PassInputs, nullptr, DummyStencilTexture); } if (PassSequence.IsEnabled(EPass::EditorPrimitive)) { FCompositePrimitiveInputs PassInputs; PassSequence.AcceptOverrideIfLastPass(EPass::EditorPrimitive, PassInputs.OverrideOutput); PassInputs.OverrideDepthOutput = ViewFamilyDepthOutput; PassInputs.SceneColor = SceneColor; PassInputs.SceneDepth = SceneDepth; PassInputs.BasePassType = FCompositePrimitiveInputs::EBasePassType::Mobile; PassInputs.OverrideOutput.LoadAction = View.IsFirstInFamily() ? ERenderTargetLoadAction::EClear : ERenderTargetLoadAction::ELoad; SceneColor = AddEditorPrimitivePass(GraphBuilder, View, PassInputs, InstanceCullingManager); } #endif #if UE_ENABLE_DEBUG_DRAWING if (PassSequence.IsEnabled(EPass::DebugPrimitive)) //Create new debug pass sequence { FCompositePrimitiveInputs PassInputs; PassSequence.AcceptOverrideIfLastPass(EPass::DebugPrimitive, PassInputs.OverrideOutput); PassInputs.SceneColor = SceneColor; PassInputs.SceneDepth = SceneDepth; PassInputs.bUseMetalMSAAHDRDecode = bMetalMSAAHDRDecode; SceneColor = AddDebugPrimitivePass(GraphBuilder, View, PassInputs); } #endif // Apply ScreenPercentage if (PassSequence.IsEnabled(EPass::PrimaryUpscale)) { ISpatialUpscaler::FInputs PassInputs; PassSequence.AcceptOverrideIfLastPass(EPass::PrimaryUpscale, PassInputs.OverrideOutput); PassInputs.Stage = EUpscaleStage::PrimaryToOutput; PassInputs.SceneColor = SceneColor; PassInputs.OverrideOutput.LoadAction = View.IsFirstInFamily() ? ERenderTargetLoadAction::EClear : ERenderTargetLoadAction::ELoad; if (const ISpatialUpscaler* CustomUpscaler = View.Family->GetPrimarySpatialUpscalerInterface()) { RDG_EVENT_SCOPE( GraphBuilder, "ThirdParty PrimaryUpscale %s %dx%d -> %dx%d", CustomUpscaler->GetDebugName(), SceneColor.ViewRect.Width(), SceneColor.ViewRect.Height(), View.UnscaledViewRect.Width(), View.UnscaledViewRect.Height()); SceneColor = CustomUpscaler->AddPasses(GraphBuilder, View, PassInputs); if (PassSequence.IsLastPass(EPass::PrimaryUpscale)) { check(SceneColor == ViewFamilyOutput); } else { check(SceneColor.ViewRect.Size() == View.UnscaledViewRect.Size()); } } else { SceneColor = ISpatialUpscaler::AddDefaultUpscalePass(GraphBuilder, View, PassInputs, EUpscaleMethod::Bilinear, View.LensDistortionLUT); } } if (PassSequence.IsEnabled(EPass::SecondaryUpscale)) { ISpatialUpscaler::FInputs PassInputs; PassSequence.AcceptOverrideIfLastPass(EPass::SecondaryUpscale, PassInputs.OverrideOutput); PassInputs.SceneColor = SceneColor; PassInputs.Stage = EUpscaleStage::SecondaryToOutput; const ISpatialUpscaler* CustomUpscaler = View.Family->GetSecondarySpatialUpscalerInterface(); if (CustomUpscaler) { RDG_EVENT_SCOPE( GraphBuilder, "ThirdParty SecondaryUpscale %s %dx%d -> %dx%d", CustomUpscaler->GetDebugName(), SceneColor.ViewRect.Width(), SceneColor.ViewRect.Height(), View.UnscaledViewRect.Width(), View.UnscaledViewRect.Height()); SceneColor = CustomUpscaler->AddPasses(GraphBuilder, View, PassInputs); if (PassSequence.IsLastPass(EPass::SecondaryUpscale)) { check(SceneColor == ViewFamilyOutput); } else { check(SceneColor.ViewRect.Size() == View.UnscaledViewRect.Size()); } } } if (PassSequence.IsEnabled(EPass::Visualize)) { FScreenPassRenderTarget OverrideOutput; PassSequence.AcceptOverrideIfLastPass(EPass::Visualize, OverrideOutput); switch (View.Family->GetDebugViewShaderMode()) { case DVSM_QuadComplexity: { float ComplexityScale = 1.f / (float)(GEngine->QuadComplexityColors.Num() - 1) / NormalizedQuadComplexityValue; // .1f comes from the values used in LightAccumulator_GetResult FVisualizeComplexityInputs PassInputs; PassInputs.OverrideOutput = OverrideOutput; PassInputs.SceneColor = SceneColor; PassInputs.Colors = GEngine->QuadComplexityColors; PassInputs.ColorSamplingMethod = FVisualizeComplexityInputs::EColorSamplingMethod::Stair; PassInputs.ComplexityScale = ComplexityScale; PassInputs.bDrawLegend = true; PassInputs.OverrideOutput.LoadAction = View.IsFirstInFamily() ? ERenderTargetLoadAction::EClear : ERenderTargetLoadAction::ELoad; SceneColor = AddVisualizeComplexityPass(GraphBuilder, View, PassInputs); break; } case DVSM_ShaderComplexity: case DVSM_ShaderComplexityContainedQuadOverhead: case DVSM_ShaderComplexityBleedingQuadOverhead: { FVisualizeComplexityInputs PassInputs; PassInputs.OverrideOutput = OverrideOutput; PassInputs.SceneColor = SceneColor; PassInputs.Colors = GEngine->ShaderComplexityColors; PassInputs.ColorSamplingMethod = FVisualizeComplexityInputs::EColorSamplingMethod::Ramp; PassInputs.ComplexityScale = 1.0f; PassInputs.bDrawLegend = true; PassInputs.OverrideOutput.LoadAction = View.IsFirstInFamily() ? ERenderTargetLoadAction::EClear : ERenderTargetLoadAction::ELoad; SceneColor = AddVisualizeComplexityPass(GraphBuilder, View, PassInputs); break; } default: ensure(false); break; } } if (PassSequence.IsEnabled(EPass::VisualizeLightGrid)) { FScreenPassRenderTarget OverrideOutput; PassSequence.AcceptOverrideIfLastPass(EPass::VisualizeLightGrid, OverrideOutput); SceneColor = AddVisualizeLightGridPass(GraphBuilder, View, SceneColor, SceneDepth); } if (ShaderPrint::IsEnabled(View.ShaderPrintData)) { ShaderPrint::DrawView(GraphBuilder, View, SceneColor, SceneDepth); } if (PassSequence.IsEnabled(EPass::HMDDistortion)) { FHMDDistortionInputs PassInputs; PassSequence.AcceptOverrideIfLastPass(EPass::HMDDistortion, PassInputs.OverrideOutput); PassInputs.SceneColor = SceneColor; PassInputs.OverrideOutput.LoadAction = View.IsFirstInFamily() ? ERenderTargetLoadAction::EClear : ERenderTargetLoadAction::ELoad; SceneColor = AddHMDDistortionPass(GraphBuilder, View, PassInputs); } #if !UE_BUILD_SHIPPING AddUserSceneTextureDebugPass(GraphBuilder, View, ViewIndex, SceneColor); #endif // Copy the scene color to back buffer in case there is no post process, such as LDR MSAA. if (SceneColor.Texture != ViewFamilyOutput.Texture) { const uint32 RTMultiViewCount = (View.bIsMobileMultiViewEnabled) ? 2 : (View.Aspects.IsMobileMultiViewEnabled() ? 1 : 0); AddDrawTexturePass(GraphBuilder, View, SceneColor, ViewFamilyOutput, RTMultiViewCount); } } FRDGTextureRef AddProcessPlanarReflectionPass( FRDGBuilder& GraphBuilder, const FViewInfo& View, FRDGTextureRef SceneColorTexture) { FSceneViewState* ViewState = View.ViewState; const EAntiAliasingMethod AntiAliasingMethod = View.AntiAliasingMethod; if (IsTemporalAccumulationBasedMethod(AntiAliasingMethod)) { check(ViewState); FSceneTextureParameters SceneTextures = GetSceneTextureParameters(GraphBuilder, View); const FTemporalAAHistory& InputHistory = View.PrevViewInfo.TemporalAAHistory; FTemporalAAHistory* OutputHistory = &ViewState->PrevFrameViewInfo.TemporalAAHistory; FTAAPassParameters Parameters(View); Parameters.SceneDepthTexture = SceneTextures.SceneDepthTexture; // Planar reflections don't support velocity. Parameters.SceneVelocityTexture = nullptr; Parameters.SceneColorInput = SceneColorTexture; FTAAOutputs PassOutputs = AddTemporalAAPass( GraphBuilder, View, Parameters, InputHistory, OutputHistory); return PassOutputs.SceneColor; } else { return SceneColorTexture; } } #if DEBUG_POST_PROCESS_VOLUME_ENABLE FScreenPassTexture AddFinalPostProcessDebugInfoPasses(FRDGBuilder& GraphBuilder, const FViewInfo& View, FScreenPassTexture& ScreenPassSceneColor) { RDG_EVENT_SCOPE(GraphBuilder, "FinalPostProcessDebugInfo"); FRDGTextureRef SceneColor = ScreenPassSceneColor.Texture; AddDrawCanvasPass(GraphBuilder, RDG_EVENT_NAME("PostProcessDebug"), View, FScreenPassRenderTarget(SceneColor, View.ViewRect, ERenderTargetLoadAction::ELoad), [&View](FCanvas& Canvas) { FLinearColor TextColor(FLinearColor::White); FLinearColor GrayTextColor(FLinearColor::Gray); FLinearColor GreenTextColor(FLinearColor::Green); FString Text; const float ViewPortWidth = float(View.ViewRect.Width()); const float ViewPortHeight = float(View.ViewRect.Height()); const float CRHeight = 20.0f; const float PrintX_CR = ViewPortWidth * 0.1f; float PrintX = PrintX_CR; float PrintY = ViewPortHeight * 0.2f; Text = FString::Printf(TEXT("Post-processing volume debug (count = %i)"), View.FinalPostProcessDebugInfo.Num()); Canvas.DrawShadowedString(PrintX, PrintY, *Text, GetStatsFont(), GreenTextColor); PrintX = PrintX_CR; PrintY += CRHeight * 1.5; Canvas.DrawShadowedString(PrintX, PrintY, *FString("Name"), GetStatsFont(), GrayTextColor); PrintX += 256.0f; Canvas.DrawShadowedString(PrintX, PrintY, *FString("IsEnabled"), GetStatsFont(), GrayTextColor); PrintX += 96.0f; Canvas.DrawShadowedString(PrintX, PrintY, *FString("Priority"), GetStatsFont(), GrayTextColor); PrintX += 96.0f; Canvas.DrawShadowedString(PrintX, PrintY, *FString("CurrentWeight"), GetStatsFont(), GrayTextColor); PrintX += 96.0f; Canvas.DrawShadowedString(PrintX, PrintY, *FString("bIsUnbound"), GetStatsFont(), GrayTextColor); PrintX += 96.0f; PrintY += CRHeight; PrintX = PrintX_CR; const int32 PPDebugInfoCount = View.FinalPostProcessDebugInfo.Num() - 1; for (int32 i = PPDebugInfoCount; i >= 0 ; --i) { const FPostProcessSettingsDebugInfo& PPDebugInfo = View.FinalPostProcessDebugInfo[i]; Text = FString::Printf(TEXT("%s"), *PPDebugInfo.Name.Left(40)); // Clamp the name to a reasonable length Canvas.DrawShadowedString(PrintX, PrintY, *Text, GetStatsFont(), TextColor); PrintX += 256.0f; Text = FString::Printf(TEXT("%d"), PPDebugInfo.bIsEnabled ? 1 : 0); Canvas.DrawShadowedString(PrintX+32.0f, PrintY, *Text, GetStatsFont(), TextColor); PrintX += 96.0f; Text = FString::Printf(TEXT("%.3f"), PPDebugInfo.Priority); Canvas.DrawShadowedString(PrintX, PrintY, *Text, GetStatsFont(), TextColor); PrintX += 96.0f; Text = FString::Printf(TEXT("%3.3f"), PPDebugInfo.CurrentBlendWeight); Canvas.DrawShadowedString(PrintX+32.0f, PrintY, *Text, GetStatsFont(), TextColor); PrintX += 96.0f; Text = FString::Printf(TEXT("%d"), PPDebugInfo.bIsUnbound ? 1 : 0); Canvas.DrawShadowedString(PrintX+32.0f, PrintY, *Text, GetStatsFont(), TextColor); PrintX += 96.0f; Canvas.DrawShadowedString(PrintX_CR, PrintY+3.0f, *FString("______________________________________________________________________________________________________________"), GetStatsFont(), TextColor); PrintX = PrintX_CR; PrintY += CRHeight; } }); return MoveTemp(ScreenPassSceneColor); } #endif #if !UE_BUILD_SHIPPING // Canvas.DrawShadowedString returns height -- this variation returns width static float CanvasDrawShadowedStringReturnWidth(FCanvas& Canvas, float PrintX, float PrintY, const FString& Text, const UFont* Font, FLinearColor TextColor) { FCanvasTextStringViewItem TextItem(FVector2D(PrintX, PrintY), Text, Font, TextColor); if (Font && Font->ImportOptions.bUseDistanceFieldAlpha) { TextItem.BlendMode = SE_BLEND_MaskedDistanceFieldShadowed; } else { TextItem.EnableShadow(FLinearColor::Black); } Canvas.DrawItem(TextItem); return TextItem.DrawnSize.X / Canvas.GetDPIScale(); } static void AddUserSceneTextureDebugPass(FRDGBuilder& GraphBuilder, const FViewInfo& View, int32 ViewIndex, FScreenPassTexture Output) { int32 UserSceneTextureDebug = CVarUserSceneTextureDebug.GetValueOnRenderThread(); bool bEnableUserSceneTextureDebug = false; if (UserSceneTextureDebug == 1) { // Enable always bEnableUserSceneTextureDebug = true; } else if (UserSceneTextureDebug == 2 && GAreScreenMessagesEnabled) { // Enable conditionally if there are errors const FSceneTextures& SceneTextures = View.GetSceneTextures(); for (const FUserSceneTextureEventData& EventData : SceneTextures.UserSceneTextureEvents) { if (EventData.Event == EUserSceneTextureEvent::MissingInput || EventData.Event == EUserSceneTextureEvent::CollidingInput) { bEnableUserSceneTextureDebug = true; break; } } for (auto& UserSceneTextureElement : SceneTextures.UserSceneTextures) { if (bEnableUserSceneTextureDebug) { break; } for (FTransientUserSceneTexture& UserSceneTexture : UserSceneTextureElement.Value) { if (!UserSceneTexture.bUsed) { bEnableUserSceneTextureDebug = true; break; } } } } else if (UserSceneTextureDebug == 3) { // Enable conditionally for view with texture being visualized if (GVisualizeTexture.IsRequestedView()) { bEnableUserSceneTextureDebug = true; } } if (!bEnableUserSceneTextureDebug) { return; } const FSceneTextures& SceneTextures = View.GetSceneTextures(); if (!SceneTextures.UserSceneTextureEvents.IsEmpty()) { FScreenPassRenderTarget OutputTarget = FScreenPassRenderTarget(Output, ERenderTargetLoadAction::ELoad); AddDrawCanvasPass(GraphBuilder, RDG_EVENT_NAME("UserSceneTextureDebug"), View, OutputTarget, [&View, ViewIndex, &SceneTextures](FCanvas& Canvas) { FLinearColor TextColor(FLinearColor::White); FLinearColor GrayTextColor(FLinearColor::Gray); FLinearColor GreenTextColor(FLinearColor::Green); FLinearColor RedTextColor(FLinearColor::Red); FLinearColor YellowTextColor(FLinearColor::Yellow); FLinearColor MagentaTextColor(1.f, 0.f, 1.f); FString Text; const UFont* Font = GetStatsFont(); const float ViewPortWidth = float(View.ViewRect.Width()); const float ViewPortHeight = float(View.ViewRect.Height()); const float CRHeight = 20.0f; const float OffsetFromLeft = 0.05f; const float OffsetFromTop = 0.2f; const float OffsetFromHeader = CRHeight * 1.5f; float PrintX_CR = ViewPortWidth * OffsetFromLeft; float PrintX = PrintX_CR; float PrintY = ViewPortHeight * OffsetFromTop; int32 NumPasses = 0; for (const FUserSceneTextureEventData& EventData : SceneTextures.UserSceneTextureEvents) { if (EventData.ViewIndex == ViewIndex && EventData.Event == EUserSceneTextureEvent::Pass) { NumPasses++; } } // Draw header Text = FString::Printf(TEXT("User Scene Texture Passes (count = %i)"), NumPasses); PrintX += CanvasDrawShadowedStringReturnWidth(Canvas, PrintX, PrintY, *Text, Font, GreenTextColor); if (CVarUserSceneTextureDebug.GetValueOnRenderThread() == 2) { CanvasDrawShadowedStringReturnWidth(Canvas, PrintX, PrintY, TEXT(" - enabled on error via \"r.PostProcessing.UserSceneTextureDebug 2\""), Font, GreenTextColor); } PrintX = PrintX_CR; PrintY += CRHeight; // Draw column description CanvasDrawShadowedStringReturnWidth(Canvas, PrintX, PrintY, TEXT("Location [Priority] Material: Inputs --> Output"), Font, GrayTextColor); PrintY += OffsetFromHeader; // Draw blendable locations and priorities static_assert(BL_MAX == 7); static const TCHAR* GBlendableLocationShortNames[BL_MAX + 1] = {}; if (!GBlendableLocationShortNames[0]) { // One time init -- enum in header isn't in numerical order, so it's simpler to initialize this way GBlendableLocationShortNames[BL_SceneColorBeforeDOF] = TEXT("BeforeDOF"); GBlendableLocationShortNames[BL_SceneColorAfterDOF] = TEXT("AfterDOF"); GBlendableLocationShortNames[BL_TranslucencyAfterDOF] = TEXT("Translucent"); GBlendableLocationShortNames[BL_SSRInput] = TEXT("SSRInput"); GBlendableLocationShortNames[BL_SceneColorBeforeBloom] = TEXT("BeforeBloom"); GBlendableLocationShortNames[BL_ReplacingTonemapper] = TEXT("ReplaceTonemap"); GBlendableLocationShortNames[BL_SceneColorAfterTonemapping] = TEXT("AfterTonemap"); GBlendableLocationShortNames[BL_MAX] = TEXT("MAX"); } static_assert((int32)FCustomRenderPassBase::ERenderOutput::MAX == 7); static const TCHAR* GCustomRenderPassOutputShortNames[(int32)FCustomRenderPassBase::ERenderOutput::MAX] = { TEXT("Depth"), // SceneDepth TEXT("Devdepth"), // DeviceDepth TEXT("ColDepth"), // SceneColorAndDepth TEXT("Color"), // SceneColorAndAlpha TEXT("ColorNoA"), // SceneColorNoAlpha TEXT("Base"), // BaseColor TEXT("Norm"), // Normal }; float MaxBlendableInfoWidth = 0.0f; for (const FUserSceneTextureEventData& EventData : SceneTextures.UserSceneTextureEvents) { if (EventData.ViewIndex == ViewIndex && EventData.Event == EUserSceneTextureEvent::Pass) { const FMaterialRenderProxy* RenderProxy = EventData.MaterialInterface->GetRenderProxy(); const FMaterial* Material = RenderProxy->GetMaterialNoFallback(View.FeatureLevel); Text = FString::Printf(TEXT("%s [%d]"), GBlendableLocationShortNames[FMath::Min((uint32)RenderProxy->GetBlendableLocation(Material), (uint32)BL_MAX)], RenderProxy->GetBlendablePriority(Material)); float BlendableInfoWidth = CanvasDrawShadowedStringReturnWidth(Canvas, PrintX, PrintY, Text, Font, TextColor); MaxBlendableInfoWidth = FMath::Max(MaxBlendableInfoWidth, BlendableInfoWidth); PrintY += CRHeight; } else if (EventData.ViewIndex == ViewIndex && EventData.Event == EUserSceneTextureEvent::CustomRenderPass) { const FCustomRenderPassBase* RenderPass = (const FCustomRenderPassBase*)EventData.MaterialInterface; const FSceneCaptureCustomRenderPassUserData& UserData = FSceneCaptureCustomRenderPassUserData::Get(RenderPass); // EventData.AllocationOrder stores the ERenderOutput enum Text = FString::Printf(TEXT("%s (CRP:%s)"), *UserData.CaptureActorName, GCustomRenderPassOutputShortNames[EventData.AllocationOrder]); float BlendableInfoWidth = CanvasDrawShadowedStringReturnWidth(Canvas, PrintX, PrintY, Text, Font, TextColor); MaxBlendableInfoWidth = FMath::Max(MaxBlendableInfoWidth, BlendableInfoWidth); PrintY += CRHeight; } } PrintX_CR = PrintX_CR + MaxBlendableInfoWidth + 10.0f; PrintX = PrintX_CR; PrintY = ViewPortHeight * OffsetFromTop + CRHeight + OffsetFromHeader; // Draw material names float MaxNameWidth = 0.0f; for (const FUserSceneTextureEventData& EventData : SceneTextures.UserSceneTextureEvents) { if (EventData.ViewIndex == ViewIndex && EventData.Event == EUserSceneTextureEvent::Pass) { const UMaterialInterface* MaterialInterface = EventData.MaterialInterface; // Skip over runtime generated dynamic instance when producing name while (MaterialInterface->IsA()) { MaterialInterface = ((const UMaterialInstanceDynamic*)MaterialInterface)->Parent; } Text = FString::Printf(TEXT("%s:"), *MaterialInterface->GetName()); float NameWidth = CanvasDrawShadowedStringReturnWidth(Canvas, PrintX, PrintY, Text, Font, TextColor); MaxNameWidth = FMath::Max(MaxNameWidth, NameWidth); PrintY += CRHeight; } } PrintX_CR = PrintX_CR + MaxNameWidth + 10.0f; PrintX = PrintX_CR; PrintY = ViewPortHeight * OffsetFromTop + CRHeight + OffsetFromHeader; // Draw everything else (inputs and outputs) bool bAnyMissing = false; bool bAnyUnused = false; bool bAnyColliding = false; for (const FUserSceneTextureEventData& EventData : SceneTextures.UserSceneTextureEvents) { if (EventData.ViewIndex == ViewIndex) { switch (EventData.Event) { case EUserSceneTextureEvent::MissingInput: Text = FString::Printf(TEXT(" %s"), *EventData.Name.ToString()); PrintX += CanvasDrawShadowedStringReturnWidth(Canvas, PrintX, PrintY, Text, Font, RedTextColor); bAnyMissing = true; break; case EUserSceneTextureEvent::CollidingInput: Text = FString::Printf(TEXT(" %s"), *EventData.Name.ToString()); PrintX += CanvasDrawShadowedStringReturnWidth(Canvas, PrintX, PrintY, Text, Font, MagentaTextColor); bAnyColliding = true; break; case EUserSceneTextureEvent::FoundInput: Text = FString::Printf(TEXT(" %s"), *EventData.Name.ToString()); PrintX += CanvasDrawShadowedStringReturnWidth(Canvas, PrintX, PrintY, Text, Font, GrayTextColor); break; case EUserSceneTextureEvent::Output: { const FTransientUserSceneTexture* UserTexture = SceneTextures.FindUserSceneTextureByEvent(EventData); check(UserTexture); Text = FString::Printf(TEXT(" --> %s [%dx%d]"), *EventData.Name.ToString(), EventData.RectSize.X, EventData.RectSize.Y); PrintX += CanvasDrawShadowedStringReturnWidth(Canvas, PrintX, PrintY, *Text, Font, UserTexture->bUsed ? GrayTextColor : YellowTextColor); bAnyUnused = bAnyUnused || !UserTexture->bUsed; // MaterialInterface can be null if this output was generated by a CustomRenderPass if (EventData.MaterialInterface && EventData.MaterialInterface->GetBlendMode() != BLEND_Opaque) { PrintX += CanvasDrawShadowedStringReturnWidth(Canvas, PrintX, PrintY, TEXT(" Blend"), Font, GrayTextColor); } } break; case EUserSceneTextureEvent::Pass: case EUserSceneTextureEvent::CustomRenderPass: // End of line PrintY += CRHeight; PrintX = PrintX_CR; break; } } } // Print color codings for warnings if present PrintX = ViewPortWidth * OffsetFromLeft; PrintY += CRHeight * 0.5f; if (bAnyUnused) { CanvasDrawShadowedStringReturnWidth(Canvas, PrintX, PrintY, TEXT("Yellow: Unused Output"), Font, YellowTextColor); PrintY += CRHeight; } if (bAnyMissing) { CanvasDrawShadowedStringReturnWidth(Canvas, PrintX, PrintY, TEXT("Red: Missing Input"), Font, RedTextColor); PrintY += CRHeight; } if (bAnyColliding) { CanvasDrawShadowedStringReturnWidth(Canvas, PrintX, PrintY, TEXT("Magenta: Input collides with Output"), Font, MagentaTextColor); PrintY += CRHeight; } }); } } #endif // !UE_BUILD_SHIPPING // Shader for visualizing GBuffer values class FGBufferPickingCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FGBufferPickingCS); SHADER_USE_PARAMETER_STRUCT(FGBufferPickingCS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneTextureUniformParameters, SceneTexturesStruct) SHADER_PARAMETER_STRUCT_INCLUDE(ShaderPrint::FShaderParameters, ShaderPrintParameters) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer) END_SHADER_PARAMETER_STRUCT() public: static bool IsSupported(EShaderPlatform Platform) { return IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM5) && ShaderPrint::IsSupported(Platform) && !IsHlslccShaderPlatform(Platform) && !IsMobilePlatform(Platform) && !Substrate::IsSubstrateEnabled(); } static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return IsSupported(Parameters.Platform) && EnumHasAllFlags(Parameters.Flags, EShaderPermutationFlags::HasEditorOnlyData); } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); ShaderPrint::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("SHADER_GBUFFER_PICKING"), 1); } }; IMPLEMENT_GLOBAL_SHADER(FGBufferPickingCS, "/Engine/Private/PostProcessGBufferHints.usf", "MainCS", SF_Compute); #if WITH_EDITOR static void AddGBufferPicking(FRDGBuilder& GraphBuilder, const FViewInfo& View, const TRDGUniformBufferRef& SceneTextures) { if (CVarGBufferPicking.GetValueOnRenderThread() <= 0 || !FGBufferPickingCS::IsSupported(View.Family->GetShaderPlatform())) { return; } // Force ShaderPrint on. ShaderPrint::SetEnabled(true); FGBufferPickingCS::FParameters* Parameters = GraphBuilder.AllocParameters(); Parameters->ViewUniformBuffer = View.ViewUniformBuffer; Parameters->SceneTexturesStruct = SceneTextures; ShaderPrint::SetParameters(GraphBuilder, View.ShaderPrintData, Parameters->ShaderPrintParameters); TShaderMapRef ComputeShader(View.ShaderMap); FComputeShaderUtils::AddPass(GraphBuilder, RDG_EVENT_NAME("Debug::GBufferPicking"), ComputeShader, Parameters, FIntVector(1,1,1)); } #endif