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

1230 lines
56 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
PostProcessAmbientOcclusionMobile.cpp
=============================================================================*/
#include "PostProcess/PostProcessAmbientOcclusionMobile.h"
#include "CompositionLighting/PostProcessAmbientOcclusion.h"
#include "DataDrivenShaderPlatformInfo.h"
#include "ShaderParameterStruct.h"
#include "SceneRendering.h"
#include "RenderTargetPool.h"
#include "PostProcess/SceneFilterRendering.h"
#include "PostProcess/SceneRenderTargets.h"
#include "SystemTextures.h"
#include "ScreenPass.h"
#include "ScenePrivate.h"
#include "SceneTextureParameters.h"
#include "SceneRenderTargetParameters.h"
#include "ClearQuad.h"
#include "PixelShaderUtils.h"
static TAutoConsoleVariable<int32> CVarMobileAmbientOcclusion(
TEXT("r.Mobile.AmbientOcclusion"),
0,
TEXT("Caution: An extra sampler will be occupied in mobile base pass pixel shader after enable the mobile ambient occlusion.\n")
TEXT("0: Disable Ambient Occlusion on mobile platform. [default]\n")
TEXT("1: Enable Ambient Occlusion on mobile platform.\n"),
ECVF_ReadOnly | ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<int32> CVarMobileAmbientOcclusionTechnique(
TEXT("r.Mobile.AmbientOcclusionTechnique"),
0,
TEXT("0: GTAO (default).\n")
TEXT("1: SSAO.\n"),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<int32> CVarMobileGTAOPreIntegratedTextureType(
TEXT("r.Mobile.GTAOPreIntegratedTextureType"),
2,
TEXT("0: No Texture.\n")
TEXT("1: Texture2D LUT.\n")
TEXT("2: Volume LUT(Default)."),
ECVF_ReadOnly);
static TAutoConsoleVariable<int32> CVarMobileAmbientOcclusionQuality(
TEXT("r.Mobile.AmbientOcclusionQuality"),
1,
TEXT("The quality of screen space ambient occlusion on mobile platform.\n")
TEXT("0: Disabled.\n")
TEXT("1: Low.(Default)\n")
TEXT("2: Medium.\n")
TEXT("3: High.\n"),
ECVF_Scalability | ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<int32> CVarMobileAmbientOcclusionShaderType(
TEXT("r.Mobile.AmbientOcclusionShaderType"),
2,
TEXT("0: ComputeShader.\n")
TEXT("1: Separate ComputeShader.\n")
TEXT("2: PixelShader.\n"),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<int32> CVarMobileAmbientOcclusionCompression(
TEXT("r.Mobile.AmbientOcclusionCompression"),
1,
TEXT("0: Disable framebuffer compression for AO texture.\n")
TEXT("1: Enable framebuffer compression for AO texture if supported (Default).\n"),
ECVF_RenderThreadSafe
);
enum class EMobileAmbientOcclusionShaderType
{
ComputeShader,
SeparateComputeShader,
PixelShader
};
static EMobileAmbientOcclusionShaderType GetCVarMobileAmbientOcclusionShaderTypeOnRenderThread()
{
int ShaderType = FMath::Clamp(CVarMobileAmbientOcclusionShaderType.GetValueOnRenderThread(), 0, 2);
if (ShaderType == 0 && GetMaxWorkGroupInvocations() < 1024)
{
ShaderType = 1;
}
return static_cast<EMobileAmbientOcclusionShaderType>(ShaderType);
}
static bool IsComputeShaderType(EMobileAmbientOcclusionShaderType ShaderType)
{
return ShaderType != EMobileAmbientOcclusionShaderType::PixelShader;
}
static bool IsTwoPassShaderType(EMobileAmbientOcclusionShaderType ShaderType)
{
return ShaderType != EMobileAmbientOcclusionShaderType::ComputeShader;
}
static TAutoConsoleVariable<int32> CVarMobileAmbientOcclusionDepthBoundsTest(
TEXT("r.Mobile.AmbientOcclusionDepthBoundsTest"),
1,
TEXT("Whether to use depth bounds test to cull distant pixels during AO pass. This option is only valid when pixel shader path is used"),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarMobileSSAOHalfResolution(
TEXT("r.Mobile.SSAOHalfResolution"),
0,
TEXT("Whether to calculate SSAO at half resolution.\n")
TEXT("0: Disabled.\n")
TEXT("1: Half Resolution with bilinear upsample\n")
TEXT("2: Half Resolution with 4 tap bilateral upsample\n")
TEXT("3: Half Resolution with 9 tap bilateral upsample\n"),
ECVF_RenderThreadSafe);
// --------------------------------------------------------------------------------------------------------------------
DECLARE_GPU_STAT_NAMED(MobileSSAO, TEXT("SSAO"));
// --------------------------------------------------------------------------------------------------------------------
bool IsUsingMobileAmbientOcclusion(EShaderPlatform ShaderPlatform)
{
static const auto MobileAmbientOcclusionQualityCVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.Mobile.AmbientOcclusionQuality"));
return IsMobileAmbientOcclusionEnabled(ShaderPlatform) && MobileAmbientOcclusionQualityCVar->GetValueOnAnyThread() > 0;
}
// --------------------------------------------------------------------------------------------------------------------
class FGTAOMobile_HorizonSearchIntegral : public FGlobalShader
{
public:
class FLUTTextureTypeDim : SHADER_PERMUTATION_INT("PREINTEGRATED_LUT_TYPE", 3);
class FShaderQualityDim : SHADER_PERMUTATION_INT("SHADER_QUALITY", 3);
using FCommonPermutationDomain = TShaderPermutationDomain<
FLUTTextureTypeDim,
FShaderQualityDim>;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
SHADER_PARAMETER_EX(FVector4f, ViewRectMin, EShaderPrecisionModifier::Half)
SHADER_PARAMETER_EX(FVector4f, DepthBufferSizeAndInvSize, EShaderPrecisionModifier::Half)
SHADER_PARAMETER_EX(FVector4f, BufferSizeAndInvSize, EShaderPrecisionModifier::Half)
SHADER_PARAMETER_EX(FVector4f, ViewSizeAndInvSize, EShaderPrecisionModifier::Half)
SHADER_PARAMETER(FVector4f, FadeRadiusMulAdd_FadeDistance_AttenFactor)
SHADER_PARAMETER(FVector4f, WorldRadiusAdj_SinDeltaAngle_CosDeltaAngle_Thickness)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SceneDepthTexture)
SHADER_PARAMETER_SAMPLER(SamplerState, SceneDepthSampler)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, NormalTexture)
SHADER_PARAMETER_SAMPLER(SamplerState, NormalSampler)
SHADER_PARAMETER_TEXTURE(Texture2D, GTAOPreIntegrated2D)
SHADER_PARAMETER_TEXTURE(Texture3D, GTAOPreIntegrated3D)
SHADER_PARAMETER_SAMPLER(SamplerState, GTAOPreIntegratedSampler)
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters, const FCommonPermutationDomain& CommonPermutationVector)
{
auto LUTTextureType = CommonPermutationVector.Get<FLUTTextureTypeDim>();
int32 MobileGTAOPreIntegratedTextureType = CVarMobileGTAOPreIntegratedTextureType.GetValueOnAnyThread();
return IsMobileAmbientOcclusionEnabled(Parameters.Platform) && (MobileGTAOPreIntegratedTextureType == LUTTextureType);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("USE_NORMALBUFFER"), 0);
}
static FCommonPermutationDomain BuildPermutationVector(int32 LUTTextureType, int32 ShaderQuality)
{
FCommonPermutationDomain PermutationVector;
PermutationVector.Set<FLUTTextureTypeDim>(LUTTextureType);
PermutationVector.Set<FShaderQualityDim>(ShaderQuality);
return PermutationVector;
}
static void SetupShaderParameters(FParameters& ShaderParameters, FRDGBuilder& GraphBuilder, const FViewInfo& View, const FIntRect& ViewRect, const FIntPoint& DepthBufferSize, const FIntPoint& BufferSize, const FVector4f& FallOffStartEndScaleBias, const FVector4f& WorldRadiusAdjSinCosDeltaAngleThickness, FRDGTextureRef SceneDepthTexture)
{
const FFinalPostProcessSettings& Settings = View.FinalPostProcessSettings;
float FadeRadius = FMath::Max(1.0f, Settings.AmbientOcclusionFadeRadius);
float InvFadeRadius = 1.0f / FadeRadius;
ShaderParameters.View = View.ViewUniformBuffer;
ShaderParameters.ViewRectMin = FVector4f(ViewRect.Min.X, ViewRect.Min.Y, 0.0f, 0.0f);
ShaderParameters.DepthBufferSizeAndInvSize = FVector4f(DepthBufferSize.X, DepthBufferSize.Y, 1.0f / DepthBufferSize.X, 1.0f / DepthBufferSize.Y);
ShaderParameters.BufferSizeAndInvSize = FVector4f(BufferSize.X, BufferSize.Y, 1.0f / BufferSize.X, 1.0f / BufferSize.Y);
ShaderParameters.ViewSizeAndInvSize = FVector4f(ViewRect.Width(), ViewRect.Height(), 1.0f / ViewRect.Width(), 1.0f / ViewRect.Height());
ShaderParameters.FadeRadiusMulAdd_FadeDistance_AttenFactor = FVector4f(InvFadeRadius, -(Settings.AmbientOcclusionFadeDistance - FadeRadius) * InvFadeRadius, Settings.AmbientOcclusionFadeDistance, 2.0f / (FallOffStartEndScaleBias.Y * FallOffStartEndScaleBias.Y));
ShaderParameters.WorldRadiusAdj_SinDeltaAngle_CosDeltaAngle_Thickness = WorldRadiusAdjSinCosDeltaAngleThickness;
ShaderParameters.SceneDepthTexture = SceneDepthTexture;
ShaderParameters.SceneDepthSampler = TStaticSamplerState<SF_Point, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
if (GSystemTextures.GTAOPreIntegrated.IsValid())
{
ShaderParameters.GTAOPreIntegrated2D = GSystemTextures.GTAOPreIntegrated->GetRHI();
ShaderParameters.GTAOPreIntegrated3D = GSystemTextures.GTAOPreIntegrated->GetRHI();
ShaderParameters.GTAOPreIntegratedSampler = TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
}
}
FGTAOMobile_HorizonSearchIntegral() = default;
FGTAOMobile_HorizonSearchIntegral(const CompiledShaderInitializerType& Initializer)
: FGlobalShader(Initializer)
{}
};
class FGTAOMobile_HorizonSearchIntegralSpatialFilterCS : public FGTAOMobile_HorizonSearchIntegral
{
using Super = FGTAOMobile_HorizonSearchIntegral;
public:
// Changing these numbers requires PostProcessAmbientOcclusionMobile.usf to be recompiled.
// The maximum thread group is 512 on IOS A9 and A10 and the shared memory is 16K
static const uint32 ThreadGroupSizeX = 32;
static const uint32 ThreadGroupSizeY = 32;
// The number of texels on each axis processed by a single thread group.
static const FIntPoint TexelsPerThreadGroup;
DECLARE_GLOBAL_SHADER(FGTAOMobile_HorizonSearchIntegralSpatialFilterCS);
SHADER_USE_PARAMETER_STRUCT(FGTAOMobile_HorizonSearchIntegralSpatialFilterCS, Super);
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_INCLUDE(FGTAOMobile_HorizonSearchIntegral::FParameters, Common)
SHADER_PARAMETER_EX(FVector4f, Power_Intensity_ScreenPixelsToSearch, EShaderPrecisionModifier::Half)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<half4>, OutTexture)
END_SHADER_PARAMETER_STRUCT()
using FPermutationDomain = TShaderPermutationDomain<
Super::FCommonPermutationDomain>;
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
FPermutationDomain PermutationVector(Parameters.PermutationId);
return Super::ShouldCompilePermutation(Parameters, PermutationVector.Get<Super::FCommonPermutationDomain>());
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
Super::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("HORIZONSEARCH_INTEGRAL_SPATIALFILTER_COMPUTE_SHADER"), 1u);
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEX"), ThreadGroupSizeX);
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEY"), ThreadGroupSizeY);
}
static FPermutationDomain BuildPermutationVector(int32 LUTTextureType, int32 ShaderQuality)
{
FPermutationDomain PermutationVector;
PermutationVector.Set<Super::FCommonPermutationDomain>(Super::BuildPermutationVector(LUTTextureType, ShaderQuality));
return PermutationVector;
}
};
const FIntPoint FGTAOMobile_HorizonSearchIntegralSpatialFilterCS::TexelsPerThreadGroup(ThreadGroupSizeX, ThreadGroupSizeY);
IMPLEMENT_GLOBAL_SHADER(FGTAOMobile_HorizonSearchIntegralSpatialFilterCS, "/Engine/Private/PostProcessAmbientOcclusionMobile.usf", "GTAOHorizonSearchIntegralSpatialFilterCS", SF_Compute);
class FGTAOMobile_HorizonSearchIntegralCS : public FGTAOMobile_HorizonSearchIntegral
{
using Super = FGTAOMobile_HorizonSearchIntegral;
public:
// Changing these numbers requires PostProcessAmbientOcclusionMobile.usf to be recompiled.
// Use smaller thread group for low end devices
static const uint32 ThreadGroupSizeX = 16;
static const uint32 ThreadGroupSizeY = 8;
// The number of texels on each axis processed by a single thread group.
static const FIntPoint TexelsPerThreadGroup;
DECLARE_GLOBAL_SHADER(FGTAOMobile_HorizonSearchIntegralCS);
SHADER_USE_PARAMETER_STRUCT(FGTAOMobile_HorizonSearchIntegralCS, Super);
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_INCLUDE(FGTAOMobile_HorizonSearchIntegral::FParameters, Common)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<half4>, OutTexture)
END_SHADER_PARAMETER_STRUCT()
using FPermutationDomain = TShaderPermutationDomain<
Super::FCommonPermutationDomain>;
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
FPermutationDomain PermutationVector(Parameters.PermutationId);
return Super::ShouldCompilePermutation(Parameters, PermutationVector.Get<Super::FCommonPermutationDomain>());
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
Super::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("HORIZONSEARCH_INTEGRAL_COMPUTE_SHADER"), 1u);
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEX"), ThreadGroupSizeX);
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEY"), ThreadGroupSizeY);
}
static FPermutationDomain BuildPermutationVector(int32 LUTTextureType, int32 ShaderQuality)
{
FPermutationDomain PermutationVector;
PermutationVector.Set<Super::FCommonPermutationDomain>(Super::BuildPermutationVector(LUTTextureType, ShaderQuality));
return PermutationVector;
}
};
const FIntPoint FGTAOMobile_HorizonSearchIntegralCS::TexelsPerThreadGroup(ThreadGroupSizeX, ThreadGroupSizeY);
IMPLEMENT_GLOBAL_SHADER(FGTAOMobile_HorizonSearchIntegralCS, "/Engine/Private/PostProcessAmbientOcclusionMobile.usf", "GTAOHorizonSearchIntegralCS", SF_Compute);
class FGTAOMobile_SpatialFilter : public FGlobalShader
{
public:
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_EX(FVector4f, ViewRectMin, EShaderPrecisionModifier::Half)
SHADER_PARAMETER_EX(FVector4f, BufferSizeAndInvSize, EShaderPrecisionModifier::Half)
SHADER_PARAMETER_EX(FVector4f, ViewSizeAndInvSize, EShaderPrecisionModifier::Half)
SHADER_PARAMETER_EX(FVector4f, Power_Intensity_ScreenPixelsToSearch, EShaderPrecisionModifier::Half)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, AOInputTexture)
SHADER_PARAMETER_SAMPLER(SamplerState, AOInputSampler)
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
FPermutationDomain PermutationVector(Parameters.PermutationId);
return IsMobileAmbientOcclusionEnabled(Parameters.Platform);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
}
static void SetupShaderParameters(FParameters& ShaderParameters, const FViewInfo& View, const FIntRect& ViewRect, const FIntPoint& BufferSize, FRDGTextureRef HorizonSearchIntegralTexture)
{
const FFinalPostProcessSettings& Settings = View.FinalPostProcessSettings;
ShaderParameters.ViewRectMin = FVector4f(ViewRect.Min.X, ViewRect.Min.Y, 0.0f, 0.0f);
ShaderParameters.BufferSizeAndInvSize = FVector4f(BufferSize.X, BufferSize.Y, 1.0f / BufferSize.X, 1.0f / BufferSize.Y);
ShaderParameters.ViewSizeAndInvSize = FVector4f(ViewRect.Width(), ViewRect.Height(), 1.0f / ViewRect.Width(), 1.0f / ViewRect.Height());
ShaderParameters.Power_Intensity_ScreenPixelsToSearch = FVector4f(Settings.AmbientOcclusionPower * 0.5f, Settings.AmbientOcclusionIntensity, 0.0f, 0.0f);
ShaderParameters.AOInputTexture = HorizonSearchIntegralTexture;
ShaderParameters.AOInputSampler = TStaticSamplerState<SF_Point, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
}
FGTAOMobile_SpatialFilter() = default;
FGTAOMobile_SpatialFilter(const CompiledShaderInitializerType& Initializer)
: FGlobalShader(Initializer)
{}
};
class FGTAOMobile_SpatialFilterCS : public FGTAOMobile_SpatialFilter
{
using Super = FGTAOMobile_SpatialFilter;
public:
// Changing these numbers requires PostProcessAmbientOcclusionMobile.usf to be recompiled.
// Use smaller thread group for low end devices
static const uint32 ThreadGroupSizeX = 16;
static const uint32 ThreadGroupSizeY = 8;
// The number of texels on each axis processed by a single thread group.
static const FIntPoint TexelsPerThreadGroup;
DECLARE_GLOBAL_SHADER(FGTAOMobile_SpatialFilterCS);
SHADER_USE_PARAMETER_STRUCT(FGTAOMobile_SpatialFilterCS, Super);
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_INCLUDE(FGTAOMobile_SpatialFilter::FParameters, Common)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<half4>, OutTexture)
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return Super::ShouldCompilePermutation(Parameters);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
Super::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("SPATIALFILTER_COMPUTE_SHADER"), 1u);
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEX"), ThreadGroupSizeX);
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEY"), ThreadGroupSizeY);
}
};
const FIntPoint FGTAOMobile_SpatialFilterCS::TexelsPerThreadGroup(ThreadGroupSizeX, ThreadGroupSizeY);
IMPLEMENT_GLOBAL_SHADER(FGTAOMobile_SpatialFilterCS, "/Engine/Private/PostProcessAmbientOcclusionMobile.usf", "GTAOSpatialFilterCS", SF_Compute);
class FGTAOMobile_HorizonSearchIntegralPS : public FGTAOMobile_HorizonSearchIntegral
{
using Super = FGTAOMobile_HorizonSearchIntegral;
public:
DECLARE_GLOBAL_SHADER(FGTAOMobile_HorizonSearchIntegralPS);
SHADER_USE_PARAMETER_STRUCT(FGTAOMobile_HorizonSearchIntegralPS, Super);
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_INCLUDE(FGTAOMobile_HorizonSearchIntegral::FParameters, Common)
RENDER_TARGET_BINDING_SLOTS()
END_SHADER_PARAMETER_STRUCT()
using FPermutationDomain = TShaderPermutationDomain<
Super::FCommonPermutationDomain>;
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
FPermutationDomain PermutationVector(Parameters.PermutationId);
return Super::ShouldCompilePermutation(Parameters, PermutationVector.Get<Super::FCommonPermutationDomain>());
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
Super::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("HORIZONSEARCH_INTEGRAL_PIXEL_SHADER"), 1u);
OutEnvironment.SetDefine(TEXT("FORCE_DEPTH_TEXTURE_READS"), 1);
}
static FPermutationDomain BuildPermutationVector(int32 LUTTextureType, int32 ShaderQuality)
{
FPermutationDomain PermutationVector;
PermutationVector.Set<Super::FCommonPermutationDomain>(Super::BuildPermutationVector(LUTTextureType, ShaderQuality));
return PermutationVector;
}
};
IMPLEMENT_GLOBAL_SHADER(FGTAOMobile_HorizonSearchIntegralPS, "/Engine/Private/PostProcessAmbientOcclusionMobile.usf", "GTAOHorizonSearchIntegralPS", SF_Pixel);
class FGTAOMobile_SpatialFilterPS : public FGTAOMobile_SpatialFilter
{
using Super = FGTAOMobile_SpatialFilter;
public:
DECLARE_GLOBAL_SHADER(FGTAOMobile_SpatialFilterPS);
SHADER_USE_PARAMETER_STRUCT(FGTAOMobile_SpatialFilterPS, Super);
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_INCLUDE(FGTAOMobile_SpatialFilter::FParameters, Common)
RENDER_TARGET_BINDING_SLOTS()
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return Super::ShouldCompilePermutation(Parameters);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
Super::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("SPATIALFILTER_PIXEL_SHADER"), 1u);
OutEnvironment.SetDefine(TEXT("FORCE_DEPTH_TEXTURE_READS"), 1);
}
};
IMPLEMENT_GLOBAL_SHADER(FGTAOMobile_SpatialFilterPS, "/Engine/Private/PostProcessAmbientOcclusionMobile.usf", "GTAOSpatialFilterPS", SF_Pixel);
FRDGTextureRef CreateMobileScreenSpaceAOTexture(FRDGBuilder& GraphBuilder, const FSceneTexturesConfig& Config)
{
bool bGTAO = (CVarMobileAmbientOcclusionTechnique.GetValueOnRenderThread() == 0);
const uint32 DownsampleFactor = bGTAO ? 2 : 1;
const FIntPoint Extent = FIntPoint::DivideAndRoundUp(Config.Extent, DownsampleFactor);
EPixelFormat Format = PF_G8;
const EMobileAmbientOcclusionShaderType ShaderType = GetCVarMobileAmbientOcclusionShaderTypeOnRenderThread();
// G8 isn't supported as UAV on Android OpenGLES, fall back to RGBA8 for compute shader usage.
if (bGTAO
&& IsOpenGLPlatform(Config.ShaderPlatform)
&& IsComputeShaderType(ShaderType)
)
{
Format = PF_R8G8B8A8;
}
ETextureCreateFlags TextureCreateFlags = TexCreate_ShaderResource | TexCreate_RenderTargetable;
if (IsComputeShaderType(ShaderType))
{
TextureCreateFlags |= TexCreate_UAV;
}
if (UE::PixelFormat::HasCapabilities(Format, EPixelFormatCapabilities::LossyCompressible) && CVarMobileAmbientOcclusionCompression.GetValueOnRenderThread() != 0)
{
TextureCreateFlags |= TexCreate_LossyCompressionLowBitrate;
}
return GraphBuilder.CreateTexture(FRDGTextureDesc::Create2D(Extent, Format, FClearValueBinding::Black, TextureCreateFlags), TEXT("ScreenSpaceAO"));
}
static void RenderGTAO(FRDGBuilder& GraphBuilder, FRDGTextureRef SceneDepthTexture, FRDGTextureRef AmbientOcclusionTexture, const TArray<FViewInfo>& Views)
{
static const auto GTAOThicknessBlendCVar = IConsoleManager::Get().FindTConsoleVariableDataFloat(TEXT("r.GTAO.ThicknessBlend"));
static const auto GTAOFalloffStartRatioCVar = IConsoleManager::Get().FindTConsoleVariableDataFloat(TEXT("r.GTAO.FalloffStartRatio"));
static const auto GTAOFalloffEndCVar = IConsoleManager::Get().FindTConsoleVariableDataFloat(TEXT("r.GTAO.FalloffEnd"));
static const auto GTAONumAnglesCVar = IConsoleManager::Get().FindTConsoleVariableDataFloat(TEXT("r.GTAO.NumAngles"));
const uint32 DownsampleFactor = 2;
const int32 MobileGTAOPreIntegratedTextureType = FMath::Min(CVarMobileGTAOPreIntegratedTextureType.GetValueOnRenderThread(), 2);
const int32 MobileAmbientOcclusionQuality = FMath::Min(CVarMobileAmbientOcclusionQuality.GetValueOnRenderThread(), 3);
const FIntPoint& DepthBufferSize = SceneDepthTexture->Desc.Extent;
const FIntPoint& BufferSize = AmbientOcclusionTexture->Desc.Extent;
float FallOffEnd = GTAOFalloffEndCVar ? GTAOFalloffEndCVar->GetValueOnRenderThread() : 200.0f;
float FallOffStartRatio = GTAOFalloffStartRatioCVar ? FMath::Clamp(GTAOFalloffStartRatioCVar->GetValueOnRenderThread(), 0.0f, 0.999f) : 0.5f;
float FallOffStart = FallOffEnd * FallOffStartRatio;
float FallOffStartSq = FallOffStart * FallOffStart;
float FallOffEndSq = FallOffEnd * FallOffEnd;
float FallOffScale = 1.0f / (FallOffEndSq - FallOffStartSq);
float FallOffBias = -FallOffStartSq * FallOffScale;
FVector4f FallOffStartEndScaleBias(FallOffStart, FallOffEnd, FallOffScale, FallOffBias);
float ThicknessBlend = GTAOThicknessBlendCVar ? GTAOThicknessBlendCVar->GetValueOnRenderThread() : 0.5f;
ThicknessBlend = FMath::Clamp(1.0f - (ThicknessBlend*ThicknessBlend), 0.0f, 0.99f);
float NumAngles = GTAONumAnglesCVar ? FMath::Clamp(GTAONumAnglesCVar->GetValueOnRenderThread(), 1.0f, 16.0f) : 2;
float SinDeltaAngle, CosDeltaAngle;
FMath::SinCos(&SinDeltaAngle, &CosDeltaAngle, PI / NumAngles);
const EMobileAmbientOcclusionShaderType ShaderType = GetCVarMobileAmbientOcclusionShaderTypeOnRenderThread();
FRDGTextureRef HorizonSearchIntegralTexture{};
FRDGTextureUAVRef HorizonSearchIntegralTextureUAV{};
FRDGTextureUAVRef AmbientOcclusionTextureUAV{};
if (IsComputeShaderType(ShaderType))
{
AmbientOcclusionTextureUAV = GraphBuilder.CreateUAV(AmbientOcclusionTexture);
}
if (IsTwoPassShaderType(ShaderType))
{
ETextureCreateFlags TextureCreateFlags = TexCreate_ShaderResource | TexCreate_RenderTargetable;
const EPixelFormat PixelFormat = PF_R8G8B8A8;
if (UE::PixelFormat::HasCapabilities(PixelFormat, EPixelFormatCapabilities::LossyCompressible) && CVarMobileAmbientOcclusionCompression.GetValueOnRenderThread() != 0)
{
TextureCreateFlags |= TexCreate_LossyCompressionLowBitrate;
}
HorizonSearchIntegralTexture = GraphBuilder.CreateTexture(FRDGTextureDesc::Create2D(BufferSize, PixelFormat, FClearValueBinding::Black, TextureCreateFlags | (IsComputeShaderType(ShaderType) ? TexCreate_UAV : TexCreate_None)), TEXT("HorizonSearchIntegralTexture"));
if (IsComputeShaderType(ShaderType))
{
HorizonSearchIntegralTextureUAV = GraphBuilder.CreateUAV(HorizonSearchIntegralTexture);
}
}
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
const FFinalPostProcessSettings& Settings = View.FinalPostProcessSettings;
const FIntRect& ViewRect = FIntRect::DivideAndRoundUp(View.ViewRect, DownsampleFactor);
FVector4f WorldRadiusAdjSinCosDeltaAngleThickness(FallOffStartEndScaleBias.Y * DepthBufferSize.Y * View.ViewMatrices.GetProjectionMatrix().M[0][0], SinDeltaAngle, CosDeltaAngle, ThicknessBlend);
if (ShaderType == EMobileAmbientOcclusionShaderType::ComputeShader)
{
FGTAOMobile_HorizonSearchIntegralSpatialFilterCS::FParameters* HorizonSearchIntegralSpatialFilterParameters = GraphBuilder.AllocParameters<FGTAOMobile_HorizonSearchIntegralSpatialFilterCS::FParameters>();
FGTAOMobile_HorizonSearchIntegral::SetupShaderParameters(HorizonSearchIntegralSpatialFilterParameters->Common, GraphBuilder, View, ViewRect, DepthBufferSize, BufferSize, FallOffStartEndScaleBias, WorldRadiusAdjSinCosDeltaAngleThickness, SceneDepthTexture);
HorizonSearchIntegralSpatialFilterParameters->Power_Intensity_ScreenPixelsToSearch = FVector4f(Settings.AmbientOcclusionPower * 0.5f, Settings.AmbientOcclusionIntensity, 0.0f, 0.0f);
HorizonSearchIntegralSpatialFilterParameters->OutTexture = AmbientOcclusionTextureUAV;
auto ComputeShaderPermutationVector = FGTAOMobile_HorizonSearchIntegralSpatialFilterCS::BuildPermutationVector(MobileGTAOPreIntegratedTextureType, MobileAmbientOcclusionQuality - 1);
TShaderMapRef<FGTAOMobile_HorizonSearchIntegralSpatialFilterCS> ComputeShader(View.ShaderMap, ComputeShaderPermutationVector);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("AmbientOcclusion_HorizonSearchIntegralSpatialFilter %dx%d (CS)", ViewRect.Width(), ViewRect.Height()),
ERDGPassFlags::Compute | ERDGPassFlags::NeverCull,
ComputeShader,
HorizonSearchIntegralSpatialFilterParameters,
FComputeShaderUtils::GetGroupCount(ViewRect.Size(), FGTAOMobile_HorizonSearchIntegralSpatialFilterCS::TexelsPerThreadGroup));
}
else if (ShaderType == EMobileAmbientOcclusionShaderType::PixelShader)
{
TShaderMapRef<FScreenPassVS> VertexShader(View.ShaderMap);
FScreenPassRenderTarget HorizonSearchIntegralRT(HorizonSearchIntegralTexture, ViewRect, ViewIndex > 0 ? ERenderTargetLoadAction::ELoad : ERenderTargetLoadAction::EClear);
FGTAOMobile_HorizonSearchIntegralPS::FParameters* HorizonSearchIntegralParameters = GraphBuilder.AllocParameters<FGTAOMobile_HorizonSearchIntegralPS::FParameters>();
FGTAOMobile_HorizonSearchIntegral::SetupShaderParameters(HorizonSearchIntegralParameters->Common, GraphBuilder, View, ViewRect, DepthBufferSize, BufferSize, FallOffStartEndScaleBias, WorldRadiusAdjSinCosDeltaAngleThickness, SceneDepthTexture);
HorizonSearchIntegralParameters->RenderTargets[0] = HorizonSearchIntegralRT.GetRenderTargetBinding();
auto HorizonSearchIntegralShaderPermutationVector = FGTAOMobile_HorizonSearchIntegralPS::BuildPermutationVector(MobileGTAOPreIntegratedTextureType, MobileAmbientOcclusionQuality - 1);
TShaderMapRef<FGTAOMobile_HorizonSearchIntegralPS> HorizonSearchIntegralShader(View.ShaderMap, HorizonSearchIntegralShaderPermutationVector);
ClearUnusedGraphResources(HorizonSearchIntegralShader, HorizonSearchIntegralParameters);
GraphBuilder.AddPass(
RDG_EVENT_NAME("AmbientOcclusion_HorizonSearchIntegral %dx%d (PS)", ViewRect.Width(), ViewRect.Height()),
HorizonSearchIntegralParameters,
ERDGPassFlags::Raster | ERDGPassFlags::NeverCull,
[VertexShader, HorizonSearchIntegralShader, HorizonSearchIntegralParameters, ViewRect, BufferSize](FRHICommandList& RHICmdList)
{
RHICmdList.SetViewport(ViewRect.Min.X, ViewRect.Min.Y, 0.0f, ViewRect.Max.X, ViewRect.Max.Y, 1.0f);
FGraphicsPipelineStateInitializer GraphicsPSOInit;
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI();
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI();
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI;
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = HorizonSearchIntegralShader.GetPixelShader();
GraphicsPSOInit.PrimitiveType = PT_TriangleList;
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0);
SetShaderParameters(RHICmdList, HorizonSearchIntegralShader, HorizonSearchIntegralShader.GetPixelShader(), *HorizonSearchIntegralParameters);
DrawRectangle(
RHICmdList,
0, 0,
BufferSize.X, BufferSize.Y,
ViewRect.Min.X, ViewRect.Min.Y,
ViewRect.Width(), ViewRect.Height(),
BufferSize,
BufferSize,
VertexShader,
EDRF_UseTriangleOptimization);
});
FScreenPassRenderTarget AmbientOcclusionRT(AmbientOcclusionTexture, ViewRect, ViewIndex > 0 ? ERenderTargetLoadAction::ELoad : ERenderTargetLoadAction::EClear);
FGTAOMobile_SpatialFilterPS::FParameters* SpatialFilterParameters = GraphBuilder.AllocParameters<FGTAOMobile_SpatialFilterPS::FParameters>();
FGTAOMobile_SpatialFilter::SetupShaderParameters(SpatialFilterParameters->Common, View, ViewRect, BufferSize, HorizonSearchIntegralTexture);
SpatialFilterParameters->RenderTargets[0] = AmbientOcclusionRT.GetRenderTargetBinding();
TShaderMapRef<FGTAOMobile_SpatialFilterPS> SpatialFilterShader(View.ShaderMap);
ClearUnusedGraphResources(SpatialFilterShader, SpatialFilterParameters);
GraphBuilder.AddPass(
RDG_EVENT_NAME("AmbientOcclusion_SpatialFilter %dx%d (PS)", ViewRect.Width(), ViewRect.Height()),
SpatialFilterParameters,
ERDGPassFlags::Raster | ERDGPassFlags::NeverCull,
[VertexShader, SpatialFilterShader, SpatialFilterParameters, ViewRect, BufferSize](FRHICommandList& RHICmdList)
{
RHICmdList.SetViewport(ViewRect.Min.X, ViewRect.Min.Y, 0.0f, ViewRect.Max.X, ViewRect.Max.Y, 1.0f);
FGraphicsPipelineStateInitializer GraphicsPSOInit;
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI();
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI();
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI;
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = SpatialFilterShader.GetPixelShader();
GraphicsPSOInit.PrimitiveType = PT_TriangleList;
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0);
SetShaderParameters(RHICmdList, SpatialFilterShader, SpatialFilterShader.GetPixelShader(), *SpatialFilterParameters);
DrawRectangle(
RHICmdList,
0, 0,
BufferSize.X, BufferSize.Y,
ViewRect.Min.X, ViewRect.Min.Y,
ViewRect.Width(), ViewRect.Height(),
BufferSize,
BufferSize,
VertexShader,
EDRF_UseTriangleOptimization);
});
}
else
{
FGTAOMobile_HorizonSearchIntegralCS::FParameters* HorizonSearchIntegralParameters = GraphBuilder.AllocParameters<FGTAOMobile_HorizonSearchIntegralCS::FParameters>();
FGTAOMobile_HorizonSearchIntegral::SetupShaderParameters(HorizonSearchIntegralParameters->Common, GraphBuilder, View, ViewRect, DepthBufferSize, BufferSize, FallOffStartEndScaleBias, WorldRadiusAdjSinCosDeltaAngleThickness, SceneDepthTexture);
HorizonSearchIntegralParameters->OutTexture = HorizonSearchIntegralTextureUAV;
auto HorizonSearchIntegralShaderPermutationVector = FGTAOMobile_HorizonSearchIntegralCS::BuildPermutationVector(MobileGTAOPreIntegratedTextureType, MobileAmbientOcclusionQuality - 1);
TShaderMapRef<FGTAOMobile_HorizonSearchIntegralCS> HorizonSearchIntegralShader(View.ShaderMap, HorizonSearchIntegralShaderPermutationVector);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("AmbientOcclusion_HorizonSearchIntegral %dx%d (CS)", ViewRect.Width(), ViewRect.Height()),
ERDGPassFlags::Compute | ERDGPassFlags::NeverCull,
HorizonSearchIntegralShader,
HorizonSearchIntegralParameters,
FComputeShaderUtils::GetGroupCount(ViewRect.Size(), FGTAOMobile_HorizonSearchIntegralCS::TexelsPerThreadGroup));
FGTAOMobile_SpatialFilterCS::FParameters* SpatialFilterParameters = GraphBuilder.AllocParameters<FGTAOMobile_SpatialFilterCS::FParameters>();
FGTAOMobile_SpatialFilter::SetupShaderParameters(SpatialFilterParameters->Common, View, ViewRect, BufferSize, HorizonSearchIntegralTexture);
SpatialFilterParameters->OutTexture = AmbientOcclusionTextureUAV;
TShaderMapRef<FGTAOMobile_SpatialFilterCS> SpatialFilterShader(View.ShaderMap);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("AmbientOcclusion_SpatialFilter %dx%d (CS)", ViewRect.Width(), ViewRect.Height()),
ERDGPassFlags::Compute | ERDGPassFlags::NeverCull,
SpatialFilterShader,
SpatialFilterParameters,
FComputeShaderUtils::GetGroupCount(ViewRect.Size(), FGTAOMobile_SpatialFilterCS::TexelsPerThreadGroup));
}
if (View.ViewState && !View.bStatePrevViewInfoIsReadOnly)
{
GraphBuilder.QueueTextureExtraction(AmbientOcclusionTexture, &View.ViewState->PrevFrameViewInfo.MobileAmbientOcclusion);
}
}
}
// --------------------------------------------------------------------------------------------------------------------
struct FMobileSSAOCommonParameters
{
TRDGUniformBufferRef<FMobileSceneTextureUniformParameters> SceneTexturesUniformBufferRDG;
FScreenPassTextureViewport SceneTexturesViewport;
FScreenPassTexture SceneDepth;
};
static const uint32 kMobileSSAOParametersArraySize = 5;
BEGIN_SHADER_PARAMETER_STRUCT(FMobileSSAOShaderParameters, )
SHADER_PARAMETER_ARRAY(FVector4f, ScreenSpaceAOParams, [kMobileSSAOParametersArraySize])
SHADER_PARAMETER_STRUCT(FScreenPassTextureViewportParameters, AOViewport)
SHADER_PARAMETER_STRUCT(FScreenPassTextureViewportParameters, AOSceneViewport)
END_SHADER_PARAMETER_STRUCT();
BEGIN_SHADER_PARAMETER_STRUCT(FMobileAmbientOcclusionParameters, RENDERER_API)
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FMobileSceneTextureUniformParameters, SceneTextures)
SHADER_PARAMETER_STRUCT_INCLUDE(FHZBParameters, HZBParameters)
SHADER_PARAMETER_STRUCT_INCLUDE(FMobileSSAOShaderParameters, SSAOParameters)
SHADER_PARAMETER_SAMPLER(SamplerState, SSAO_Sampler)
SHADER_PARAMETER(FVector2f, SSAO_DownsampledAOInverseSize)
SHADER_PARAMETER(FVector2f, SSAO_SvPositionScaleBias)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, RandomNormalTexture)
SHADER_PARAMETER_SAMPLER(SamplerState, RandomNormalTextureSampler)
END_SHADER_PARAMETER_STRUCT();
class FMobileAmbientOcclusionPS : public FGlobalShader
{
public:
DECLARE_GLOBAL_SHADER(FMobileAmbientOcclusionPS);
SHADER_USE_PARAMETER_STRUCT(FMobileAmbientOcclusionPS, FGlobalShader);
class FShaderQualityDim : SHADER_PERMUTATION_INT("SHADER_QUALITY", 5);
class FOutputDepth : SHADER_PERMUTATION_BOOL("OUTPUT_DEPTH");
using FPermutationDomain = TShaderPermutationDomain<FShaderQualityDim, FOutputDepth>;
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("SSAO"), 1);
OutEnvironment.SetDefine(TEXT("FORCE_DEPTH_TEXTURE_READS"), 1);
}
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return IsMobileAmbientOcclusionEnabled(Parameters.Platform);
}
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_INCLUDE(FMobileAmbientOcclusionParameters, SharedParameters)
RENDER_TARGET_BINDING_SLOTS()
END_SHADER_PARAMETER_STRUCT();
};
IMPLEMENT_GLOBAL_SHADER(FMobileAmbientOcclusionPS, "/Engine/Private/PostProcessAmbientOcclusionMobile.usf", "MainPS", SF_Pixel);
class FMobileAmbientOcclusionUpsamplePS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FMobileAmbientOcclusionUpsamplePS);
SHADER_USE_PARAMETER_STRUCT(FMobileAmbientOcclusionUpsamplePS, FGlobalShader);
class FUpsampleQualityDim : SHADER_PERMUTATION_INT("UPSAMPLE_QUALITY", 3);
using FPermutationDomain = TShaderPermutationDomain <FUpsampleQualityDim>;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FMobileSceneTextureUniformParameters, SceneTextures)
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, AOTexture)
SHADER_PARAMETER_SAMPLER(SamplerState, AOSampler)
RENDER_TARGET_BINDING_SLOTS()
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return IsMobileAmbientOcclusionEnabled(Parameters.Platform);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
OutEnvironment.SetDefine(TEXT("UPSAMPLE_PASS"), 1);
}
};
IMPLEMENT_GLOBAL_SHADER(FMobileAmbientOcclusionUpsamplePS, "/Engine/Private/PostProcessAmbientOcclusionMobile.usf", "AmbientOcclusionUpsamplePS", SF_Pixel);
// --------------------------------------------------------------------------------------------------------------------
static FMobileSSAOCommonParameters GetMobileSSAOCommonParameters(
FRDGBuilder& GraphBuilder,
const FViewInfo& View,
FRDGTextureRef SceneDepthTexture,
TRDGUniformBufferRef<FMobileSceneTextureUniformParameters> SceneTexturesUniformBufferRDG)
{
FMobileSSAOCommonParameters CommonParameters;
CommonParameters.SceneTexturesUniformBufferRDG = SceneTexturesUniformBufferRDG;
CommonParameters.SceneTexturesViewport = FScreenPassTextureViewport(SceneDepthTexture, View.ViewRect);
CommonParameters.SceneDepth = FScreenPassTexture(SceneDepthTexture);
return CommonParameters;
}
static FMobileSSAOShaderParameters GetMobileSSAOShaderParameters(
const FViewInfo& View,
const FScreenPassTextureViewport& InputViewport,
const FScreenPassTextureViewport& OutputViewport,
const FScreenPassTextureViewport& SceneViewport)
{
const FFinalPostProcessSettings& Settings = View.FinalPostProcessSettings;
FIntPoint RandomizationSize = GSystemTextures.SSAORandomization->GetDesc().Extent * InputViewport.Extent / OutputViewport.Extent;
FVector2D ViewportUVToRandomUV(InputViewport.Extent.X / (float)RandomizationSize.X, InputViewport.Extent.Y / (float)RandomizationSize.Y);
// e.g. 4 means the input texture is 4x smaller than the buffer size
uint32 ScaleToFullRes = SceneViewport.Extent.X / InputViewport.Extent.X;
FIntRect ViewRect = FIntRect::DivideAndRoundUp(View.ViewRect, ScaleToFullRes);
float AORadiusInShader = Settings.AmbientOcclusionRadius;
float ScaleRadiusInWorldSpace = 1.0f;
if (!Settings.AmbientOcclusionRadiusInWS)
{
// radius is defined in view space in 400 units
AORadiusInShader /= 400.0f;
ScaleRadiusInWorldSpace = 0.0f;
}
// /4 is an adjustment for usage with multiple mips
float f = FMath::Log2(float(ScaleToFullRes));
float g = pow(Settings.AmbientOcclusionMipScale, f);
AORadiusInShader *= pow(Settings.AmbientOcclusionMipScale, FMath::Log2(float(ScaleToFullRes))) / 4.0f;
float Ratio = View.UnscaledViewRect.Width() / (float)View.UnscaledViewRect.Height();
// Grab this and pass into shader so we can negate the fov influence of projection on the screen pos.
float InvTanHalfFov = View.ViewMatrices.GetProjectionMatrix().M[0][0];
float StaticFraction = FMath::Clamp(Settings.AmbientOcclusionStaticFraction, 0.0f, 1.0f);
// clamp to prevent user error
float FadeRadius = FMath::Max(1.0f, Settings.AmbientOcclusionFadeRadius);
float InvFadeRadius = 1.0f / FadeRadius;
FVector2D TemporalOffset(0.0f, 0.0f);
if (View.State)
{
TemporalOffset = (View.State->GetCurrentTemporalAASampleIndex() % 8) * FVector2D(2.48f, 7.52f) / (float)RandomizationSize.X;
}
const float HzbStepMipLevelFactorValue = FMath::Clamp(FSSAOHelper::GetAmbientOcclusionStepMipLevelFactor(), 0.0f, 100.0f);
const float InvAmbientOcclusionDistance = 1.0f / FMath::Max(Settings.AmbientOcclusionDistance_DEPRECATED, KINDA_SMALL_NUMBER);
FMobileSSAOShaderParameters Result{};
// /1000 to be able to define the value in that distance
Result.ScreenSpaceAOParams[0] = FVector4f(Settings.AmbientOcclusionPower, Settings.AmbientOcclusionBias / 1000.0f, InvAmbientOcclusionDistance, Settings.AmbientOcclusionIntensity);
Result.ScreenSpaceAOParams[1] = FVector4f(ViewportUVToRandomUV.X, ViewportUVToRandomUV.Y, AORadiusInShader, Ratio);
Result.ScreenSpaceAOParams[2] = FVector4f(ScaleToFullRes, Settings.AmbientOcclusionMipThreshold / ScaleToFullRes, ScaleRadiusInWorldSpace, Settings.AmbientOcclusionMipBlend);
Result.ScreenSpaceAOParams[3] = FVector4f(TemporalOffset.X, TemporalOffset.Y, StaticFraction, InvTanHalfFov);
Result.ScreenSpaceAOParams[4] = FVector4f(InvFadeRadius, -(Settings.AmbientOcclusionFadeDistance - FadeRadius) * InvFadeRadius, HzbStepMipLevelFactorValue, Settings.AmbientOcclusionFadeDistance);
Result.AOViewport = GetScreenPassTextureViewportParameters(OutputViewport);
Result.AOSceneViewport = GetScreenPassTextureViewportParameters(SceneViewport);
return Result;
}
static void AddMobileAmbientOcclusionPass(
FRDGBuilder& GraphBuilder,
const FViewInfo& View,
const FMobileSSAOCommonParameters& CommonParameters,
bool bHalfResolution,
bool bOutputDepth,
FScreenPassRenderTarget Output)
{
check(Output.IsValid());
const FScreenPassTextureViewport InputViewport = CommonParameters.SceneTexturesViewport;
const FScreenPassTextureViewport OutputViewport = GetDownscaledViewport(CommonParameters.SceneTexturesViewport, bHalfResolution ? 2 : 1);
const bool bDepthBoundsTestEnabled =
CVarMobileAmbientOcclusionDepthBoundsTest.GetValueOnRenderThread()
&& !bHalfResolution
&& GSupportsDepthBoundsTest
&& CommonParameters.SceneDepth.IsValid()
&& CommonParameters.SceneDepth.Texture->Desc.NumSamples == 1;
float DepthFar = 0.0f;
FDepthStencilBinding DepthStencilBinding(CommonParameters.SceneDepth.Texture, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ELoad, FExclusiveDepthStencil::DepthRead_StencilWrite);
if (bDepthBoundsTestEnabled)
{
const FFinalPostProcessSettings& Settings = View.FinalPostProcessSettings;
const FMatrix& ProjectionMatrix = View.ViewMatrices.GetProjectionMatrix();
const FVector4f Far = (FVector4f)ProjectionMatrix.TransformFVector4(FVector4(0, 0, Settings.AmbientOcclusionFadeDistance));
DepthFar = FMath::Min(1.0f, Far.Z / Far.W);
static_assert(bool(ERHIZBuffer::IsInverted), "Inverted depth buffer is assumed when setting depth bounds test for AO.");
FRenderTargetParameters* ClearParameters = GraphBuilder.AllocParameters<FRenderTargetParameters>();
ClearParameters->RenderTargets[0] = Output.GetRenderTargetBinding();
ClearParameters->RenderTargets.DepthStencil = DepthStencilBinding;
GraphBuilder.AddPass(
RDG_EVENT_NAME("DepthBounds ClearQuad(%s)", Output.Texture->Name),
ClearParameters,
ERDGPassFlags::Raster,
[OutputViewport, DepthFar](FRHICommandListImmediate& RHICmdList)
{
// We must clear all pixels that won't be touched by AO shader.
FClearQuadCallbacks Callbacks;
Callbacks.PSOModifier = [](FGraphicsPipelineStateInitializer& PSOInitializer)
{
PSOInitializer.bDepthBounds = true;
};
Callbacks.PreClear = [DepthFar](FRHICommandList& InRHICmdList)
{
// This is done by rendering a clear quad over a depth range from AmbientOcclusionFadeDistance to far plane.
InRHICmdList.SetDepthBounds(0, DepthFar); // NOTE: Inverted depth
};
Callbacks.PostClear = [DepthFar](FRHICommandList& InRHICmdList)
{
// Set depth bounds test to cover everything from near plane to AmbientOcclusionFadeDistance and run AO pixel shader.
InRHICmdList.SetDepthBounds(DepthFar, 1.0f);
};
RHICmdList.SetViewport(OutputViewport.Rect.Min.X, OutputViewport.Rect.Min.Y, 0.0f, OutputViewport.Rect.Max.X, OutputViewport.Rect.Max.Y, 1.0f);
DrawClearQuad(RHICmdList, FLinearColor::White, Callbacks);
});
// Make sure the following pass doesn't clear or ignore the data
Output.LoadAction = ERenderTargetLoadAction::ELoad;
}
FMobileAmbientOcclusionParameters SharedParameters;
SharedParameters.View = View.ViewUniformBuffer;
SharedParameters.SceneTextures = CommonParameters.SceneTexturesUniformBufferRDG;
SharedParameters.HZBParameters = GetHZBParametersForAO(GraphBuilder, View, CommonParameters.SceneTexturesViewport.Extent, EAOTechnique::SSAO);
SharedParameters.SSAOParameters = GetMobileSSAOShaderParameters(View, InputViewport, OutputViewport, CommonParameters.SceneTexturesViewport);
SharedParameters.SSAO_Sampler = TStaticSamplerState<SF_Point, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
SharedParameters.SSAO_SvPositionScaleBias = bHalfResolution ? FVector2f(2, -0.5) : FVector2f(1, 0);
SharedParameters.SSAO_DownsampledAOInverseSize = bHalfResolution ? FVector2f(1, 1) / FVector2f(OutputViewport.Extent) : FVector2f(1, 1);
SharedParameters.RandomNormalTexture = GraphBuilder.RegisterExternalTexture(GSystemTextures.SSAORandomization, TEXT("SSAORandomization"));
SharedParameters.RandomNormalTextureSampler = TStaticSamplerState<SF_Point, AM_Wrap, AM_Wrap, AM_Wrap>::GetRHI();
FRDGEventName EventName(TEXT("AmbientOcclusionPS %dx%d"),
OutputViewport.Rect.Width(), OutputViewport.Rect.Height());
FMobileAmbientOcclusionPS::FParameters* PassParameters = GraphBuilder.AllocParameters<FMobileAmbientOcclusionPS::FParameters>();
PassParameters->SharedParameters = MoveTemp(SharedParameters);
PassParameters->RenderTargets[0] = Output.GetRenderTargetBinding();
if (bDepthBoundsTestEnabled)
{
PassParameters->RenderTargets.DepthStencil = DepthStencilBinding;
}
const int32 MobileAmbientOcclusionQuality = FMath::Min(CVarMobileAmbientOcclusionQuality.GetValueOnRenderThread(), 3) - 1;
FMobileAmbientOcclusionPS::FPermutationDomain PermutationVector;
PermutationVector.Set<FMobileAmbientOcclusionPS::FShaderQualityDim>(MobileAmbientOcclusionQuality);
PermutationVector.Set<FMobileAmbientOcclusionPS::FOutputDepth>(bOutputDepth);
TShaderMapRef<FMobileAmbientOcclusionPS> PixelShader(View.ShaderMap, PermutationVector);
TShaderMapRef<FScreenPassVS> VertexShader(View.ShaderMap);
check(PassParameters);
ClearUnusedGraphResources(PixelShader, PassParameters);
GraphBuilder.AddPass(
MoveTemp(EventName),
PassParameters,
ERDGPassFlags::Raster,
[&View, OutputViewport, InputViewport, VertexShader, PixelShader, PassParameters, bDepthBoundsTestEnabled, DepthFar](FRHICommandListImmediate& RHICmdList)
{
const FIntRect InputRect = InputViewport.Rect;
const FIntPoint InputSize = InputViewport.Extent;
const FIntRect OutputRect = OutputViewport.Rect;
const FIntPoint OutputSize = OutputRect.Size();
RHICmdList.SetViewport(OutputRect.Min.X, OutputRect.Min.Y, 0.0f, OutputRect.Max.X, OutputRect.Max.Y, 1.0f);
FGraphicsPipelineStateInitializer GraphicsPSOInit;
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI();
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<FM_Solid, CM_None>::GetRHI();
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI;
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
GraphicsPSOInit.PrimitiveType = PT_TriangleList;
GraphicsPSOInit.bDepthBounds = bDepthBoundsTestEnabled;
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0);
SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), *PassParameters);
if (bDepthBoundsTestEnabled)
{
RHICmdList.SetDepthBounds(DepthFar, 1.0f);
}
DrawPostProcessPass(
RHICmdList,
0, 0, OutputSize.X, OutputSize.Y,
InputRect.Min.X, InputRect.Min.Y, InputRect.Width(), InputRect.Height(),
OutputSize,
InputSize,
VertexShader,
View.StereoViewIndex,
false,
EDRF_UseTriangleOptimization);
if (bDepthBoundsTestEnabled)
{
RHICmdList.SetDepthBounds(0, 1.0f);
}
});
}
static void AddMobileAmbientOcclusionUpsamplePass(
FRDGBuilder& GraphBuilder,
const FViewInfo& View,
const FMobileSSAOCommonParameters& CommonParameters,
int32 UpsampleQuality,
FRDGTextureRef SourceTexture,
FScreenPassRenderTarget Output)
{
const bool bDepthBoundsTestEnabled =
CVarMobileAmbientOcclusionDepthBoundsTest.GetValueOnRenderThread()
&& GSupportsDepthBoundsTest
&& CommonParameters.SceneDepth.IsValid()
&& CommonParameters.SceneDepth.Texture->Desc.NumSamples == 1;
const FScreenPassTextureViewport OutputViewport = CommonParameters.SceneTexturesViewport;
float DepthFar = 0.0f;
FDepthStencilBinding DepthStencilBinding(CommonParameters.SceneDepth.Texture, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ELoad, FExclusiveDepthStencil::DepthRead_StencilWrite);
if (bDepthBoundsTestEnabled)
{
const FFinalPostProcessSettings& Settings = View.FinalPostProcessSettings;
const FMatrix& ProjectionMatrix = View.ViewMatrices.GetProjectionMatrix();
const FVector4f Far = (FVector4f)ProjectionMatrix.TransformFVector4(FVector4(0, 0, Settings.AmbientOcclusionFadeDistance));
DepthFar = FMath::Min(1.0f, Far.Z / Far.W);
static_assert(bool(ERHIZBuffer::IsInverted), "Inverted depth buffer is assumed when setting depth bounds test for AO.");
FRenderTargetParameters* ClearParameters = GraphBuilder.AllocParameters<FRenderTargetParameters>();
ClearParameters->RenderTargets[0] = Output.GetRenderTargetBinding();
ClearParameters->RenderTargets.DepthStencil = DepthStencilBinding;
GraphBuilder.AddPass(
RDG_EVENT_NAME("DepthBounds ClearQuad(%s)", Output.Texture->Name),
ClearParameters,
ERDGPassFlags::Raster,
[OutputViewport, DepthFar](FRHICommandListImmediate& RHICmdList)
{
RHICmdList.SetViewport(OutputViewport.Rect.Min.X, OutputViewport.Rect.Min.Y, 0.0f, OutputViewport.Rect.Max.X, OutputViewport.Rect.Max.Y, 1.0f);
// We must clear all pixels that won't be touched by AO shader.
FClearQuadCallbacks Callbacks;
Callbacks.PSOModifier = [](FGraphicsPipelineStateInitializer& PSOInitializer)
{
PSOInitializer.bDepthBounds = true;
};
Callbacks.PreClear = [DepthFar](FRHICommandList& InRHICmdList)
{
// This is done by rendering a clear quad over a depth range from AmbientOcclusionFadeDistance to far plane.
InRHICmdList.SetDepthBounds(0, DepthFar); // NOTE: Inverted depth
};
Callbacks.PostClear = [DepthFar](FRHICommandList& InRHICmdList)
{
// Set depth bounds test to cover everything from near plane to AmbientOcclusionFadeDistance and run AO pixel shader.
InRHICmdList.SetDepthBounds(DepthFar, 1.0f);
};
DrawClearQuad(RHICmdList, FLinearColor::White, Callbacks);
});
// Make sure the following pass doesn't clear or ignore the data
Output.LoadAction = ERenderTargetLoadAction::ELoad;
}
FMobileAmbientOcclusionUpsamplePS::FParameters* PassParameters = GraphBuilder.AllocParameters<FMobileAmbientOcclusionUpsamplePS::FParameters>();
PassParameters->RenderTargets[0] = Output.GetRenderTargetBinding();
if (bDepthBoundsTestEnabled)
{
PassParameters->RenderTargets.DepthStencil = DepthStencilBinding;
}
PassParameters->View = GetShaderBinding(View.ViewUniformBuffer);
PassParameters->SceneTextures = CommonParameters.SceneTexturesUniformBufferRDG;
PassParameters->AOTexture = SourceTexture;
PassParameters->AOSampler = TStaticSamplerState<SF_Bilinear>::GetRHI();
typename FMobileAmbientOcclusionUpsamplePS::FPermutationDomain PermutationVector;
PermutationVector.Set<typename FMobileAmbientOcclusionUpsamplePS::FUpsampleQualityDim>(UpsampleQuality);
TShaderMapRef<FMobileAmbientOcclusionUpsamplePS> PixelShader(View.ShaderMap, PermutationVector);
TShaderMapRef<FScreenPassVS> VertexShader(View.ShaderMap);
ClearUnusedGraphResources(PixelShader, PassParameters);
GraphBuilder.AddPass(
RDG_EVENT_NAME("UpsamplePS Quality %d", UpsampleQuality),
PassParameters,
ERDGPassFlags::Raster,
[PassParameters, &View, OutputViewport, VertexShader, PixelShader, bDepthBoundsTestEnabled, DepthFar](FRHICommandList& RHICmdList)
{
RHICmdList.SetViewport(OutputViewport.Rect.Min.X, OutputViewport.Rect.Min.Y, 0.0f, OutputViewport.Rect.Max.X, OutputViewport.Rect.Max.Y, 1.0f);
FGraphicsPipelineStateInitializer GraphicsPSOInit;
FPixelShaderUtils::InitFullscreenPipelineState(RHICmdList, View.ShaderMap, PixelShader, GraphicsPSOInit);
GraphicsPSOInit.bDepthBounds = bDepthBoundsTestEnabled;
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0);
SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), *PassParameters);
if (bDepthBoundsTestEnabled)
{
RHICmdList.SetDepthBounds(DepthFar, 1.0f);
}
FPixelShaderUtils::DrawFullscreenTriangle(RHICmdList);
if (bDepthBoundsTestEnabled)
{
RHICmdList.SetDepthBounds(0.0f, 1.0f);
}
});
}
static void RenderSSAO(FRDGBuilder& GraphBuilder, FRDGTextureRef SceneDepthTexture, FRDGTextureRef AmbientOcclusionTexture, const TArray<FViewInfo>& Views)
{
RDG_EVENT_SCOPE_STAT(GraphBuilder, MobileSSAO, "MobileSSAO");
RDG_GPU_STAT_SCOPE(GraphBuilder, MobileSSAO);
const int32 HalfResolutionSetting = CVarMobileSSAOHalfResolution.GetValueOnRenderThread();
const bool bHalfResolution = HalfResolutionSetting > 0;
const int32 UpsampleQuality = FMath::Clamp(HalfResolutionSetting - 1, 0, 2);
const bool bBilateralUpsample = UpsampleQuality > 0;
FRDGTextureRef HalfResolutionTexture = nullptr;
if (bHalfResolution)
{
// Bilateral requires 32bit format for AO + depth.
const EPixelFormat Format = bBilateralUpsample ? PF_R8G8B8A8 : AmbientOcclusionTexture->Desc.Format;
const FIntPoint Extent = GetDownscaledExtent(AmbientOcclusionTexture->Desc.Extent, 2);
HalfResolutionTexture = GraphBuilder.CreateTexture(
FRDGTextureDesc::Create2D(Extent, Format, FClearValueBinding::None, TexCreate_ShaderResource | TexCreate_RenderTargetable),
TEXT("HalfResolutionScreenSpaceAO"));
}
TRDGUniformBufferRef<FMobileSceneTextureUniformParameters> SceneTexturesUniformBufferRDG = CreateMobileSceneTextureUniformBuffer(
GraphBuilder, GetViewFamilyInfo(Views).GetSceneTexturesChecked(), EMobileSceneTextureSetupMode::SceneDepth);
for (FViewInfo const& View : Views)
{
FMobileSSAOCommonParameters Parameters = GetMobileSSAOCommonParameters(GraphBuilder, View, SceneDepthTexture, SceneTexturesUniformBufferRDG);
FScreenPassRenderTarget FinalTarget = FScreenPassRenderTarget(AmbientOcclusionTexture, View.ViewRect, ERenderTargetLoadAction::ENoAction);
if (HalfResolutionTexture != nullptr)
{
FScreenPassRenderTarget HalfResolutionTarget = FScreenPassRenderTarget(HalfResolutionTexture, View.ViewRect, ERenderTargetLoadAction::ENoAction);
AddMobileAmbientOcclusionPass(GraphBuilder, View, Parameters, bHalfResolution, bBilateralUpsample, HalfResolutionTarget);
AddMobileAmbientOcclusionUpsamplePass(GraphBuilder, View, Parameters, UpsampleQuality, HalfResolutionTexture, FinalTarget);
}
else
{
AddMobileAmbientOcclusionPass(GraphBuilder, View, Parameters, bHalfResolution, bBilateralUpsample, FinalTarget);
}
}
}
// --------------------------------------------------------------------------------------------------------------------
void FMobileSceneRenderer::RenderAmbientOcclusion(FRDGBuilder& GraphBuilder, FRDGTextureRef SceneDepthTexture, FRDGTextureRef AmbientOcclusionTexture)
{
const int32 Technique = CVarMobileAmbientOcclusionTechnique.GetValueOnRenderThread();
switch (Technique)
{
case 0:
RenderGTAO(GraphBuilder, SceneDepthTexture, AmbientOcclusionTexture, Views);
break;
case 1:
RenderSSAO(GraphBuilder, SceneDepthTexture, AmbientOcclusionTexture, Views);
break;
}
}