// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. /*============================================================================= PostProcessMaterial.cpp: Post processing Material implementation. =============================================================================*/ #include "PostProcess/PostProcessMaterial.h" #include "RendererModule.h" #include "Materials/Material.h" #include "MaterialShaderType.h" #include "MaterialShader.h" #include "SceneUtils.h" #include "PostProcess/SceneRenderTargets.h" #include "PostProcess/SceneFilterRendering.h" #include "SceneRendering.h" #include "ClearQuad.h" #include "Materials/MaterialExpressionSceneTexture.h" #include "RHI/Public/PipelineStateCache.h" #include "PostProcessing.h" #include "PostProcessMobile.h" #include "BufferVisualizationData.h" namespace { TAutoConsoleVariable CVarPostProcessAllowStencilTest( TEXT("r.PostProcessAllowStencilTest"), 1, TEXT("Enables stencil testing in post process materials.\n") TEXT("0: disable stencil testing\n") TEXT("1: allow stencil testing\n") ); TAutoConsoleVariable CVarPostProcessAllowBlendModes( TEXT("r.PostProcessAllowBlendModes"), 1, TEXT("Enables blend modes in post process materials.\n") TEXT("0: disable blend modes. Uses replace\n") TEXT("1: allow blend modes\n") ); TAutoConsoleVariable CVarPostProcessingDisableMaterials( TEXT("r.PostProcessing.DisableMaterials"), 0, TEXT(" Allows to disable post process materials. \n"), ECVF_Scalability | ECVF_RenderThreadSafe); bool IsPostProcessStencilTestAllowed() { return CVarPostProcessAllowStencilTest.GetValueOnRenderThread() != 0; } bool IsCustomDepthEnabled() { static const IConsoleVariable* CVarCustomDepth = IConsoleManager::Get().FindConsoleVariable(TEXT("r.CustomDepth")); check(CVarCustomDepth); return CVarCustomDepth->GetInt() == 3; } enum class ECustomDepthPolicy : uint32 { // Custom depth is disabled. Disabled, // Custom Depth-Stencil is enabled; potentially simultaneous SRV / DSV usage. Enabled }; ECustomDepthPolicy GetMaterialCustomDepthPolicy(const FMaterial* Material, ERHIFeatureLevel::Type FeatureLevel) { check(Material); // Material requesting stencil test and post processing CVar allows it. if (Material->IsStencilTestEnabled() && IsPostProcessStencilTestAllowed()) { // Custom stencil texture allocated and available. if (IsCustomDepthEnabled()) { return ECustomDepthPolicy::Enabled; } else { UE_LOG(LogRenderer, Warning, TEXT("PostProcessMaterial uses stencil test, but stencil not allocated. Set r.CustomDepth to 3 to allocate custom stencil.")); } } return ECustomDepthPolicy::Disabled; } void GetMaterialInfo( const UMaterialInterface* InMaterialInterface, ERHIFeatureLevel::Type InFeatureLevel, EPixelFormat InOutputFormat, const FMaterial*& OutMaterial, const FMaterialRenderProxy*& OutMaterialProxy, const FMaterialShaderMap*& OutMaterialShaderMap) { const FMaterialRenderProxy* MaterialProxy = InMaterialInterface->GetRenderProxy(); check(MaterialProxy); const FMaterial* Material = MaterialProxy->GetMaterialNoFallback(InFeatureLevel); if (!Material || Material->GetMaterialDomain() != MD_PostProcess || !Material->GetRenderingThreadShaderMap()) { // Fallback to the default post process material. const UMaterialInterface* DefaultMaterial = UMaterial::GetDefaultMaterial(MD_PostProcess); check(DefaultMaterial); check(DefaultMaterial != InMaterialInterface); return GetMaterialInfo( DefaultMaterial, InFeatureLevel, InOutputFormat, OutMaterial, OutMaterialProxy, OutMaterialShaderMap); } if (Material->IsStencilTestEnabled() || Material->GetBlendableOutputAlpha()) { // Only allowed to have blend/stencil test if output format is compatible with ePId_Input0. // PF_Unknown implies output format is that of EPId_Input0 ensure(InOutputFormat == PF_Unknown); } const FMaterialShaderMap* MaterialShaderMap = Material->GetRenderingThreadShaderMap();; check(MaterialShaderMap); OutMaterial = Material; OutMaterialProxy = MaterialProxy; OutMaterialShaderMap = MaterialShaderMap; } FRHIDepthStencilState* GetMaterialStencilState(const FMaterial* Material) { static FRHIDepthStencilState* StencilStates[] = { TStaticDepthStencilState::GetRHI(), TStaticDepthStencilState::GetRHI(), TStaticDepthStencilState::GetRHI(), TStaticDepthStencilState::GetRHI(), TStaticDepthStencilState::GetRHI(), TStaticDepthStencilState::GetRHI(), TStaticDepthStencilState::GetRHI(), TStaticDepthStencilState::GetRHI(), }; static_assert(EMaterialStencilCompare::MSC_Count == UE_ARRAY_COUNT(StencilStates), "Ensure that all EMaterialStencilCompare values are accounted for."); check(Material); return StencilStates[Material->GetStencilCompare()]; } bool IsMaterialBlendEnabled(const FMaterial* Material) { check(Material); return Material->GetBlendableOutputAlpha() && CVarPostProcessAllowBlendModes.GetValueOnRenderThread() != 0; } FRHIBlendState* GetMaterialBlendState(const FMaterial* Material) { static FRHIBlendState* BlendStates[] = { TStaticBlendState<>::GetRHI(), TStaticBlendState<>::GetRHI(), TStaticBlendState::GetRHI(), TStaticBlendState::GetRHI(), TStaticBlendState::GetRHI(), TStaticBlendState::GetRHI(), TStaticBlendState::GetRHI(), }; static_assert(EBlendMode::BLEND_MAX == UE_ARRAY_COUNT(BlendStates), "Ensure that all EBlendMode values are accounted for."); check(Material); return BlendStates[Material->GetBlendMode()]; } class FPostProcessMaterialShader : public FMaterialShader { public: using FParameters = FPostProcessMaterialParameters; SHADER_USE_PARAMETER_STRUCT_WITH_LEGACY_BASE(FPostProcessMaterialShader, FMaterialShader); class FMobileDimension : SHADER_PERMUTATION_BOOL("POST_PROCESS_MATERIAL_MOBILE"); using FPermutationDomain = TShaderPermutationDomain; static bool ShouldCompilePermutation(const FMaterialShaderPermutationParameters& Parameters) { if (Parameters.Material->GetMaterialDomain() == MD_PostProcess) { const FPermutationDomain PermutationVector(Parameters.PermutationId); if (PermutationVector.Get()) { return IsMobilePlatform(Parameters.Platform) && IsMobileHDR(); } else { return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5); } } return false; } static void ModifyCompilationEnvironment(const FMaterialShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FMaterialShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("POST_PROCESS_MATERIAL"), 1); EBlendableLocation Location = EBlendableLocation(Parameters.Material->GetBlendableLocation()); OutEnvironment.SetDefine(TEXT("POST_PROCESS_MATERIAL_BEFORE_TONEMAP"), (Location == BL_AfterTonemapping || Location == BL_ReplacingTonemapper) ? 0 : 1); FPermutationDomain PermutationVector(Parameters.PermutationId); if (PermutationVector.Get()) { OutEnvironment.SetDefine(TEXT("POST_PROCESS_MATERIAL_BEFORE_TONEMAP"), (Parameters.Material->GetBlendableLocation() != BL_AfterTonemapping) ? 1 : 0); } } protected: template void SetParameters(FRHICommandList& RHICmdList, TRHIShader* ShaderRHI, const FViewInfo& View, const FMaterialRenderProxy* Proxy, const FParameters& Parameters) { FMaterialShader::SetParameters(RHICmdList, ShaderRHI, Proxy, *Proxy->GetMaterial(View.GetFeatureLevel()), View, View.ViewUniformBuffer, ESceneTextureSetupMode::All); SetShaderParameters(RHICmdList, this, ShaderRHI, Parameters); } }; class FPostProcessMaterialVS : public FPostProcessMaterialShader { public: DECLARE_MATERIAL_SHADER(FPostProcessMaterialVS); void SetParameters(FRHICommandList& RHICmdList, const FViewInfo& View, const FMaterialRenderProxy* Proxy, const FParameters& Parameters) { FPostProcessMaterialShader::SetParameters(RHICmdList, GetVertexShader(), View, Proxy, Parameters); } FPostProcessMaterialVS() = default; FPostProcessMaterialVS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) : FPostProcessMaterialShader(Initializer) {} }; class FPostProcessMaterialPS : public FPostProcessMaterialShader { public: DECLARE_MATERIAL_SHADER(FPostProcessMaterialPS); void SetParameters(FRHICommandList& RHICmdList, const FViewInfo& View, const FMaterialRenderProxy* Proxy, const FParameters& Parameters) { FPostProcessMaterialShader::SetParameters(RHICmdList, GetPixelShader(), View, Proxy, Parameters); } FPostProcessMaterialPS() = default; FPostProcessMaterialPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) : FPostProcessMaterialShader(Initializer) {} }; IMPLEMENT_MATERIAL_SHADER(FPostProcessMaterialVS, "/Engine/Private/PostProcessMaterialShaders.usf", "MainVS", SF_Vertex); IMPLEMENT_MATERIAL_SHADER(FPostProcessMaterialPS, "/Engine/Private/PostProcessMaterialShaders.usf", "MainPS", SF_Pixel); class FPostProcessMaterialVertexDeclaration : public FRenderResource { public: FVertexDeclarationRHIRef VertexDeclarationRHI; void InitRHI() override { FVertexDeclarationElementList Elements; uint32 Stride = sizeof(FFilterVertex); Elements.Add(FVertexElement(0, STRUCT_OFFSET(FFilterVertex, Position), VET_Float4, 0, Stride)); VertexDeclarationRHI = PipelineStateCache::GetOrCreateVertexDeclaration(Elements); } void ReleaseRHI() override { VertexDeclarationRHI.SafeRelease(); } }; TGlobalResource GPostProcessMaterialVertexDeclaration; } //! namespace FScreenPassTexture AddPostProcessMaterialPass( FRDGBuilder& GraphBuilder, const FViewInfo& View, const FPostProcessMaterialInputs& Inputs, const UMaterialInterface* MaterialInterface) { Inputs.Validate(); const FScreenPassTexture SceneColor = Inputs.GetInput(EPostProcessMaterialInput::SceneColor); const ERHIFeatureLevel::Type FeatureLevel = View.GetFeatureLevel(); const FMaterial* Material = nullptr; const FMaterialRenderProxy* MaterialRenderProxy = nullptr; const FMaterialShaderMap* MaterialShaderMap = nullptr; GetMaterialInfo(MaterialInterface, FeatureLevel, Inputs.OutputFormat, Material, MaterialRenderProxy, MaterialShaderMap); FRHIDepthStencilState* DefaultDepthStencilState = FScreenPassPipelineState::FDefaultDepthStencilState::GetRHI(); FRHIDepthStencilState* DepthStencilState = DefaultDepthStencilState; FRDGTextureRef DepthStencilTexture = nullptr; // Allocate custom depth stencil texture(s) and depth stencil state. const ECustomDepthPolicy CustomStencilPolicy = GetMaterialCustomDepthPolicy(Material, FeatureLevel); if (CustomStencilPolicy == ECustomDepthPolicy::Enabled) { check(Inputs.CustomDepthTexture); DepthStencilTexture = Inputs.CustomDepthTexture; DepthStencilState = GetMaterialStencilState(Material); } FRHIBlendState* DefaultBlendState = FScreenPassPipelineState::FDefaultBlendState::GetRHI(); FRHIBlendState* BlendState = GetMaterialBlendState(Material); // Blend / Depth Stencil usage requires that the render target have primed color data. const bool bIsCompositeWithInput = DepthStencilState != DefaultDepthStencilState || BlendState != DefaultBlendState; // We only prime color on the output texture if we are using fixed function Blend / Depth-Stencil, // or we need to retain previously rendered views. const bool bPrimeOutputColor = bIsCompositeWithInput || !View.IsFirstInFamily(); FScreenPassRenderTarget Output = Inputs.OverrideOutput; // We can re-use the scene color texture as the render target if we're not simultaneously reading from it. // This is only necessary to do if we're going to be priming content from the render target since it avoids // the copy. Otherwise, we just allocate a new render target. if (!Output.IsValid() && !MaterialShaderMap->UsesSceneTexture(PPI_PostProcessInput0) && bPrimeOutputColor) { Output = FScreenPassRenderTarget(SceneColor, ERenderTargetLoadAction::ELoad); } else { // Allocate new transient output texture if none exists. if (!Output.IsValid()) { FRDGTextureDesc OutputDesc = SceneColor.Texture->Desc; OutputDesc.Reset(); if (Inputs.OutputFormat != PF_Unknown) { OutputDesc.Format = Inputs.OutputFormat; } OutputDesc.ClearValue = FClearValueBinding(FLinearColor::Black); OutputDesc.Flags |= GFastVRamConfig.PostProcessMaterial; Output = FScreenPassRenderTarget(GraphBuilder.CreateTexture(OutputDesc, TEXT("PostProcessMaterial")), SceneColor.ViewRect, View.GetOverwriteLoadAction()); } if (bPrimeOutputColor) { // Copy existing contents to new output and use load-action to preserve untouched pixels. AddDrawTexturePass(GraphBuilder, View, SceneColor.Texture, Output.Texture); Output.LoadAction = ERenderTargetLoadAction::ELoad; } } const FScreenPassTextureViewport SceneColorViewport(SceneColor); const FScreenPassTextureViewport OutputViewport(Output); RDG_EVENT_SCOPE(GraphBuilder, "PostProcessMaterial %dx%d Material=%s", SceneColorViewport.Rect.Width(), SceneColorViewport.Rect.Height(), *Material->GetFriendlyName()); FPostProcessMaterialParameters* PostProcessMaterialParameters = GraphBuilder.AllocParameters(); PostProcessMaterialParameters->PostProcessOutput = GetScreenPassTextureViewportParameters(SceneColorViewport); PostProcessMaterialParameters->CustomDepth = DepthStencilTexture; PostProcessMaterialParameters->RenderTargets[0] = Output.GetRenderTargetBinding(); if (DepthStencilTexture) { PostProcessMaterialParameters->RenderTargets.DepthStencil = FDepthStencilBinding( DepthStencilTexture, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ELoad, FExclusiveDepthStencil::DepthRead_StencilRead); } PostProcessMaterialParameters->PostProcessInput_BilinearSampler = TStaticSamplerState::GetRHI();; const FScreenPassTexture BlackDummy(GSystemTextures.GetBlackDummy(GraphBuilder)); // This gets passed in whether or not it's used. GraphBuilder.RemoveUnusedTextureWarning(BlackDummy.Texture); FRHISamplerState* PointClampSampler = TStaticSamplerState::GetRHI(); for (uint32 InputIndex = 0; InputIndex < kPostProcessMaterialInputCountMax; ++InputIndex) { FScreenPassTexture Input = Inputs.GetInput((EPostProcessMaterialInput)InputIndex); // Need to provide valid textures for when shader compilation doesn't cull unused parameters. if (!Input.Texture || !MaterialShaderMap->UsesSceneTexture(PPI_PostProcessInput0 + InputIndex)) { Input = BlackDummy; } PostProcessMaterialParameters->PostProcessInput[InputIndex] = GetScreenPassTextureInput(Input, PointClampSampler); } const bool bIsMobile = FeatureLevel <= ERHIFeatureLevel::ES3_1; PostProcessMaterialParameters->bFlipYAxis = Inputs.bFlipYAxis; FPostProcessMaterialShader::FPermutationDomain PermutationVector; PermutationVector.Set(bIsMobile); FPostProcessMaterialVS* VertexShader = MaterialShaderMap->GetShader(PermutationVector); FPostProcessMaterialPS* PixelShader = MaterialShaderMap->GetShader(PermutationVector); const uint32 MaterialStencilRef = Material->GetStencilRefValue(); EScreenPassDrawFlags ScreenPassFlags = EScreenPassDrawFlags::AllowHMDHiddenAreaMask; if (Inputs.bFlipYAxis) { ScreenPassFlags |= EScreenPassDrawFlags::FlipYAxis; } const bool bNeedsGBuffer = Material->NeedsGBuffer(); if (bNeedsGBuffer) { FSceneRenderTargets::Get(GraphBuilder.RHICmdList).AdjustGBufferRefCount(GraphBuilder.RHICmdList, 1); } GraphBuilder.AddPass( RDG_EVENT_NAME("PostProcessMaterial"), PostProcessMaterialParameters, ERDGPassFlags::Raster, [&View, OutputViewport, SceneColorViewport, VertexShader, PixelShader, BlendState, DepthStencilState, ScreenPassFlags, MaterialRenderProxy, PostProcessMaterialParameters, MaterialStencilRef, bNeedsGBuffer] (FRHICommandListImmediate& RHICmdList) { DrawScreenPass( RHICmdList, View, OutputViewport, SceneColorViewport, FScreenPassPipelineState(VertexShader, PixelShader, BlendState, DepthStencilState), ScreenPassFlags, [&](FRHICommandListImmediate&) { VertexShader->SetParameters(RHICmdList, View, MaterialRenderProxy, *PostProcessMaterialParameters); PixelShader->SetParameters(RHICmdList, View, MaterialRenderProxy, *PostProcessMaterialParameters); RHICmdList.SetStencilRef(MaterialStencilRef); }); if (bNeedsGBuffer) { FSceneRenderTargets::Get(RHICmdList).AdjustGBufferRefCount(RHICmdList, -1); } }); return MoveTemp(Output); } static bool IsPostProcessMaterialsEnabledForView(const FViewInfo& View) { if (!View.Family->EngineShowFlags.PostProcessing || !View.Family->EngineShowFlags.PostProcessMaterial || View.Family->EngineShowFlags.VisualizeShadingModels || CVarPostProcessingDisableMaterials.GetValueOnRenderThread() != 0) { return false; } return true; } static FPostProcessMaterialNode* IteratePostProcessMaterialNodes(const FFinalPostProcessSettings& Dest, EBlendableLocation Location, FBlendableEntry*& Iterator) { for (;;) { FPostProcessMaterialNode* DataPtr = Dest.BlendableManager.IterateBlendables(Iterator); if (!DataPtr || DataPtr->GetLocation() == Location) { return DataPtr; } } } FPostProcessMaterialChain GetPostProcessMaterialChain(const FViewInfo& View, EBlendableLocation Location) { if (!IsPostProcessMaterialsEnabledForView(View)) { return {}; } const FSceneViewFamily& ViewFamily = *View.Family; TArray> Nodes; FBlendableEntry* Iterator = nullptr; if (ViewFamily.EngineShowFlags.VisualizeBuffer) { UMaterial* Material = GetBufferVisualizationData().GetMaterial(View.CurrentBufferVisualizationMode); if (Material && Material->BlendableLocation == Location) { Nodes.Add(FPostProcessMaterialNode(Material, Location, Material->BlendablePriority)); } } while (FPostProcessMaterialNode* Data = IteratePostProcessMaterialNodes(View.FinalPostProcessSettings, Location, Iterator)) { check(Data->GetMaterialInterface()); Nodes.Add(*Data); } if (!Nodes.Num()) { return {}; } ::Sort(Nodes.GetData(), Nodes.Num(), FPostProcessMaterialNode::FCompare()); FPostProcessMaterialChain OutputChain; OutputChain.Reserve(Nodes.Num()); for (const FPostProcessMaterialNode& Node : Nodes) { OutputChain.Add(Node.GetMaterialInterface()); } return OutputChain; } FScreenPassTexture AddPostProcessMaterialChain( FRDGBuilder& GraphBuilder, const FViewInfo& View, const FPostProcessMaterialInputs& InputsTemplate, const FPostProcessMaterialChain& Materials) { FScreenPassTexture Outputs = InputsTemplate.GetInput(EPostProcessMaterialInput::SceneColor); for (const UMaterialInterface* MaterialInterface : Materials) { FPostProcessMaterialInputs Inputs = InputsTemplate; Inputs.SetInput(EPostProcessMaterialInput::SceneColor, Outputs); // Certain inputs are only respected by the final post process material in the chain. if (MaterialInterface != Materials.Last()) { Inputs.OverrideOutput = FScreenPassRenderTarget(); Inputs.bFlipYAxis = false; } Outputs = AddPostProcessMaterialPass(GraphBuilder, View, Inputs, MaterialInterface); } return Outputs; } FRenderingCompositePass* AddPostProcessMaterialPass( const FPostprocessContext& PostProcessContext, const UMaterialInterface* MaterialInterface, EPixelFormat OverrideOutputFormat) { const FMaterial* Material = nullptr; { const FMaterialRenderProxy* MaterialRenderProxy = nullptr; const FMaterialShaderMap* MaterialShaderMap = nullptr; GetMaterialInfo(MaterialInterface, PostProcessContext.View.GetFeatureLevel(), OverrideOutputFormat, Material, MaterialRenderProxy, MaterialShaderMap); check(Material); } if (Material->NeedsGBuffer()) { FSceneRenderTargets::Get(PostProcessContext.RHICmdList).AdjustGBufferRefCount(PostProcessContext.RHICmdList, 1); } return PostProcessContext.Graph.RegisterPass(new(FMemStack::Get()) TRCPassForRDG( [MaterialInterface, Material, OverrideOutputFormat](FRenderingCompositePass* Pass, FRenderingCompositePassContext& InContext) { FRDGBuilder GraphBuilder(InContext.RHICmdList); FPostProcessMaterialInputs Inputs; Inputs.OutputFormat = OverrideOutputFormat; // Either finds the overridden frame buffer target or returns null. if (FRDGTextureRef OutputTexture = Pass->FindRDGTextureForOutput(GraphBuilder, ePId_Output0, TEXT("FrameBufferOverride"))) { Inputs.OverrideOutput.Texture = OutputTexture; Inputs.OverrideOutput.ViewRect = InContext.GetSceneColorDestRect(Pass); Inputs.OverrideOutput.LoadAction = InContext.View.IsFirstInFamily() ? ERenderTargetLoadAction::EClear : ERenderTargetLoadAction::ELoad; } for (uint32 InputIndex = 0; InputIndex < kPostProcessMaterialInputCountMax; ++InputIndex) { FRDGTextureRef InputTexture = Pass->CreateRDGTextureForOptionalInput(GraphBuilder, EPassInputId(InputIndex), TEXT("PostProcessInput")); /** * TODO: Propagate each texture viewport through the graph setup instead of guessing. This is wrong for * any scaled target (e.g. half resolution bloom). We deal with the upsample from TAAU explicitly here, * but it's a band-aid at best. The problem is that we rely too heavily on the ViewRect--in pixels--which * only applies to the primary screen resolution viewport. */ const FIntRect InputViewportRect = (InputIndex == 0) ? InContext.SceneColorViewRect : InContext.View.ViewRect; Inputs.SetInput((EPostProcessMaterialInput)InputIndex, FScreenPassTexture(InputTexture, InputViewportRect)); } Inputs.bFlipYAxis = ShouldMobilePassFlipVerticalAxis(Pass); if (TRefCountPtr CustomDepthTarget = FSceneRenderTargets::Get(InContext.RHICmdList).CustomDepth) { Inputs.CustomDepthTexture = GraphBuilder.RegisterExternalTexture(CustomDepthTarget, TEXT("CustomDepth")); } FScreenPassTexture Outputs = AddPostProcessMaterialPass(GraphBuilder, InContext.View, Inputs, MaterialInterface); Pass->ExtractRDGTextureForOutput(GraphBuilder, ePId_Output0, Outputs.Texture); GraphBuilder.Execute(); if (Material->NeedsGBuffer()) { FSceneRenderTargets::Get(InContext.RHICmdList).AdjustGBufferRefCount(InContext.RHICmdList, -1); } })); } FRenderingCompositeOutputRef AddPostProcessMaterialChain( FPostprocessContext& Context, EBlendableLocation Location, FRenderingCompositeOutputRef SeparateTranslucency, FRenderingCompositeOutputRef PreTonemapHDRColor, FRenderingCompositeOutputRef PostTonemapHDRColor, FRenderingCompositeOutputRef PreFlattenVelocity) { const FPostProcessMaterialChain MaterialChain = GetPostProcessMaterialChain(Context.View, Location); ERHIFeatureLevel::Type FeatureLevel = Context.View.GetFeatureLevel(); FRenderingCompositeOutputRef LastOutput = Context.FinalOutput; for (const UMaterialInterface* MaterialInterface : MaterialChain) { FRenderingCompositePass* Pass = AddPostProcessMaterialPass(Context, MaterialInterface); Pass->SetInput(EPassInputId(EPostProcessMaterialInput::SceneColor), LastOutput); Pass->SetInput(EPassInputId(EPostProcessMaterialInput::SeparateTranslucency), SeparateTranslucency); Pass->SetInput(EPassInputId(EPostProcessMaterialInput::PreTonemapHDRColor), PreTonemapHDRColor); Pass->SetInput(EPassInputId(EPostProcessMaterialInput::PostTonemapHDRColor), PostTonemapHDRColor); if (!FVelocityRendering::BasePassCanOutputVelocity(FeatureLevel)) { Pass->SetInput(EPassInputId(EPostProcessMaterialInput::Velocity), PreFlattenVelocity); } LastOutput = FRenderingCompositeOutputRef(Pass); } return LastOutput; } extern void AddDumpToColorArrayPass(FRDGBuilder& GraphBuilder, FScreenPassTexture Input, TArray* OutputColorArray); bool IsHighResolutionScreenshotMaskEnabled(const FViewInfo& View) { return View.Family->EngineShowFlags.HighResScreenshotMask || View.FinalPostProcessSettings.HighResScreenshotCaptureRegionMaterial; } FScreenPassTexture AddHighResolutionScreenshotMaskPass( FRDGBuilder& GraphBuilder, const FViewInfo& View, const FHighResolutionScreenshotMaskInputs& Inputs) { check(Inputs.Material || Inputs.MaskMaterial || Inputs.CaptureRegionMaterial); enum class EPass { Material, MaskMaterial, CaptureRegionMaterial, MAX }; const TCHAR* PassNames[] { TEXT("Material"), TEXT("MaskMaterial"), TEXT("CaptureRegionMaterial") }; static_assert(UE_ARRAY_COUNT(PassNames) == static_cast(EPass::MAX), "Pass names array doesn't match pass enum"); TOverridePassSequence PassSequence(Inputs.OverrideOutput); PassSequence.SetEnabled(EPass::Material, Inputs.Material != nullptr); PassSequence.SetEnabled(EPass::MaskMaterial, Inputs.MaskMaterial != nullptr && GIsHighResScreenshot); PassSequence.SetEnabled(EPass::CaptureRegionMaterial, Inputs.CaptureRegionMaterial != nullptr); PassSequence.Finalize(); FScreenPassTexture Output = Inputs.SceneColor; if (PassSequence.IsEnabled(EPass::Material)) { FPostProcessMaterialInputs PassInputs; PassSequence.AcceptOverrideIfLastPass(EPass::Material, PassInputs.OverrideOutput); PassInputs.SetInput(EPostProcessMaterialInput::SceneColor, Output); Output = AddPostProcessMaterialPass(GraphBuilder, View, PassInputs, Inputs.Material); } if (PassSequence.IsEnabled(EPass::MaskMaterial)) { FPostProcessMaterialInputs PassInputs; PassSequence.AcceptOverrideIfLastPass(EPass::MaskMaterial, PassInputs.OverrideOutput); PassInputs.SetInput(EPostProcessMaterialInput::SceneColor, Output); Output = AddPostProcessMaterialPass(GraphBuilder, View, PassInputs, Inputs.MaskMaterial); AddDumpToColorArrayPass(GraphBuilder, Output, FScreenshotRequest::GetHighresScreenshotMaskColorArray()); } if (PassSequence.IsEnabled(EPass::CaptureRegionMaterial)) { FPostProcessMaterialInputs PassInputs; PassSequence.AcceptOverrideIfLastPass(EPass::CaptureRegionMaterial, PassInputs.OverrideOutput); PassInputs.SetInput(EPostProcessMaterialInput::SceneColor, Output); Output = AddPostProcessMaterialPass(GraphBuilder, View, PassInputs, Inputs.CaptureRegionMaterial); } return Output; } void AddHighResScreenshotMask(FPostprocessContext& Context) { FRenderingCompositePass* Pass = Context.Graph.RegisterPass( new(FMemStack::Get()) TRCPassForRDG<1, 1>( [](FRenderingCompositePass* InPass, FRenderingCompositePassContext& InContext) { FRDGBuilder GraphBuilder(InContext.RHICmdList); FHighResolutionScreenshotMaskInputs PassInputs; PassInputs.SceneColor.Texture = InPass->CreateRDGTextureForRequiredInput(GraphBuilder, ePId_Input0, TEXT("SceneColor")); PassInputs.SceneColor.ViewRect = InContext.SceneColorViewRect; if (FRDGTextureRef OverrideOutputTexture = InPass->FindRDGTextureForOutput(GraphBuilder, ePId_Output0, TEXT("FrameBuffer"))) { PassInputs.OverrideOutput.Texture = OverrideOutputTexture; PassInputs.OverrideOutput.ViewRect = InContext.GetSceneColorDestRect(InPass); PassInputs.OverrideOutput.LoadAction = InContext.View.IsFirstInFamily() ? ERenderTargetLoadAction::EClear : ERenderTargetLoadAction::ELoad; } FScreenPassTexture PassOutput = AddHighResolutionScreenshotMaskPass(GraphBuilder, InContext.View, PassInputs); InPass->ExtractRDGTextureForOutput(GraphBuilder, ePId_Output0, PassOutput.Texture); GraphBuilder.Execute(); })); Pass->SetInput(ePId_Input0, Context.FinalOutput); Context.FinalOutput = Pass; }