// Copyright Epic Games, Inc. All Rights Reserved. #include "SlatePostProcessor.h" #include "SlateShaders.h" #include "RendererUtils.h" #include "Interfaces/SlateRHIRenderingPolicyInterface.h" ////////////////////////////////////////////////////////////////////////// float GetSlateHDRUILevel() { static auto CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.HDR.UI.Level")); return CVar ? CVar->GetFloat() : 1.0f; } float GetSlateHDRUILuminance() { static auto CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.HDR.UI.Luminance")); return CVar ? CVar->GetFloat() : 300.0f; } ETextureCreateFlags GetSlateTransientRenderTargetFlags() { return ETextureCreateFlags::RenderTargetable | ETextureCreateFlags::ShaderResource | ETextureCreateFlags::FastVRAM // Avoid fast clear metadata when this flag is set, since we'd otherwise have to clear transient render targets instead of discard. #if PLATFORM_REQUIRES_TYPELESS_RESOURCE_DISCARD_WORKAROUND | ETextureCreateFlags::NoFastClear #endif ; } ETextureCreateFlags GetSlateTransientDepthStencilFlags() { return ETextureCreateFlags::DepthStencilTargetable | ETextureCreateFlags::FastVRAM; } ////////////////////////////////////////////////////////////////////////// // Pixel shader to composite UI over HDR buffer prior to doing a blur class FCompositeHDRForBlurPS : public FGlobalShader { public: DECLARE_GLOBAL_SHADER(FCompositeHDRForBlurPS); SHADER_USE_PARAMETER_STRUCT(FCompositeHDRForBlurPS, FGlobalShader); class FUseSRGBEncoding : SHADER_PERMUTATION_BOOL("SCRGB_ENCODING"); using FPermutationDomain = TShaderPermutationDomain; BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, UITexture) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, UIWriteMaskTexture) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SceneTexture) SHADER_PARAMETER_SAMPLER(SamplerState, UISampler) SHADER_PARAMETER(float, UILevel) SHADER_PARAMETER(float, UILuminance) SHADER_PARAMETER(FVector2f, UITextureSize) RENDER_TARGET_BINDING_SLOTS() END_SHADER_PARAMETER_STRUCT() static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5) && (RHISupportsGeometryShaders(Parameters.Platform) || RHISupportsVertexShaderLayer(Parameters.Platform)); } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("COMPOSITE_UI_FOR_BLUR_PS"), 1); } }; IMPLEMENT_GLOBAL_SHADER(FCompositeHDRForBlurPS, "/Engine/Private/CompositeUIPixelShader.usf", "CompositeUIForBlur", SF_Pixel); struct FSlateCompositeHDRForBlurPassInputs { FIntRect InputRect; FRDGTexture* InputCompositeTexture; FRDGTexture* InputTexture; FIntPoint OutputExtent; }; FScreenPassTexture AddSlateCompositeHDRForBlurPass(FRDGBuilder& GraphBuilder, const FSlateCompositeHDRForBlurPassInputs& Inputs) { const ERHIFeatureLevel::Type FeatureLevel = GMaxRHIFeatureLevel; FGlobalShaderMap* ShaderMap = GetGlobalShaderMap(FeatureLevel); FRDGTexture* UIWriteMaskTexture = nullptr; if (RHISupportsRenderTargetWriteMask(GMaxRHIShaderPlatform)) { FRenderTargetWriteMask::Decode(GraphBuilder, ShaderMap, MakeArrayView({ Inputs.InputCompositeTexture }), UIWriteMaskTexture, TexCreate_None, TEXT("UIRTWriteMask")); } FScreenPassRenderTarget Output( GraphBuilder.CreateTexture( FRDGTextureDesc::Create2D(Inputs.OutputExtent, PF_FloatR11G11B10, FClearValueBinding::Black, GetSlateTransientRenderTargetFlags()), TEXT("CompositeHDRUI")), ERenderTargetLoadAction::ENoAction); const FScreenPassTextureViewport InputViewport = FScreenPassTextureViewport(Inputs.InputCompositeTexture, Inputs.InputRect); const FScreenPassTextureViewport OutputViewport = FScreenPassTextureViewport(Output); FCompositeHDRForBlurPS::FPermutationDomain PermutationVector; PermutationVector.Set(Inputs.InputTexture->Desc.Format == PF_FloatRGBA); FCompositeHDRForBlurPS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->RenderTargets[0] = Output.GetRenderTargetBinding(); PassParameters->SceneTexture = Inputs.InputTexture; PassParameters->UITexture = Inputs.InputCompositeTexture; PassParameters->UIWriteMaskTexture = UIWriteMaskTexture; PassParameters->UISampler = TStaticSamplerState::GetRHI(); PassParameters->UITextureSize = InputViewport.Extent; PassParameters->UILevel = GetSlateHDRUILevel(); PassParameters->UILuminance = GetSlateHDRUILuminance(); TShaderMapRef PixelShader(ShaderMap, PermutationVector); AddDrawScreenPass(GraphBuilder, RDG_EVENT_NAME("CompositeHDR"), FeatureLevel, OutputViewport, InputViewport, PixelShader, PassParameters); return Output; } ////////////////////////////////////////////////////////////////////////// class FSlatePostProcessDownsamplePS : public FGlobalShader { public: DECLARE_GLOBAL_SHADER(FSlatePostProcessDownsamplePS); SHADER_USE_PARAMETER_STRUCT(FSlatePostProcessDownsamplePS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, ElementTexture) SHADER_PARAMETER_SAMPLER(SamplerState, ElementTextureSampler) SHADER_PARAMETER(FVector4f, ShaderParams) SHADER_PARAMETER(FVector4f, UVBounds) RENDER_TARGET_BINDING_SLOTS() END_SHADER_PARAMETER_STRUCT() }; IMPLEMENT_GLOBAL_SHADER(FSlatePostProcessDownsamplePS, "/Engine/Private/SlatePostProcessPixelShader.usf", "DownsampleMain", SF_Pixel); struct FSlatePostProcessDownsamplePassInputs { FScreenPassTexture InputTexture; FIntPoint OutputExtent; }; FScreenPassTexture AddSlatePostProcessDownsamplePass(FRDGBuilder& GraphBuilder, const FSlatePostProcessDownsamplePassInputs& Inputs) { const ERHIFeatureLevel::Type FeatureLevel = GMaxRHIFeatureLevel; FGlobalShaderMap* ShaderMap = GetGlobalShaderMap(FeatureLevel); TShaderMapRef PixelShader(ShaderMap); const FScreenPassRenderTarget Output( GraphBuilder.CreateTexture(FRDGTextureDesc::Create2D(Inputs.OutputExtent, Inputs.InputTexture.Texture->Desc.Format, FClearValueBinding::None, GetSlateTransientRenderTargetFlags()), TEXT("DownsampleUI")), ERenderTargetLoadAction::ENoAction); const FScreenPassTextureViewport InputViewport(Inputs.InputTexture); const FScreenPassTextureViewportParameters InputParameters = GetScreenPassTextureViewportParameters(InputViewport); const FScreenPassTextureViewport OutputViewport(Output); FSlatePostProcessDownsamplePS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->RenderTargets[0] = Output.GetRenderTargetBinding(); PassParameters->ElementTexture = Inputs.InputTexture.Texture; PassParameters->ElementTextureSampler = TStaticSamplerState::GetRHI(); PassParameters->ShaderParams = FVector4f(InputParameters.ExtentInverse.X, InputParameters.ExtentInverse.Y, 0.0f, 0.0f); PassParameters->UVBounds = FVector4f(InputParameters.UVViewportBilinearMin, InputParameters.UVViewportBilinearMax); AddDrawScreenPass(GraphBuilder, RDG_EVENT_NAME("DownsampleUI"), FeatureLevel, OutputViewport, InputViewport, PixelShader, PassParameters); return Output; } ////////////////////////////////////////////////////////////////////////// enum class ESlatePostProcessUpsampleOutputFormat { SDR = 0, HDR_SCRGB, HDR_PQ10, MAX }; class FSlatePostProcessUpsamplePS : public FGlobalShader { public: DECLARE_GLOBAL_SHADER(FSlatePostProcessUpsamplePS); SHADER_USE_PARAMETER_STRUCT(FSlatePostProcessUpsamplePS, FGlobalShader); class FUpsampleOutputFormat : SHADER_PERMUTATION_ENUM_CLASS("UPSAMPLE_OUTPUT_FORMAT", ESlatePostProcessUpsampleOutputFormat); using FPermutationDomain = TShaderPermutationDomain; BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, ElementTexture) SHADER_PARAMETER_SAMPLER(SamplerState, ElementTextureSampler) SHADER_PARAMETER(FVector4f, ShaderParams) SHADER_PARAMETER(FVector4f, ShaderParams2) RENDER_TARGET_BINDING_SLOTS() END_SHADER_PARAMETER_STRUCT() }; IMPLEMENT_GLOBAL_SHADER(FSlatePostProcessUpsamplePS, "/Engine/Private/SlatePostProcessPixelShader.usf", "UpsampleMain", SF_Pixel); struct FSlatePostProcessUpsampleInputs { FScreenPassTexture InputTexture; FRDGTexture* OutputTextureToClear = nullptr; FRDGTexture* OutputTexture = nullptr; ERenderTargetLoadAction OutputLoadAction = ERenderTargetLoadAction::ELoad; const FSlateClippingOp* ClippingOp = nullptr; const FDepthStencilBinding* ClippingStencilBinding = nullptr; FIntRect ClippingElementsViewRect; FIntRect OutputRect; FVector4f CornerRadius = FVector4f::Zero(); }; void AddSlatePostProcessUpsamplePass(FRDGBuilder& GraphBuilder, const FSlatePostProcessUpsampleInputs& Inputs) { FSlatePostProcessUpsamplePS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->RenderTargets[0] = FRenderTargetBinding(Inputs.OutputTexture, Inputs.OutputLoadAction); if (Inputs.ClippingStencilBinding) { PassParameters->RenderTargets.DepthStencil = *Inputs.ClippingStencilBinding; } ESlatePostProcessUpsampleOutputFormat OutputFormat = ESlatePostProcessUpsampleOutputFormat::SDR; if (Inputs.OutputTextureToClear) { OutputFormat = Inputs.OutputTexture->Desc.Format == PF_FloatRGBA ? ESlatePostProcessUpsampleOutputFormat::HDR_SCRGB : ESlatePostProcessUpsampleOutputFormat::HDR_PQ10; PassParameters->RenderTargets[1] = FRenderTargetBinding(Inputs.OutputTextureToClear, ERenderTargetLoadAction::ELoad); } FSlatePostProcessUpsamplePS::FPermutationDomain PermutationVector; PermutationVector.Set(OutputFormat); const ERHIFeatureLevel::Type FeatureLevel = GMaxRHIFeatureLevel; FGlobalShaderMap* ShaderMap = GetGlobalShaderMap(FeatureLevel); TShaderMapRef VertexShader(ShaderMap); TShaderMapRef PixelShader(ShaderMap, PermutationVector); const FScreenPassTextureViewport InputViewport(Inputs.InputTexture); const FScreenPassTextureViewport OutputViewport(Inputs.OutputTexture, Inputs.OutputRect); const FScreenPassTextureViewportParameters InputParameters = GetScreenPassTextureViewportParameters(InputViewport); PassParameters->ElementTexture = Inputs.InputTexture.Texture; PassParameters->ElementTextureSampler = Inputs.InputTexture.ViewRect == Inputs.OutputRect ? TStaticSamplerState::GetRHI() : TStaticSamplerState::GetRHI(); PassParameters->ShaderParams = FVector4f(InputParameters.ViewportSize, InputParameters.UVViewportSize); PassParameters->ShaderParams2 = Inputs.CornerRadius; FRHIBlendState* BlendState = Inputs.CornerRadius == FVector4f::Zero() ? TStaticBlendState<>::GetRHI() : TStaticBlendState::GetRHI(); FScreenPassPipelineState PipelineState(VertexShader, PixelShader, BlendState); GetSlateClippingPipelineState(Inputs.ClippingOp, PipelineState.DepthStencilState, PipelineState.StencilRef); GraphBuilder.AddPass( RDG_EVENT_NAME("Upsample"), PassParameters, ERDGPassFlags::Raster, [OutputViewport, InputViewport, ClippingElementsViewRect = Inputs.ClippingElementsViewRect, PipelineState, PixelShader, ClippingOp = Inputs.ClippingOp, PassParameters](FRDGAsyncTask, FRHICommandList& RHICmdList) { if (ClippingOp && ClippingOp->Method == EClippingMethod::Stencil) { // Stencil clipping quads have their own viewport. RHICmdList.SetViewport(ClippingElementsViewRect.Min.X, ClippingElementsViewRect.Min.Y, 0.0f, ClippingElementsViewRect.Max.X, ClippingElementsViewRect.Max.Y, 1.0f); // Stencil clipping will issue its own draw calls. SetSlateClipping(RHICmdList, ClippingOp, ClippingElementsViewRect); } RHICmdList.SetViewport(OutputViewport.Rect.Min.X, OutputViewport.Rect.Min.Y, 0.0f, OutputViewport.Rect.Max.X, OutputViewport.Rect.Max.Y, 1.0f); if (ClippingOp && ClippingOp->Method == EClippingMethod::Scissor) { SetSlateClipping(RHICmdList, ClippingOp, ClippingElementsViewRect); } SetScreenPassPipelineState(RHICmdList, PipelineState); SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), *PassParameters); DrawScreenPass_PostSetup(RHICmdList, FScreenPassViewInfo(), OutputViewport, InputViewport, PipelineState, EScreenPassDrawFlags::None); }); } ////////////////////////////////////////////////////////////////////////// class FSlatePostProcessBlurPS : public FGlobalShader { public: static const int32 MAX_BLUR_SAMPLES = 127 / 2; DECLARE_GLOBAL_SHADER(FSlatePostProcessBlurPS); SHADER_USE_PARAMETER_STRUCT(FSlatePostProcessBlurPS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, ElementTexture) SHADER_PARAMETER_SAMPLER(SamplerState, ElementTextureSampler) SHADER_PARAMETER_ARRAY(FVector4f, WeightAndOffsets, [MAX_BLUR_SAMPLES]) SHADER_PARAMETER(uint32, SampleCount) SHADER_PARAMETER(FVector4f, BufferSizeAndDirection) SHADER_PARAMETER(FVector4f, UVBounds) RENDER_TARGET_BINDING_SLOTS() END_SHADER_PARAMETER_STRUCT() }; IMPLEMENT_GLOBAL_SHADER(FSlatePostProcessBlurPS, "/Engine/Private/SlatePostProcessPixelShader.usf", "GaussianBlurMain", SF_Pixel); void AddSlatePostProcessBlurPass(FRDGBuilder& GraphBuilder, const FSlatePostProcessBlurPassInputs& Inputs) { RDG_EVENT_SCOPE(GraphBuilder, "GaussianBlur"); const auto GetWeight = [](float Dist, float Strength) { float Strength2 = Strength*Strength; return (1.0f / FMath::Sqrt(2 * PI*Strength2))*FMath::Exp(-(Dist*Dist) / (2 * Strength2)); }; const auto GetWeightsAndOffset = [GetWeight](float Dist, float Sigma) { float Offset1 = Dist; float Weight1 = GetWeight(Offset1, Sigma); float Offset2 = Dist + 1; float Weight2 = GetWeight(Offset2, Sigma); float TotalWeight = Weight1 + Weight2; float Offset = 0; if (TotalWeight > 0) { Offset = (Weight1 * Offset1 + Weight2 * Offset2) / TotalWeight; } return FVector2f(TotalWeight, Offset); }; const int32 SampleCount = FMath::DivideAndRoundUp(Inputs.KernelSize, 2u); // We need half of the sample count array because we're packing two samples into one float; TArray WeightsAndOffsets; WeightsAndOffsets.AddUninitialized(SampleCount % 2 == 0 ? SampleCount / 2 : SampleCount / 2 + 1); WeightsAndOffsets[0] = FVector4f(FVector2f(GetWeight(0, Inputs.Strength), 0), GetWeightsAndOffset(1, Inputs.Strength) ); for (uint32 X = 3, SampleIndex = 1; X < Inputs.KernelSize; X += 4, SampleIndex++) { WeightsAndOffsets[SampleIndex] = FVector4f(GetWeightsAndOffset((float)X, Inputs.Strength), GetWeightsAndOffset((float)(X + 2), Inputs.Strength)); } FScreenPassTextureViewport OutputTextureViewport(Inputs.InputRect.Size()); const EPixelFormat InputPixelFormat = Inputs.InputTexture->Desc.Format; // Defaults to the input UI texture unless a downsample / composite pass is needed. FScreenPassTexture BlurInputTexture(Inputs.InputTexture, Inputs.InputRect); // Need to composite the HDR scene texture with a separate SDR UI texture (which also does a downsample). if (Inputs.SDRCompositeUITexture) { FSlateCompositeHDRForBlurPassInputs CompositeInputs; CompositeInputs.InputRect = Inputs.InputRect; CompositeInputs.InputTexture = Inputs.InputTexture; CompositeInputs.InputCompositeTexture = Inputs.SDRCompositeUITexture; CompositeInputs.OutputExtent = OutputTextureViewport.Extent; BlurInputTexture = AddSlateCompositeHDRForBlurPass(GraphBuilder, CompositeInputs); } // Need to do an explicit downsample pass. else if (Inputs.DownsampleAmount > 0) { OutputTextureViewport = FScreenPassTextureViewport(GetDownscaledExtent(Inputs.InputRect.Size(), Inputs.DownsampleAmount)); FSlatePostProcessDownsamplePassInputs DownsampleInputs; DownsampleInputs.InputTexture = BlurInputTexture; DownsampleInputs.OutputExtent = OutputTextureViewport.Extent; BlurInputTexture = AddSlatePostProcessDownsamplePass(GraphBuilder, DownsampleInputs); } const ERHIFeatureLevel::Type FeatureLevel = GMaxRHIFeatureLevel; FGlobalShaderMap* ShaderMap = GetGlobalShaderMap(FeatureLevel); TShaderMapRef PixelShader(ShaderMap); FScreenPassRenderTarget BlurOutputTexture( GraphBuilder.CreateTexture(FRDGTextureDesc::Create2D(OutputTextureViewport.Extent, InputPixelFormat, FClearValueBinding::None, GetSlateTransientRenderTargetFlags()), TEXT("SlateBlurHorizontalTexture")), ERenderTargetLoadAction::ENoAction); FSlatePostProcessBlurPS::FParameters* PassParameters = nullptr; { const FScreenPassTextureViewport BlurInputViewport(BlurInputTexture); const FScreenPassTextureViewportParameters BlurInputParameters = GetScreenPassTextureViewportParameters(BlurInputViewport); PassParameters = GraphBuilder.AllocParameters(); PassParameters->RenderTargets[0] = BlurOutputTexture.GetRenderTargetBinding(); PassParameters->ElementTexture = BlurInputTexture.Texture; PassParameters->ElementTextureSampler = TStaticSamplerState::GetRHI(); PassParameters->SampleCount = SampleCount; PassParameters->BufferSizeAndDirection = FVector4f(BlurInputParameters.ExtentInverse, FVector2f(1.0f, 0.0f)); PassParameters->UVBounds = FVector4f(BlurInputParameters.UVViewportBilinearMin, BlurInputParameters.UVViewportBilinearMax); check(PassParameters->WeightAndOffsets.Num() * sizeof(PassParameters->WeightAndOffsets[0]) >= WeightsAndOffsets.Num() * sizeof(WeightsAndOffsets[0])); FPlatformMemory::Memcpy(PassParameters->WeightAndOffsets.GetData(), WeightsAndOffsets.GetData(), WeightsAndOffsets.Num() * sizeof(WeightsAndOffsets[0])); AddDrawScreenPass(GraphBuilder, RDG_EVENT_NAME("Horizontal"), FeatureLevel, FScreenPassTextureViewport(BlurOutputTexture), BlurInputViewport, PixelShader, PassParameters); } BlurInputTexture = BlurOutputTexture; BlurOutputTexture = FScreenPassRenderTarget( GraphBuilder.CreateTexture(FRDGTextureDesc::Create2D(OutputTextureViewport.Extent, InputPixelFormat, FClearValueBinding::None, GetSlateTransientRenderTargetFlags()), TEXT("SlateBlurVerticalTexture")), ERenderTargetLoadAction::ENoAction); { const FScreenPassTextureViewport BlurInputViewport(BlurInputTexture); const FScreenPassTextureViewportParameters BlurInputParameters = GetScreenPassTextureViewportParameters(BlurInputViewport); PassParameters = GraphBuilder.AllocParameters(); PassParameters->RenderTargets[0] = BlurOutputTexture.GetRenderTargetBinding(); PassParameters->ElementTexture = BlurInputTexture.Texture; PassParameters->ElementTextureSampler = TStaticSamplerState::GetRHI(); PassParameters->SampleCount = SampleCount; PassParameters->BufferSizeAndDirection = FVector4f(BlurInputParameters.ExtentInverse, FVector2f(0.0f, 1.0f)); PassParameters->UVBounds = FVector4f(BlurInputParameters.UVViewportBilinearMin, BlurInputParameters.UVViewportBilinearMax); check(PassParameters->WeightAndOffsets.Num() * sizeof(PassParameters->WeightAndOffsets[0]) >= WeightsAndOffsets.Num() * sizeof(WeightsAndOffsets[0])); FPlatformMemory::Memcpy(PassParameters->WeightAndOffsets.GetData(), WeightsAndOffsets.GetData(), WeightsAndOffsets.Num() * sizeof(WeightsAndOffsets[0])); AddDrawScreenPass(GraphBuilder, RDG_EVENT_NAME("Vertical"), FeatureLevel, FScreenPassTextureViewport(BlurOutputTexture), BlurInputViewport, PixelShader, PassParameters); } FSlatePostProcessUpsampleInputs UpsampleInputs; UpsampleInputs.InputTexture = BlurOutputTexture; UpsampleInputs.OutputTextureToClear = Inputs.SDRCompositeUITexture; UpsampleInputs.OutputTexture = Inputs.OutputTexture; UpsampleInputs.OutputRect = Inputs.OutputRect; UpsampleInputs.ClippingOp = Inputs.ClippingOp; UpsampleInputs.ClippingStencilBinding = Inputs.ClippingStencilBinding; UpsampleInputs.ClippingElementsViewRect = Inputs.ClippingElementsViewRect; UpsampleInputs.CornerRadius = Inputs.CornerRadius; AddSlatePostProcessUpsamplePass(GraphBuilder, UpsampleInputs); } void AddSlatePostProcessBlurPass(FRDGBuilder& GraphBuilder, const FSlatePostProcessSimpleBlurPassInputs& SimpleInputs) { const int32 MinKernelSize = 3; const int32 MaxKernelSize = 255; const int32 Downsample2Threshold = 9; const int32 Downsample4Threshold = 64; const float StrengthToKernelSize = 3.0f; const float MinStrength = 0.5f; float Strength = FMath::Max(MinStrength, SimpleInputs.Strength); int32 KernelSize = FMath::RoundToInt(Strength * StrengthToKernelSize); int32 DownsampleAmount = 0; if (KernelSize > Downsample2Threshold) { DownsampleAmount = KernelSize >= Downsample4Threshold ? 4 : 2; KernelSize /= DownsampleAmount; } // Kernel sizes must be odd if (KernelSize % 2 == 0) { ++KernelSize; } if (DownsampleAmount > 0) { Strength /= DownsampleAmount; } KernelSize = FMath::Clamp(KernelSize, MinKernelSize, MaxKernelSize); FSlatePostProcessBlurPassInputs Inputs; Inputs.InputTexture = SimpleInputs.InputTexture.Texture; Inputs.InputRect = SimpleInputs.InputTexture.ViewRect; Inputs.OutputTexture = SimpleInputs.OutputTexture.Texture; Inputs.OutputRect = SimpleInputs.OutputTexture.ViewRect; Inputs.KernelSize = KernelSize; Inputs.Strength = Strength; Inputs.DownsampleAmount = DownsampleAmount; AddSlatePostProcessBlurPass(GraphBuilder, Inputs); } ////////////////////////////////////////////////////////////////////////// class FSlatePostProcessColorDeficiencyPS : public FGlobalShader { public: DECLARE_GLOBAL_SHADER(FSlatePostProcessColorDeficiencyPS); SHADER_USE_PARAMETER_STRUCT(FSlatePostProcessColorDeficiencyPS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, ElementTexture) SHADER_PARAMETER_SAMPLER(SamplerState, ElementTextureSampler) SHADER_PARAMETER(float, ColorVisionDeficiencyType) SHADER_PARAMETER(float, ColorVisionDeficiencySeverity) SHADER_PARAMETER(float, bCorrectDeficiency) SHADER_PARAMETER(float, bSimulateCorrectionWithDeficiency) RENDER_TARGET_BINDING_SLOTS() END_SHADER_PARAMETER_STRUCT() }; IMPLEMENT_GLOBAL_SHADER(FSlatePostProcessColorDeficiencyPS, "/Engine/Private/SlatePostProcessColorDeficiencyPixelShader.usf", "ColorDeficiencyMain", SF_Pixel); void AddSlatePostProcessColorDeficiencyPass(FRDGBuilder& GraphBuilder, const FSlatePostProcessColorDeficiencyPassInputs& Inputs) { const ERHIFeatureLevel::Type FeatureLevel = GMaxRHIFeatureLevel; FGlobalShaderMap* ShaderMap = GetGlobalShaderMap(FeatureLevel); TShaderMapRef PixelShader(ShaderMap); const FRDGTextureDesc& InputDesc = Inputs.InputTexture.Texture->Desc; const FScreenPassRenderTarget Output( GraphBuilder.CreateTexture(FRDGTextureDesc::Create2D(InputDesc.Extent, InputDesc.Format, FClearValueBinding::None, GetSlateTransientRenderTargetFlags()), TEXT("ColorDeficiency")), ERenderTargetLoadAction::ENoAction); FSlatePostProcessColorDeficiencyPS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->RenderTargets[0] = Output.GetRenderTargetBinding(); PassParameters->ElementTexture = Inputs.InputTexture.Texture; PassParameters->ElementTextureSampler = TStaticSamplerState::GetRHI(); PassParameters->ColorVisionDeficiencyType = (float)GSlateColorDeficiencyType; PassParameters->ColorVisionDeficiencySeverity = (float)GSlateColorDeficiencySeverity; PassParameters->bCorrectDeficiency = GSlateColorDeficiencyCorrection ? 1.0f : 0.0f; PassParameters->bSimulateCorrectionWithDeficiency = GSlateShowColorDeficiencyCorrectionWithDeficiency ? 1.0f : 0.0f; const FScreenPassTextureViewport Viewport(Output); AddDrawScreenPass(GraphBuilder, RDG_EVENT_NAME("ColorDeficiency"), FeatureLevel, Viewport, Viewport, PixelShader, PassParameters); FSlatePostProcessUpsampleInputs UpsampleInputs; UpsampleInputs.InputTexture = Output; UpsampleInputs.OutputTexture = Inputs.OutputTexture.Texture; UpsampleInputs.OutputRect = Inputs.OutputTexture.ViewRect; AddSlatePostProcessUpsamplePass(GraphBuilder, UpsampleInputs); }