427 lines
23 KiB
C#
427 lines
23 KiB
C#
|
|
using System;
|
||
|
|
using UnityEngine.Experimental.Rendering;
|
||
|
|
using UnityEngine.Rendering.RenderGraphModule;
|
||
|
|
using System.Runtime.CompilerServices; // AggressiveInlining
|
||
|
|
|
||
|
|
namespace UnityEngine.Rendering.Universal
|
||
|
|
{
|
||
|
|
internal sealed class PostProcess : IDisposable
|
||
|
|
{
|
||
|
|
// Passes
|
||
|
|
StopNanPostProcessPass m_StopNanPostProcessPass;
|
||
|
|
SmaaPostProcessPass m_SmaaPostProcessPass;
|
||
|
|
DepthOfFieldGaussianPostProcessPass m_DepthOfFieldGaussianPass;
|
||
|
|
DepthOfFieldBokehPostProcessPass m_DepthOfFieldBokehPass;
|
||
|
|
UpscalerPostProcessPass m_UpscalerPostProcessPass;
|
||
|
|
StpPostProcessPass m_StpPostProcessPass;
|
||
|
|
TemporalAntiAliasingPostProcessPass m_TemporalAntiAliasingPass;
|
||
|
|
MotionBlurPostProcessPass m_MotionBlurPass;
|
||
|
|
PaniniProjectionPostProcessPass m_PaniniProjectionPass;
|
||
|
|
BloomPostProcessPass m_BloomPass;
|
||
|
|
LensFlareScreenSpacePostProcessPass m_LensFlareScreenSpacePass;
|
||
|
|
LensFlareDataDrivenPostProcessPass m_LensFlareDataDrivenPass;
|
||
|
|
UberPostProcessPass m_UberPass;
|
||
|
|
|
||
|
|
// Final Passes
|
||
|
|
ScalingSetupPostProcessPass m_ScalingSetupFinalPostProcessPass;
|
||
|
|
Fsr1UpscalePostProcessPass m_Fsr1UpscaleFinalPostProcessPass;
|
||
|
|
FinalPostProcessPass m_FinalPostProcessPass;
|
||
|
|
|
||
|
|
PostProcessData m_Resources;
|
||
|
|
|
||
|
|
int m_DitheringTextureIndex; // 8-bit dithering
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Creates a new <c>PostProcessPass</c> instance.
|
||
|
|
/// </summary>
|
||
|
|
/// <param name="postProcessResourceAssetData">The <c>PostProcessData</c> resources to use.</param>
|
||
|
|
/// <param name="requestPostProColorFormat">Requested <c>GraphicsFormat</c> for postprocess rendering.</param>
|
||
|
|
/// <seealso cref="RenderPassEvent"/>
|
||
|
|
/// <seealso cref="PostProcessData"/>
|
||
|
|
/// <seealso cref="PostProcessParams"/>
|
||
|
|
/// <seealso cref="GraphicsFormat"/>
|
||
|
|
public PostProcess(PostProcessData postProcessResourceAssetData)
|
||
|
|
{
|
||
|
|
Assertions.Assert.IsNotNull(postProcessResourceAssetData, "PostProcessData and resources cannot be null.");
|
||
|
|
m_Resources = postProcessResourceAssetData;
|
||
|
|
|
||
|
|
m_StopNanPostProcessPass = new StopNanPostProcessPass(m_Resources.shaders.stopNanPS);
|
||
|
|
m_SmaaPostProcessPass = new SmaaPostProcessPass(m_Resources.shaders.subpixelMorphologicalAntialiasingPS, m_Resources.textures.smaaAreaTex, m_Resources.textures.smaaSearchTex);
|
||
|
|
m_DepthOfFieldGaussianPass = new DepthOfFieldGaussianPostProcessPass(m_Resources.shaders.gaussianDepthOfFieldPS);
|
||
|
|
m_DepthOfFieldBokehPass = new DepthOfFieldBokehPostProcessPass(m_Resources.shaders.bokehDepthOfFieldPS);
|
||
|
|
m_UpscalerPostProcessPass = new UpscalerPostProcessPass(m_Resources.textures.blueNoise16LTex);
|
||
|
|
m_StpPostProcessPass = new StpPostProcessPass(m_Resources.textures.blueNoise16LTex);
|
||
|
|
m_TemporalAntiAliasingPass = new TemporalAntiAliasingPostProcessPass(m_Resources.shaders.temporalAntialiasingPS);
|
||
|
|
m_MotionBlurPass = new MotionBlurPostProcessPass(m_Resources.shaders.cameraMotionBlurPS);
|
||
|
|
m_PaniniProjectionPass = new PaniniProjectionPostProcessPass(m_Resources.shaders.paniniProjectionPS);
|
||
|
|
m_BloomPass = new BloomPostProcessPass(m_Resources.shaders.bloomPS);
|
||
|
|
m_LensFlareScreenSpacePass = new LensFlareScreenSpacePostProcessPass(m_Resources.shaders.LensFlareScreenSpacePS);
|
||
|
|
m_LensFlareDataDrivenPass = new LensFlareDataDrivenPostProcessPass(m_Resources.shaders.LensFlareDataDrivenPS);
|
||
|
|
m_UberPass = new UberPostProcessPass(m_Resources.shaders.uberPostPS, m_Resources.textures.filmGrainTex);
|
||
|
|
|
||
|
|
// Final post processing.
|
||
|
|
m_ScalingSetupFinalPostProcessPass = new ScalingSetupPostProcessPass(m_Resources.shaders.scalingSetupPS);
|
||
|
|
m_Fsr1UpscaleFinalPostProcessPass = new Fsr1UpscalePostProcessPass(m_Resources.shaders.easuPS);
|
||
|
|
m_FinalPostProcessPass = new FinalPostProcessPass(m_Resources.shaders.finalPostPassPS, m_Resources.textures.filmGrainTex);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Disposes used resources.
|
||
|
|
/// </summary>
|
||
|
|
public void Dispose()
|
||
|
|
{
|
||
|
|
m_FinalPostProcessPass?.Dispose();
|
||
|
|
m_Fsr1UpscaleFinalPostProcessPass?.Dispose();
|
||
|
|
m_ScalingSetupFinalPostProcessPass?.Dispose();
|
||
|
|
|
||
|
|
m_UberPass?.Dispose();
|
||
|
|
m_LensFlareDataDrivenPass?.Dispose();
|
||
|
|
m_LensFlareScreenSpacePass?.Dispose();
|
||
|
|
m_BloomPass?.Dispose();
|
||
|
|
m_PaniniProjectionPass?.Dispose();
|
||
|
|
m_MotionBlurPass?.Dispose();
|
||
|
|
m_TemporalAntiAliasingPass?.Dispose();
|
||
|
|
m_StpPostProcessPass?.Dispose();
|
||
|
|
m_UpscalerPostProcessPass.Dispose();
|
||
|
|
m_DepthOfFieldBokehPass?.Dispose();
|
||
|
|
m_DepthOfFieldGaussianPass?.Dispose();
|
||
|
|
m_SmaaPostProcessPass?.Dispose();
|
||
|
|
m_StopNanPostProcessPass?.Dispose();
|
||
|
|
}
|
||
|
|
|
||
|
|
// Some Android devices do not support sRGB backbuffer
|
||
|
|
// We need to do the conversion manually on those
|
||
|
|
// Also if HDR output is active
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
static bool RequireSRGBConversionBlitToBackBuffer(UniversalCameraData cameraData, bool enableColorEncodingIfNeeded)
|
||
|
|
{
|
||
|
|
return cameraData.requireSrgbConversion && enableColorEncodingIfNeeded;
|
||
|
|
}
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
int GetNextDitherIndex()
|
||
|
|
{
|
||
|
|
#if LWRP_DEBUG_STATIC_POSTFX // Used by QA for automated testing
|
||
|
|
return 0;
|
||
|
|
#else
|
||
|
|
if (++m_DitheringTextureIndex >= m_Resources.textures.blueNoise16LTex.Length)
|
||
|
|
m_DitheringTextureIndex = 0;
|
||
|
|
return m_DitheringTextureIndex;
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
Texture2D GetNextDitherTexture()
|
||
|
|
{
|
||
|
|
return m_Resources.textures.blueNoise16LTex[GetNextDitherIndex()];
|
||
|
|
}
|
||
|
|
|
||
|
|
static void UpdateGlobalDebugHandlerPass(RenderGraph renderGraph, UniversalCameraData cameraData, bool isFinalPass)
|
||
|
|
{
|
||
|
|
// NOTE: Debug handling injects a global state render pass.
|
||
|
|
DebugHandler debugHandler = ScriptableRenderPass.GetActiveDebugHandler(cameraData);
|
||
|
|
bool resolveToDebugScreen = debugHandler != null && debugHandler.WriteToDebugScreenTexture(cameraData.resolveFinalTarget);
|
||
|
|
debugHandler?.UpdateShaderGlobalPropertiesForFinalValidationPass(renderGraph, cameraData, isFinalPass && !resolveToDebugScreen);
|
||
|
|
}
|
||
|
|
|
||
|
|
// If hasFinalPass == true, Film Grain and Dithering are setup in the final pass, otherwise they are setup in this pass.
|
||
|
|
public void RenderPostProcessing(RenderGraph renderGraph, ContextContainer frameData, bool hasFinalPass, bool enableColorEncodingIfNeeded)
|
||
|
|
{
|
||
|
|
var resourceData = frameData.Get<UniversalResourceData>();
|
||
|
|
var cameraData = frameData.Get<UniversalCameraData>();
|
||
|
|
var postProcessingData = frameData.Get<UniversalPostProcessingData>();
|
||
|
|
|
||
|
|
var stack = VolumeManager.instance.stack;
|
||
|
|
var depthOfField = stack.GetComponent<DepthOfField>();
|
||
|
|
var motionBlur = stack.GetComponent<MotionBlur>();
|
||
|
|
var paniniProjection = stack.GetComponent<PaniniProjection>();
|
||
|
|
var bloom = stack.GetComponent<Bloom>();
|
||
|
|
var lensFlareScreenSpace = stack.GetComponent<ScreenSpaceLensFlare>();
|
||
|
|
|
||
|
|
bool useFastSRGBLinearConversion = postProcessingData.useFastSRGBLinearConversion;
|
||
|
|
bool supportDataDrivenLensFlare = postProcessingData.supportDataDrivenLensFlare;
|
||
|
|
bool supportScreenSpaceLensFlare = postProcessingData.supportScreenSpaceLensFlare;
|
||
|
|
|
||
|
|
bool isSceneViewCamera = cameraData.isSceneViewCamera;
|
||
|
|
|
||
|
|
//We blit back and forth without msaa untill the last blit.
|
||
|
|
bool useStopNan = cameraData.isStopNaNEnabled;
|
||
|
|
bool useSubPixelMorpAA = (cameraData.antialiasing == AntialiasingMode.SubpixelMorphologicalAntiAliasing);
|
||
|
|
bool useDepthOfField = depthOfField.IsActive() && !isSceneViewCamera;
|
||
|
|
bool useLensFlare = !LensFlareCommonSRP.Instance.IsEmpty() && supportDataDrivenLensFlare;
|
||
|
|
bool useLensFlareScreenSpace = lensFlareScreenSpace.IsActive() && supportScreenSpaceLensFlare;
|
||
|
|
bool useMotionBlur = motionBlur.IsActive() && !isSceneViewCamera;
|
||
|
|
bool usePaniniProjection = paniniProjection.IsActive() && !isSceneViewCamera;
|
||
|
|
|
||
|
|
// Disable MotionBlur in EditMode, so that editing remains clear and readable.
|
||
|
|
// NOTE: HDRP does the same via CoreUtils::AreAnimatedMaterialsEnabled().
|
||
|
|
// Disable MotionBlurMode.CameraAndObjects on renderers that do not support motion vectors
|
||
|
|
useMotionBlur = useMotionBlur && Application.isPlaying;
|
||
|
|
if (useMotionBlur && motionBlur.mode.value == MotionBlurMode.CameraAndObjects)
|
||
|
|
{
|
||
|
|
ScriptableRenderer renderer = cameraData.renderer;
|
||
|
|
useMotionBlur &= renderer.SupportsMotionVectors();
|
||
|
|
if (!useMotionBlur)
|
||
|
|
{
|
||
|
|
var warning = "Disabling Motion Blur for Camera And Objects because the renderer does not implement motion vectors.";
|
||
|
|
const int warningThrottleFrames = 60 * 1; // 60 FPS * 1 sec
|
||
|
|
if (Time.frameCount % warningThrottleFrames == 0)
|
||
|
|
Debug.LogWarning(warning);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Note that enabling jitters uses the same CameraData::IsTemporalAAEnabled(). So if we add any other kind of overrides (like
|
||
|
|
// disable useTemporalAA if another feature is disabled) then we need to put it in CameraData::IsTemporalAAEnabled() as opposed
|
||
|
|
// to tweaking the value here.
|
||
|
|
bool useTemporalAA = cameraData.IsTemporalAAEnabled() && m_TemporalAntiAliasingPass.IsValid();
|
||
|
|
|
||
|
|
// STP is only enabled when TAA is enabled and all of its runtime requirements are met.
|
||
|
|
// Using IsSTPRequested() vs IsSTPEnabled() for perf reason here, as we already know TAA status
|
||
|
|
bool isSTPRequested = cameraData.IsSTPRequested();
|
||
|
|
bool useSTP = useTemporalAA && isSTPRequested;
|
||
|
|
|
||
|
|
// Warn users if TAA and STP are disabled despite being requested
|
||
|
|
if (!useTemporalAA && cameraData.IsTemporalAARequested())
|
||
|
|
TemporalAA.ValidateAndWarn(cameraData, isSTPRequested);
|
||
|
|
|
||
|
|
// NOTE: Debug handling injects a global state render pass.
|
||
|
|
UpdateGlobalDebugHandlerPass(renderGraph, cameraData, !hasFinalPass);
|
||
|
|
|
||
|
|
var colorSourceDesc = resourceData.cameraColor.GetDescriptor(renderGraph);
|
||
|
|
|
||
|
|
// Optional NaN killer before post-processing kicks in
|
||
|
|
// stopNaN may be null on Adreno 3xx. It doesn't support full shader level 3.5, but SystemInfo.graphicsShaderLevel is 35.
|
||
|
|
if (useStopNan)
|
||
|
|
{
|
||
|
|
m_StopNanPostProcessPass.RecordRenderGraph(renderGraph, frameData);
|
||
|
|
}
|
||
|
|
|
||
|
|
if(useSubPixelMorpAA)
|
||
|
|
{
|
||
|
|
m_SmaaPostProcessPass.Setup(cameraData.antialiasingQuality);
|
||
|
|
m_SmaaPostProcessPass.RecordRenderGraph(renderGraph, frameData);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Depth of Field
|
||
|
|
// Adreno 3xx SystemInfo.graphicsShaderLevel is 35, but instancing support is disabled due to buggy drivers.
|
||
|
|
// DOF shader uses #pragma target 3.5 which adds requirement for instancing support, thus marking the shader unsupported on those devices.
|
||
|
|
if (useDepthOfField)
|
||
|
|
{
|
||
|
|
if(depthOfField.mode.value == DepthOfFieldMode.Gaussian)
|
||
|
|
{
|
||
|
|
m_DepthOfFieldGaussianPass.RecordRenderGraph(renderGraph, frameData);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
m_DepthOfFieldBokehPass.Setup(useFastSRGBLinearConversion);
|
||
|
|
m_DepthOfFieldBokehPass.RecordRenderGraph(renderGraph, frameData);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Temporal Anti Aliasing / Upscaling
|
||
|
|
|
||
|
|
if (useTemporalAA)
|
||
|
|
{
|
||
|
|
#if ENABLE_UPSCALER_FRAMEWORK
|
||
|
|
if (postProcessingData.activeUpscaler != null)
|
||
|
|
{
|
||
|
|
m_UpscalerPostProcessPass.RecordRenderGraph(renderGraph, frameData);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
#endif
|
||
|
|
if (useSTP)
|
||
|
|
{
|
||
|
|
m_StpPostProcessPass.RecordRenderGraph(renderGraph, frameData);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
m_TemporalAntiAliasingPass.RecordRenderGraph(renderGraph, frameData);
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
if(useMotionBlur)
|
||
|
|
{
|
||
|
|
m_MotionBlurPass.RecordRenderGraph(renderGraph, frameData);
|
||
|
|
}
|
||
|
|
|
||
|
|
if(usePaniniProjection)
|
||
|
|
{
|
||
|
|
m_PaniniProjectionPass.RecordRenderGraph(renderGraph, frameData);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Uberpost
|
||
|
|
{
|
||
|
|
// Bloom goes first
|
||
|
|
bool bloomActive = bloom.IsActive() || useLensFlareScreenSpace;
|
||
|
|
|
||
|
|
//Even if bloom is not active we need the texture if the lensFlareScreenSpace pass is active.
|
||
|
|
if (bloomActive)
|
||
|
|
{
|
||
|
|
// NOTE: bloom destination texture is some texture in the bloom mip pyramid. It's not explicitly set beforehand.
|
||
|
|
m_BloomPass.RecordRenderGraph(renderGraph, frameData);
|
||
|
|
|
||
|
|
if (useLensFlareScreenSpace)
|
||
|
|
{
|
||
|
|
var mipPyramid = m_BloomPass.mipPyramid;
|
||
|
|
|
||
|
|
// TODO: Bloom pass could compute the flare source resource.
|
||
|
|
// We need to take into account how many valid mips the bloom pass produced.
|
||
|
|
int bloomMipCount = mipPyramid.mipCount;
|
||
|
|
int bloomMipMax = Mathf.Clamp(bloomMipCount - 1, 0, bloom.maxIterations.value / 2);
|
||
|
|
int bloomMipIndex = Mathf.Clamp(lensFlareScreenSpace.bloomMip.value, 0, bloomMipMax);
|
||
|
|
|
||
|
|
var prevCameraColor = resourceData.cameraColor;
|
||
|
|
resourceData.cameraColor = mipPyramid.GetResultMip(bloomMipIndex);;
|
||
|
|
|
||
|
|
m_LensFlareScreenSpacePass.Setup(colorSourceDesc.width, colorSourceDesc.height, bloomMipIndex);
|
||
|
|
m_LensFlareScreenSpacePass.RecordRenderGraph(renderGraph, frameData);
|
||
|
|
|
||
|
|
resourceData.cameraColor = prevCameraColor;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (useLensFlare)
|
||
|
|
{
|
||
|
|
// Lens Flares are procedurally generated and blended to the destination texture.
|
||
|
|
m_LensFlareDataDrivenPass.RecordRenderGraph(renderGraph, frameData);
|
||
|
|
}
|
||
|
|
|
||
|
|
var ditherTexture = cameraData.isDitheringEnabled ? GetNextDitherTexture() : null;
|
||
|
|
var hdrOperations = HDROutputUtils.Operation.None;
|
||
|
|
var applySrgbEncoding = RequireSRGBConversionBlitToBackBuffer(cameraData, enableColorEncodingIfNeeded);
|
||
|
|
|
||
|
|
bool requireHDROutput = PostProcessUtils.RequireHDROutput(cameraData);
|
||
|
|
if (requireHDROutput)
|
||
|
|
{
|
||
|
|
// Color space conversion is already applied through color grading, do encoding if uber post is the last pass
|
||
|
|
// Otherwise encoding will happen in the final post process pass or the final blit pass
|
||
|
|
hdrOperations = !hasFinalPass && enableColorEncodingIfNeeded ? HDROutputUtils.Operation.ColorEncoding : HDROutputUtils.Operation.None;
|
||
|
|
}
|
||
|
|
|
||
|
|
UberPostProcessPass.FilteringOperation filteringOperation = UberPostProcessPass.FilteringOperation.Linear;
|
||
|
|
|
||
|
|
// Point sampling is only used for upscaling so the default linear sampler should be used if there is a final pass
|
||
|
|
if (cameraData.imageScalingMode == ImageScalingMode.Upscaling && !hasFinalPass && cameraData.upscalingFilter == ImageUpscalingFilter.Point)
|
||
|
|
{
|
||
|
|
filteringOperation = UberPostProcessPass.FilteringOperation.Point;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool renderOverlayUI = requireHDROutput && enableColorEncodingIfNeeded && resourceData.overlayUITexture.IsValid();
|
||
|
|
m_UberPass.Setup(ditherTexture, filteringOperation, hdrOperations, applySrgbEncoding, useFastSRGBLinearConversion, !hasFinalPass, renderOverlayUI);
|
||
|
|
m_UberPass.RecordRenderGraph(renderGraph, frameData);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#region FinalPass
|
||
|
|
public void RenderFinalPostProcessing(RenderGraph renderGraph, ContextContainer frameData, bool enableColorEncodingIfNeeded)
|
||
|
|
{
|
||
|
|
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
|
||
|
|
|
||
|
|
var stack = VolumeManager.instance.stack;
|
||
|
|
|
||
|
|
// NOTE: Debug handling injects a global state render pass.
|
||
|
|
UpdateGlobalDebugHandlerPass(renderGraph, cameraData, true);
|
||
|
|
|
||
|
|
var resourceData = frameData.Get<UniversalResourceData>();
|
||
|
|
var sourceDesc = renderGraph.GetTextureDesc(resourceData.cameraColor);
|
||
|
|
|
||
|
|
HDROutputUtils.Operation hdrOperations = HDROutputUtils.Operation.None;
|
||
|
|
bool requireHDROutput = PostProcessUtils.RequireHDROutput(cameraData);
|
||
|
|
if (requireHDROutput)
|
||
|
|
{
|
||
|
|
// Final post process pass always does color encoding
|
||
|
|
hdrOperations = enableColorEncodingIfNeeded ? HDROutputUtils.Operation.ColorEncoding : HDROutputUtils.Operation.None;
|
||
|
|
// If the color space conversion wasn't applied by the uber pass, do it here
|
||
|
|
if (!cameraData.postProcessEnabled)
|
||
|
|
hdrOperations |= HDROutputUtils.Operation.ColorConversion;
|
||
|
|
}
|
||
|
|
|
||
|
|
FinalPostProcessPass.FilteringOperation filteringOperation = FinalPostProcessPass.FilteringOperation.Linear;
|
||
|
|
|
||
|
|
// Reuse RCAS pass as an optional standalone post sharpening pass for TAA.
|
||
|
|
// This avoids the cost of EASU and is available for other upscaling options.
|
||
|
|
// If FSR is enabled then FSR settings override the TAA settings and we perform RCAS only once.
|
||
|
|
// If STP is enabled, then TAA sharpening has already been performed inside STP.
|
||
|
|
if(PostProcessUtils.IsTaaSharpeningEnabled(cameraData))
|
||
|
|
filteringOperation = FinalPostProcessPass.FilteringOperation.TaaSharpening;
|
||
|
|
|
||
|
|
bool applyFxaa = PostProcessUtils.IsFxaaEnabled(cameraData);
|
||
|
|
|
||
|
|
if (cameraData.imageScalingMode != ImageScalingMode.None)
|
||
|
|
{
|
||
|
|
// When FXAA is enabled in scaled renders, we execute it in a separate blit since it's not designed to be used in
|
||
|
|
// situations where the input and output resolutions do not match.
|
||
|
|
// When FSR is active, we always need an additional pass since it has a very particular color encoding requirement.
|
||
|
|
|
||
|
|
// NOTE: An ideal implementation could inline this color conversion logic into the UberPost pass, but the current code structure would make
|
||
|
|
// this process very complex. Specifically, we'd need to guarantee that the uber post output is always written to a UNORM format render
|
||
|
|
// target in order to preserve the precision of specially encoded color data.
|
||
|
|
bool isSetupRequired = (applyFxaa || PostProcessUtils.IsFsrEnabled(cameraData));
|
||
|
|
|
||
|
|
// When FXAA is needed while scaling is active, we must perform it before the scaling takes place.
|
||
|
|
if (isSetupRequired)
|
||
|
|
{
|
||
|
|
m_ScalingSetupFinalPostProcessPass.Setup(hdrOperations);
|
||
|
|
m_ScalingSetupFinalPostProcessPass.RecordRenderGraph(renderGraph, frameData);
|
||
|
|
|
||
|
|
// Indicate that we no longer need to perform FXAA in the final pass since it was already perfomed here.
|
||
|
|
applyFxaa = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Upscaling (and downscaling)
|
||
|
|
|
||
|
|
var upscaledDesc = sourceDesc;
|
||
|
|
upscaledDesc.width = cameraData.pixelWidth;
|
||
|
|
upscaledDesc.height = cameraData.pixelHeight;
|
||
|
|
// NOTE: upscaledDesc.format != scalingSetupDesc.format (in resourceData.cameraColor)
|
||
|
|
|
||
|
|
switch (cameraData.imageScalingMode)
|
||
|
|
{
|
||
|
|
case ImageScalingMode.Upscaling:
|
||
|
|
{
|
||
|
|
switch (cameraData.upscalingFilter)
|
||
|
|
{
|
||
|
|
case ImageUpscalingFilter.Point:
|
||
|
|
{
|
||
|
|
// TAA post sharpening is an RCAS pass, avoid overriding it with point sampling.
|
||
|
|
if (filteringOperation != FinalPostProcessPass.FilteringOperation.TaaSharpening)
|
||
|
|
filteringOperation = FinalPostProcessPass.FilteringOperation.Point;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case ImageUpscalingFilter.Linear:
|
||
|
|
{
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case ImageUpscalingFilter.FSR:
|
||
|
|
{
|
||
|
|
m_Fsr1UpscaleFinalPostProcessPass.Setup(upscaledDesc);
|
||
|
|
m_Fsr1UpscaleFinalPostProcessPass.RecordRenderGraph(renderGraph, frameData);
|
||
|
|
filteringOperation = FinalPostProcessPass.FilteringOperation.FsrSharpening;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case ImageScalingMode.Downscaling:
|
||
|
|
{
|
||
|
|
// In the downscaling case, we don't perform any sort of filter override logic since we always want linear filtering
|
||
|
|
// and it's already the default option in the shader.
|
||
|
|
|
||
|
|
// Also disable TAA post sharpening pass when downscaling.
|
||
|
|
filteringOperation = FinalPostProcessPass.FilteringOperation.Linear;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
var ditherTexture = cameraData.isDitheringEnabled ? GetNextDitherTexture() : null;
|
||
|
|
bool applySrgbEncoding = RequireSRGBConversionBlitToBackBuffer(cameraData, enableColorEncodingIfNeeded);
|
||
|
|
bool renderOverlayUI = requireHDROutput && enableColorEncodingIfNeeded && cameraData.rendersOverlayUI;
|
||
|
|
m_FinalPostProcessPass.Setup(ditherTexture, filteringOperation, hdrOperations, applySrgbEncoding, applyFxaa, renderOverlayUI);
|
||
|
|
m_FinalPostProcessPass.RecordRenderGraph(renderGraph, frameData);
|
||
|
|
}
|
||
|
|
#endregion
|
||
|
|
}
|
||
|
|
}
|