Files
UnrealEngine/Engine/Source/Runtime/Renderer/Private/PostProcess/PostProcessing.cpp
2025-05-18 13:04:45 +08:00

3817 lines
160 KiB
C++

// 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<float> 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<float> 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<bool> 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<int32> CVarPostProcessingPreferCompute(
TEXT("r.PostProcessing.PreferCompute"),
0,
TEXT("Will use compute shaders for post processing where implementations available."),
ECVF_RenderThreadSafe);
TAutoConsoleVariable<int32> CVarPostProcessingQuarterResolutionDownsample(
TEXT("r.PostProcessing.QuarterResolutionDownsample"),
0,
TEXT("Uses quarter resolution downsample instead of half resolution to feed into exposure / bloom."),
ECVF_RenderThreadSafe);
TAutoConsoleVariable<int32> 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<int32> 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<int32> 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<int32> CVarGBufferPicking(
TEXT("r.PostProcessing.GBufferPicking"), 0,
TEXT("Evaluate GBuffer value for debugging purpose."),
ECVF_RenderThreadSafe);
#endif
#if !(UE_BUILD_SHIPPING)
TAutoConsoleVariable<int32> 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<FSceneTextureUniformParameters>& SceneTextures);
#endif
EDownsampleQuality GetDownsampleQuality(const TAutoConsoleVariable<int32>& CVar)
{
const int32 DownsampleQuality = FMath::Clamp(CVar.GetValueOnRenderThread(), 0, 1);
return static_cast<EDownsampleQuality>(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<uint32>(EPass::MAX) == UE_ARRAY_COUNT(PassNames), "EPass does not match PassNames.");
TOverridePassSequence<EPass> 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<int32>(EPass::MotionBlur) == 0);
constexpr int32 FirstAfterPass = static_cast<int32>(ISceneViewExtension::EPostProcessingPass::MotionBlur);
// Scene view extension delegates that precede the override pass sequence are to be called directly.
TStaticArray<FPostProcessingPassDelegateArray, static_cast<uint32>(FirstAfterPass)> SceneViewExtensionDelegates;
for (const TSharedRef<ISceneViewExtension>& ViewExtension : View.Family->ViewExtensions)
{
for (int32 SceneViewPassId = 0; SceneViewPassId < FirstAfterPass; SceneViewPassId++)
{
const ISceneViewExtension::EPostProcessingPass SceneViewPass = static_cast<ISceneViewExtension::EPostProcessingPass>(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<int32>(ISceneViewExtension::EPostProcessingPass::MAX); SceneViewPassId++)
{
const ISceneViewExtension::EPostProcessingPass SceneViewPass = static_cast<ISceneViewExtension::EPostProcessingPass>(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<uint32>(ISceneViewExtension::EPostProcessingPass::BeforeDOF)].Num())
{
SceneColor = AddSceneViewExtensionPassChain(GraphBuilder, View, GetPostProcessMaterialInputs(SceneColor),
SceneViewExtensionDelegates[static_cast<uint32>(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<uint32>(ISceneViewExtension::EPostProcessingPass::AfterDOF)].Num())
{
SceneColor = AddSceneViewExtensionPassChain(GraphBuilder, View, GetPostProcessMaterialInputs(SceneColor),
SceneViewExtensionDelegates[static_cast<uint32>(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<uint32>(ISceneViewExtension::EPostProcessingPass::TranslucencyAfterDOF)].Num())
{
FScreenPassTexture PostDOFTranslucency = AddSceneViewExtensionPassChain(
GraphBuilder, View,
GetPostProcessMaterialInputs(SceneColor),
SceneViewExtensionDelegates[static_cast<uint32>(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<uint32>(ISceneViewExtension::EPostProcessingPass::SSRInput)].Num())
{
PassInputs = GetPostProcessMaterialInputs(FScreenPassTexture::CopyFromSlice(GraphBuilder, SceneColorSlice));
PassOutput = AddSceneViewExtensionPassChain(GraphBuilder, View, PassInputs,
SceneViewExtensionDelegates[static_cast<uint32>(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<uint32>(ELensFlareQuality::MAX) - static_cast<uint32>(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<uint32>(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<const FViewInfo*>(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<uint32>(EPass::MAX) == UE_ARRAY_COUNT(PassNames), "EPass does not match PassNames.");
TOverridePassSequence<EPass> 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<uint32>(EPass::MAX) == UE_ARRAY_COUNT(PassNames), "EPass does not match PassNames.");
TOverridePassSequence<EPass> 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<int32>(ISceneViewExtension::EPostProcessingPass::MotionBlur);
// Scene view extension delegates that precede the override pass sequence are to be called directly.
TStaticArray<FPostProcessingPassDelegateArray, static_cast<uint32>(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<ISceneViewExtension>& ViewExtension : View.Family->ViewExtensions)
{
for (int32 SceneViewPassId = 0; SceneViewPassId < FirstAfterPass; SceneViewPassId++)
{
const ISceneViewExtension::EPostProcessingPass SceneViewPass = static_cast<ISceneViewExtension::EPostProcessingPass>(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<int32>(ISceneViewExtension::EPostProcessingPass::MAX); SceneViewPassId++)
{
const ISceneViewExtension::EPostProcessingPass SceneViewPass = static_cast<ISceneViewExtension::EPostProcessingPass>(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<uint32>(ISceneViewExtension::EPostProcessingPass::BeforeDOF)].Num())
{
SceneColor = AddSceneViewExtensionPassChain(GraphBuilder, View, GetPostProcessMaterialInputs(SceneColor),
SceneViewExtensionDelegates[static_cast<uint32>(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<uint32>(ELensFlareQuality::MAX) - static_cast<uint32>(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<uint32>(ISceneViewExtension::EPostProcessingPass::AfterDOF)].Num())
{
SceneColor = AddSceneViewExtensionPassChain(GraphBuilder, View, GetPostProcessMaterialInputs(SceneColor),
SceneViewExtensionDelegates[static_cast<uint32>(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<const FViewInfo*>(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<UMaterialInstanceDynamic>())
{
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<FSceneTextureUniformParameters>& SceneTextures)
{
if (CVarGBufferPicking.GetValueOnRenderThread() <= 0 || !FGBufferPickingCS::IsSupported(View.Family->GetShaderPlatform()))
{
return;
}
// Force ShaderPrint on.
ShaderPrint::SetEnabled(true);
FGBufferPickingCS::FParameters* Parameters = GraphBuilder.AllocParameters<FGBufferPickingCS::FParameters>();
Parameters->ViewUniformBuffer = View.ViewUniformBuffer;
Parameters->SceneTexturesStruct = SceneTextures;
ShaderPrint::SetParameters(GraphBuilder, View.ShaderPrintData, Parameters->ShaderPrintParameters);
TShaderMapRef<FGBufferPickingCS> ComputeShader(View.ShaderMap);
FComputeShaderUtils::AddPass(GraphBuilder, RDG_EVENT_NAME("Debug::GBufferPicking"), ComputeShader, Parameters, FIntVector(1,1,1));
}
#endif