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

305 lines
13 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=========================================================================================
PostProcessDeviceEncodingOnly.cpp: Post processing device encoding only implementation.
===========================================================================================*/
#include "PostProcess/PostProcessDeviceEncodingOnly.h"
#include "DataDrivenShaderPlatformInfo.h"
#include "EngineGlobals.h"
#include "ScenePrivate.h"
#include "RendererModule.h"
#include "PostProcess/PostProcessing.h"
#include "PostProcess/SceneFilterRendering.h"
#include "PostProcess/PostProcessCombineLUTs.h"
#include "PostProcess/PostProcessMobile.h"
#include "PostProcess/PostProcessing.h"
#include "ClearQuad.h"
#include "PipelineStateCache.h"
#include "HDRHelper.h"
#include "ACESUtils.h"
namespace
{
const int32 GDeviceEncodingOnlyComputeTileSizeX = 8;
const int32 GDeviceEncodingOnlyComputeTileSizeY = 8;
namespace DeviceEncodingOnlyPermutation
{
// Desktop renderer permutation dimensions.
class FDeviceEncodingOnlyOutputDeviceSRGB : SHADER_PERMUTATION_BOOL("OUTPUT_DEVICE_SRGB");
class FDeviceEncodingUseACES2 : SHADER_PERMUTATION_BOOL("USE_ACES_2");
using FDesktopDomain = TShaderPermutationDomain<FDeviceEncodingOnlyOutputDeviceSRGB, FDeviceEncodingUseACES2>;
} // namespace DeviceEncodingOnlyPermutation
} // namespace
FDeviceEncodingOnlyOutputDeviceParameters GetDeviceEncodingOnlyOutputDeviceParameters(const FSceneViewFamily& Family)
{
static TConsoleVariableData<float>* CVarOutputGamma = IConsoleManager::Get().FindTConsoleVariableDataFloat(TEXT("r.TonemapperGamma"));
EDisplayOutputFormat OutputDeviceValue;
if (Family.SceneCaptureSource == SCS_FinalColorHDR)
{
OutputDeviceValue = EDisplayOutputFormat::HDR_LinearNoToneCurve;
}
else if (Family.SceneCaptureSource == SCS_FinalToneCurveHDR)
{
OutputDeviceValue = EDisplayOutputFormat::HDR_LinearWithToneCurve;
}
else
{
OutputDeviceValue = Family.RenderTarget->GetDisplayOutputFormat();
}
float Gamma = CVarOutputGamma->GetValueOnRenderThread();
// In case gamma is unspecified, fall back to 2.2 which is the most common case
if ((PLATFORM_APPLE || OutputDeviceValue == EDisplayOutputFormat::SDR_ExplicitGammaMapping) && Gamma == 0.0f)
{
Gamma = 2.2f;
}
// Enforce user-controlled ramp over sRGB or Rec709
if (Gamma > 0.0f && (OutputDeviceValue == EDisplayOutputFormat::SDR_sRGB || OutputDeviceValue == EDisplayOutputFormat::SDR_Rec709))
{
OutputDeviceValue = EDisplayOutputFormat::SDR_ExplicitGammaMapping;
}
FVector InvDisplayGammaValue;
InvDisplayGammaValue.X = 1.0f / Family.RenderTarget->GetDisplayGamma();
InvDisplayGammaValue.Y = 2.2f / Family.RenderTarget->GetDisplayGamma();
InvDisplayGammaValue.Z = 1.0f / FMath::Max(Gamma, 1.0f);
FDeviceEncodingOnlyOutputDeviceParameters Parameters;
Parameters.InverseGamma = (FVector3f)InvDisplayGammaValue;
Parameters.OutputDevice = static_cast<uint32>(OutputDeviceValue);
Parameters.OutputGamut = static_cast<uint32>(Family.RenderTarget->GetDisplayColorGamut());
Parameters.OutputMaxLuminance = HDRGetDisplayMaximumLuminance();
return Parameters;
}
BEGIN_SHADER_PARAMETER_STRUCT(FDeviceEncodingACESTonemapShaderParameters, )
SHADER_PARAMETER(FVector4f, ACESMinMaxData) // xy = min ACES/luminance, zw = max ACES/luminance
SHADER_PARAMETER(FVector4f, ACESMidData) // x = mid ACES, y = mid luminance, z = mid slope
SHADER_PARAMETER(FVector4f, ACESCoefsLow_0) // coeflow 0-3
SHADER_PARAMETER(FVector4f, ACESCoefsHigh_0) // coefhigh 0-3
SHADER_PARAMETER(float, ACESCoefsLow_4)
SHADER_PARAMETER(float, ACESCoefsHigh_4)
SHADER_PARAMETER(float, ACESSceneColorMultiplier)
SHADER_PARAMETER(float, ACESGamutCompression)
SHADER_PARAMETER_SRV(Texture2D<float>, ACESReachTable)
SHADER_PARAMETER_SRV(Texture2D<FVector3f>, ACESGamutTable)
SHADER_PARAMETER_SRV(Texture2D<float>, ACESGammaTable)
END_SHADER_PARAMETER_STRUCT()
BEGIN_SHADER_PARAMETER_STRUCT(FDeviceEncodingOnlyParameters, )
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
SHADER_PARAMETER_STRUCT_REF(FWorkingColorSpaceShaderParameters, WorkingColorSpace)
SHADER_PARAMETER_STRUCT_INCLUDE(FDeviceEncodingOnlyOutputDeviceParameters, OutputDevice)
SHADER_PARAMETER_STRUCT(FScreenPassTextureViewportParameters, Color)
SHADER_PARAMETER_STRUCT(FScreenPassTextureViewportParameters, Output)
SHADER_PARAMETER_STRUCT_INCLUDE(FDeviceEncodingACESTonemapShaderParameters, ACESTonemapParameters)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, ColorTexture)
SHADER_PARAMETER_SAMPLER(SamplerState, ColorSampler)
SHADER_PARAMETER(float, EditorNITLevel)
END_SHADER_PARAMETER_STRUCT()
class FDeviceEncodingOnlyPS : public FGlobalShader
{
public:
DECLARE_GLOBAL_SHADER(FDeviceEncodingOnlyPS);
SHADER_USE_PARAMETER_STRUCT(FDeviceEncodingOnlyPS, FGlobalShader);
using FPermutationDomain = DeviceEncodingOnlyPermutation::FDesktopDomain;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_INCLUDE(FDeviceEncodingOnlyParameters, DeviceEncoding)
RENDER_TARGET_BINDING_SLOTS()
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return true;
}
};
IMPLEMENT_GLOBAL_SHADER(FDeviceEncodingOnlyPS, "/Engine/Private/PostProcessDeviceEncodingOnly.usf", "MainPS", SF_Pixel);
class FDeviceEncodingOnlyCS : public FGlobalShader
{
public:
DECLARE_GLOBAL_SHADER(FDeviceEncodingOnlyCS);
SHADER_USE_PARAMETER_STRUCT(FDeviceEncodingOnlyCS, FGlobalShader);
using FPermutationDomain = TShaderPermutationDomain<DeviceEncodingOnlyPermutation::FDesktopDomain>;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_INCLUDE(FDeviceEncodingOnlyParameters, DeviceEncoding)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, RWOutputTexture)
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
if (!IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5))
{
return false;
}
return true;
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEX"), GDeviceEncodingOnlyComputeTileSizeX);
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEY"), GDeviceEncodingOnlyComputeTileSizeY);
}
};
IMPLEMENT_GLOBAL_SHADER(FDeviceEncodingOnlyCS, "/Engine/Private/PostProcessDeviceEncodingOnly.usf", "MainCS", SF_Compute);
FScreenPassTexture AddDeviceEncodingOnlyPass(FRDGBuilder& GraphBuilder, const FViewInfo& View, const FDeviceEncodingOnlyInputs& Inputs)
{
check(Inputs.SceneColor.IsValid());
const FSceneViewFamily& ViewFamily = *(View.Family);
const FPostProcessSettings& PostProcessSettings = View.FinalPostProcessSettings;
const FScreenPassTextureViewport SceneColorViewport(Inputs.SceneColor);
FScreenPassRenderTarget Output = Inputs.OverrideOutput;
if (!Output.IsValid())
{
FRDGTextureDesc OutputDesc = Inputs.SceneColor.Texture->Desc;
OutputDesc.Reset();
// RGB is the color in LDR, A is the luminance for PostprocessAA
OutputDesc.Format = Inputs.bOutputInHDR ? GRHIHDRDisplayOutputFormat : PF_B8G8R8A8;
OutputDesc.ClearValue = FClearValueBinding(FLinearColor(0, 0, 0, 0));
OutputDesc.Flags |= View.bUseComputePasses ? TexCreate_UAV : TexCreate_RenderTargetable;
OutputDesc.Flags |= GFastVRamConfig.Tonemap;
const FDeviceEncodingOnlyOutputDeviceParameters OutputDeviceParameters = GetDeviceEncodingOnlyOutputDeviceParameters(*View.Family);
const EDisplayOutputFormat OutputDevice = static_cast<EDisplayOutputFormat>(OutputDeviceParameters.OutputDevice);
if (OutputDevice == EDisplayOutputFormat::HDR_LinearEXR)
{
OutputDesc.Format = PF_A32B32G32R32F;
}
if (OutputDevice == EDisplayOutputFormat::HDR_LinearNoToneCurve || OutputDevice == EDisplayOutputFormat::HDR_LinearWithToneCurve)
{
OutputDesc.Format = PF_FloatRGBA;
}
Output = FScreenPassRenderTarget(
GraphBuilder.CreateTexture(OutputDesc, TEXT("DeviceEncodingOnly")),
Inputs.SceneColor.ViewRect,
View.GetOverwriteLoadAction());
}
const FScreenPassTextureViewport OutputViewport(Output);
FRHISamplerState* BilinearClampSampler = TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
FRHISamplerState* PointClampSampler = TStaticSamplerState<SF_Point, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
float EditorNITLevel = 160.0f;
#if WITH_EDITOR
{
static auto CVarHDRNITLevel = IConsoleManager::Get().FindConsoleVariable(TEXT("Editor.HDRNITLevel"));
if (CVarHDRNITLevel)
{
EditorNITLevel = CVarHDRNITLevel->GetFloat();
}
}
#endif
FDeviceEncodingOnlyParameters CommonParameters;
CommonParameters.View = View.ViewUniformBuffer;
CommonParameters.WorkingColorSpace = GDefaultWorkingColorSpaceUniformBuffer.GetUniformBufferRef();
CommonParameters.OutputDevice = GetDeviceEncodingOnlyOutputDeviceParameters(ViewFamily);
CommonParameters.Color = GetScreenPassTextureViewportParameters(SceneColorViewport);
CommonParameters.Output = GetScreenPassTextureViewportParameters(OutputViewport);
GetACESTonemapParameters(CommonParameters.ACESTonemapParameters);
UE::Color::ACES::GetTransformResources(
GraphBuilder,
HDRGetDisplayMaximumLuminance(),
CommonParameters.ACESTonemapParameters.ACESReachTable,
CommonParameters.ACESTonemapParameters.ACESGamutTable,
CommonParameters.ACESTonemapParameters.ACESGammaTable
);
CommonParameters.ColorTexture = Inputs.SceneColor.Texture;
CommonParameters.ColorSampler = BilinearClampSampler;
CommonParameters.EditorNITLevel = EditorNITLevel;
// Generate permutation vector for the desktop tonemapper.
DeviceEncodingOnlyPermutation::FDesktopDomain DesktopPermutationVector;
const bool bOutputDeviceSRGB = (CommonParameters.OutputDevice.OutputDevice == (uint32)EDisplayOutputFormat::SDR_sRGB);
DesktopPermutationVector.Set<DeviceEncodingOnlyPermutation::FDeviceEncodingOnlyOutputDeviceSRGB>(bOutputDeviceSRGB);
static const auto CVarAcesVersion = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.HDR.Aces.Version"));
DesktopPermutationVector.Set<DeviceEncodingOnlyPermutation::FDeviceEncodingUseACES2>(CVarAcesVersion->GetValueOnRenderThread() > 1);
// Override output might not support UAVs.
const bool bComputePass = (Output.Texture->Desc.Flags & TexCreate_UAV) == TexCreate_UAV ? View.bUseComputePasses : false;
if (bComputePass)
{
FDeviceEncodingOnlyCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FDeviceEncodingOnlyCS::FParameters>();
PassParameters->DeviceEncoding = CommonParameters;
PassParameters->RWOutputTexture = GraphBuilder.CreateUAV(Output.Texture);
FDeviceEncodingOnlyCS::FPermutationDomain PermutationVector;
PermutationVector.Set<DeviceEncodingOnlyPermutation::FDesktopDomain>(DesktopPermutationVector);
TShaderMapRef<FDeviceEncodingOnlyCS> ComputeShader(View.ShaderMap, PermutationVector);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("DeviceEncodingOnly %dx%d (CS)", OutputViewport.Rect.Width(), OutputViewport.Rect.Height()),
ComputeShader,
PassParameters,
FComputeShaderUtils::GetGroupCount(OutputViewport.Rect.Size(), FIntPoint(GDeviceEncodingOnlyComputeTileSizeX, GDeviceEncodingOnlyComputeTileSizeY)));
}
else
{
FDeviceEncodingOnlyPS::FParameters* PassParameters = GraphBuilder.AllocParameters<FDeviceEncodingOnlyPS::FParameters>();
PassParameters->DeviceEncoding = CommonParameters;
PassParameters->RenderTargets[0] = Output.GetRenderTargetBinding();
TShaderMapRef<FScreenPassVS> VertexShader(View.ShaderMap);
TShaderMapRef<FDeviceEncodingOnlyPS> PixelShader(View.ShaderMap, DesktopPermutationVector);
// If this is a stereo view, there's a good chance we need alpha out of the tonemapper
// @todo: Remove this once Oculus fix the bug in their runtime that requires alpha here.
//const bool bIsStereo = IStereoRendering::IsStereoEyeView(View);
//FRHIBlendState* BlendState = bIsStereo ? FScreenPassPipelineState::FDefaultBlendState::GetRHI() : TStaticBlendStateWriteMask<CW_RGB>::GetRHI();
FRHIBlendState* BlendState = TStaticBlendStateWriteMask<CW_RGB>::GetRHI();
FRHIDepthStencilState* DepthStencilState = FScreenPassPipelineState::FDefaultDepthStencilState::GetRHI();
EScreenPassDrawFlags DrawFlags = EScreenPassDrawFlags::AllowHMDHiddenAreaMask;
AddDrawScreenPass(
GraphBuilder,
RDG_EVENT_NAME("DeviceEncodingOnly %dx%d (PS)", OutputViewport.Rect.Width(), OutputViewport.Rect.Height()),
View,
OutputViewport,
SceneColorViewport,
FScreenPassPipelineState(VertexShader, PixelShader, BlendState, DepthStencilState),
PassParameters,
DrawFlags,
[PixelShader, PassParameters](FRHICommandList& RHICmdList)
{
SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), *PassParameters);
});
}
return MoveTemp(Output);
}