From 8633cdcfc2b542b70315bec09e2c8f50d4abc0fe Mon Sep 17 00:00:00 2001 From: Nico de Poel Date: Wed, 22 Feb 2023 17:16:30 +0100 Subject: [PATCH] Initial implementation of Reconstruct & Dilate pass. Got a pretty decent way of dealing with double-buffered resources. Uploading Lanczos table in R16_SFloat format using pre-normalized values. Fixed frametime delta being converted improperly. --- Assets/Scripts/Fsr2.cs | 2 +- Assets/Scripts/Fsr2Context.cs | 51 +++++++++++++++++------------ Assets/Scripts/Fsr2Pipeline.cs | 59 +++++++++++++++++++++------------- 3 files changed, 68 insertions(+), 44 deletions(-) diff --git a/Assets/Scripts/Fsr2.cs b/Assets/Scripts/Fsr2.cs index 5fb654a..d290b27 100644 --- a/Assets/Scripts/Fsr2.cs +++ b/Assets/Scripts/Fsr2.cs @@ -142,7 +142,7 @@ namespace FidelityFX public Vector2Int RenderSize; public bool EnableSharpening; public float Sharpness; - public float FrameTimeDelta; + public float FrameTimeDelta; // in seconds public float PreExposure; public bool Reset; public float CameraNear; diff --git a/Assets/Scripts/Fsr2Context.cs b/Assets/Scripts/Fsr2Context.cs index 987bfa0..98d8d8b 100644 --- a/Assets/Scripts/Fsr2Context.cs +++ b/Assets/Scripts/Fsr2Context.cs @@ -26,6 +26,7 @@ namespace FidelityFX private Texture2D _lanczosLutResource; private RenderTexture _autoExposureResource; + private readonly RenderTexture[] _dilatedMotionVectorResources = new RenderTexture[2]; private ComputeBuffer _fsr2ConstantsBuffer; private readonly Fsr2.Fsr2Constants[] _fsr2ConstantsArray = { new Fsr2.Fsr2Constants() }; @@ -45,7 +46,7 @@ namespace FidelityFX private bool _firstExecution; private Vector2 _previousJitterOffset; - private uint _resourceFrameIndex; + private int _resourceFrameIndex; public void Create(Fsr2.ContextDescription contextDescription) { @@ -70,12 +71,12 @@ namespace FidelityFX { // Generate the data for the LUT const int lanczos2LutWidth = 128; - short[] lanczos2Weights = new short[lanczos2LutWidth]; + float[] lanczos2Weights = new float[lanczos2LutWidth]; for (int currentLanczosWidthIndex = 0; currentLanczosWidthIndex < lanczos2LutWidth; ++currentLanczosWidthIndex) { float x = 2.0f * currentLanczosWidthIndex / (lanczos2LutWidth - 1); float y = Fsr2.Lanczos2(x); - lanczos2Weights[currentLanczosWidthIndex] = (short)Mathf.Round(y * 32767.0f); + lanczos2Weights[currentLanczosWidthIndex] = y; } // TODO: create resources, i.e. render textures used for intermediate results. @@ -85,14 +86,22 @@ namespace FidelityFX // Unity doesn't do 1D textures so just default to Texture2D // Resource FSR2_LanczosLutData: FFX_RESOURCE_USAGE_READ_ONLY, FFX_SURFACE_FORMAT_R16_SNORM, FFX_RESOURCE_FLAGS_NONE - // TODO FIXME: R16_SNorm not supported? That's weird... This really ought to be a ComputeBuffer, not a Texture2D. Or just use R16_SFloat and upload pre-normalized floats, I guess... - // _lanczosLutResource = new Texture2D(lanczos2LutWidth, 1, GraphicsFormat.R16_SNorm, TextureCreationFlags.None) { name = "FSR2_LanczosLutData" }; - // _lanczosLutResource.SetPixelData(lanczos2Weights, 0); - // _lanczosLutResource.Apply(); + // R16_SNorm textures are not supported by Unity on most platforms, strangely enough. So instead we use R16_SFloat and upload pre-normalized float data. + _lanczosLutResource = new Texture2D(lanczos2LutWidth, 1, GraphicsFormat.R16_SFloat, TextureCreationFlags.None) { name = "FSR2_LanczosLutData" }; + _lanczosLutResource.SetPixelData(lanczos2Weights, 0); + _lanczosLutResource.Apply(); // Resource FSR2_AutoExposure: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R32G32_FLOAT, FFX_RESOURCE_FLAGS_NONE _autoExposureResource = new RenderTexture(1, 1, 1, GraphicsFormat.R32G32_SFloat) { name = "FSR2_AutoExposure", enableRandomWrite = true }; _autoExposureResource.Create(); + + // Resources FSR2_InternalDilatedVelocity1/2: FFX_RESOURCE_USAGE_RENDERTARGET | FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R16G16_FLOAT, FFX_RESOURCE_FLAGS_NONE + for (int i = 0; i < 2; ++i) + { + _dilatedMotionVectorResources[i] = new RenderTexture(_contextDescription.MaxRenderSize.x, _contextDescription.MaxRenderSize.y, 1, GraphicsFormat.R16G16_SFloat) + { name = "FSR2_InternalDilatedVelocity" + (i + 1), enableRandomWrite = true }; + _dilatedMotionVectorResources[i].Create(); + } } // private void InitShaders() @@ -111,7 +120,7 @@ namespace FidelityFX private void CreatePipelines() { _computeLuminancePyramidPipeline = new Fsr2ComputeLuminancePyramidPipeline(_contextDescription, _fsr2ConstantsBuffer, _spdConstantsBuffer, _autoExposureResource); - _reconstructPreviousDepthPipeline = new Fsr2ReconstructPreviousDepthPipeline(_contextDescription, _fsr2ConstantsBuffer); + _reconstructPreviousDepthPipeline = new Fsr2ReconstructPreviousDepthPipeline(_contextDescription, _fsr2ConstantsBuffer, _dilatedMotionVectorResources); _accumulatePipeline = new Fsr2AccumulatePipeline(_contextDescription, _fsr2ConstantsBuffer); _accumulateSharpenPipeline = new Fsr2AccumulateSharpenPipeline(_contextDescription, _fsr2ConstantsBuffer); _rcasPipeline = new Fsr2RcasPipeline(_contextDescription, _fsr2ConstantsBuffer, _rcasConstantsBuffer); @@ -129,6 +138,8 @@ namespace FidelityFX DestroyPipeline(ref _reconstructPreviousDepthPipeline); DestroyPipeline(ref _depthClipPipeline); + DestroyResource(ref _dilatedMotionVectorResources[1]); + DestroyResource(ref _dilatedMotionVectorResources[0]); DestroyResource(ref _autoExposureResource); DestroyResource(ref _lanczosLutResource); @@ -156,7 +167,8 @@ namespace FidelityFX } // TODO: setup resource indices for buffers that get swapped per frame - + int frameIndex = _resourceFrameIndex % 2; + bool resetAccumulation = dispatchParams.Reset || _firstExecution; _firstExecution = false; @@ -187,23 +199,22 @@ namespace FidelityFX _spdConstantsBuffer.SetData(_spdConstantsArray); // Compute luminance pyramid - _computeLuminancePyramidPipeline.ScheduleDispatch(_commandBuffer, dispatchParams, dispatchThreadGroupCount.x, dispatchThreadGroupCount.y); + _computeLuminancePyramidPipeline.ScheduleDispatch(_commandBuffer, dispatchParams, frameIndex, dispatchThreadGroupCount.x, dispatchThreadGroupCount.y); + + // Reconstruct previous depth + _reconstructPreviousDepthPipeline.ScheduleDispatch(_commandBuffer, dispatchParams, frameIndex, dispatchSrcX, dispatchSrcY); - // // Reconstruct previous depth - // _commandBuffer.DispatchCompute(_reconstructPreviousDepthShader, _reconstructPreviousDepthKernel, dispatchSrcX, dispatchSrcY, 1); - // - // // Depth clip + // Depth clip // _commandBuffer.DispatchCompute(_depthClipShader, _depthClipKernel, dispatchSrcX, dispatchSrcY, 1); - // - // // Lock + + // Create locks // _commandBuffer.DispatchCompute(_lockShader, _lockKernel, dispatchSrcX, dispatchSrcY, 1); - // bool sharpenEnabled = dispatchParams.EnableSharpening; // Accumulate var accumulatePipeline = sharpenEnabled ? _accumulateSharpenPipeline : _accumulatePipeline; - accumulatePipeline.ScheduleDispatch(_commandBuffer, dispatchParams, dispatchDstX, dispatchDstY); + accumulatePipeline.ScheduleDispatch(_commandBuffer, dispatchParams, frameIndex, dispatchDstX, dispatchDstY); if (sharpenEnabled) { @@ -215,7 +226,7 @@ namespace FidelityFX const int threadGroupWorkRegionDimRcas = 16; int threadGroupsX = (Screen.width + threadGroupWorkRegionDimRcas - 1) / threadGroupWorkRegionDimRcas; int threadGroupsY = (Screen.height + threadGroupWorkRegionDimRcas - 1) / threadGroupWorkRegionDimRcas; - _rcasPipeline.ScheduleDispatch(_commandBuffer, dispatchParams, threadGroupsX, threadGroupsY); + _rcasPipeline.ScheduleDispatch(_commandBuffer, dispatchParams, frameIndex, threadGroupsX, threadGroupsY); } else { @@ -280,7 +291,7 @@ namespace FidelityFX } // Convert delta time to seconds and clamp to [0, 1] - constants.deltaTime = Mathf.Clamp01(dispatchParams.FrameTimeDelta / 1000.0f); + constants.deltaTime = Mathf.Clamp01(dispatchParams.FrameTimeDelta); if (resetAccumulation) constants.frameIndex = 0; diff --git a/Assets/Scripts/Fsr2Pipeline.cs b/Assets/Scripts/Fsr2Pipeline.cs index 0126fb7..b9b99fe 100644 --- a/Assets/Scripts/Fsr2Pipeline.cs +++ b/Assets/Scripts/Fsr2Pipeline.cs @@ -8,7 +8,7 @@ namespace FidelityFX { internal abstract class Fsr2Pipeline: IDisposable { - internal const int ShadingChangeMipLevel = 4; // Corresponds to FFX_FSR2_SHADING_CHANGE_MIP_LEVEL define + internal const int ShadingChangeMipLevel = 4; // This matches the FFX_FSR2_SHADING_CHANGE_MIP_LEVEL define protected readonly Fsr2.ContextDescription ContextDescription; protected readonly ComputeBuffer Constants; @@ -30,6 +30,10 @@ namespace FidelityFX protected static readonly int UavExposureMip5 = Shader.PropertyToID("rw_img_mip_5"); protected static readonly int UavAutoExposure = Shader.PropertyToID("rw_auto_exposure"); protected static readonly int UavSpdAtomicCount = Shader.PropertyToID("rw_spd_global_atomic"); + protected static readonly int UavReconstructedPrevNearestDepth = Shader.PropertyToID("rw_reconstructed_previous_nearest_depth"); + protected static readonly int UavDilatedMotionVectors = Shader.PropertyToID("rw_dilated_motion_vectors"); + protected static readonly int UavDilatedDepth = Shader.PropertyToID("rw_dilatedDepth"); + protected static readonly int UavLockInputLuma = Shader.PropertyToID("rw_lock_input_luma"); // Constant buffer bindings protected static readonly int CbFsr2 = Shader.PropertyToID("cbFSR2"); @@ -48,7 +52,7 @@ namespace FidelityFX UnloadComputeShader(); } - public abstract void ScheduleDispatch(CommandBuffer commandBuffer, Fsr2.DispatchDescription dispatchParams, int dispatchX, int dispatchY); + public abstract void ScheduleDispatch(CommandBuffer commandBuffer, Fsr2.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY); public static void RegisterResources(CommandBuffer commandBuffer, Fsr2.ContextDescription contextDescription, Fsr2.DispatchDescription dispatchParams) { @@ -65,6 +69,15 @@ namespace FidelityFX const int lumaMip = ShadingChangeMipLevel + 1; commandBuffer.GetTemporaryRT(UavExposureMipLumaChange, maxRenderSize.x >> lumaMip, maxRenderSize.y >> lumaMip, 0, FilterMode.Point, GraphicsFormat.R16_SFloat, 1, true); commandBuffer.GetTemporaryRT(UavExposureMip5, maxRenderSize.x >> 6, maxRenderSize.y >> 6, 0, FilterMode.Point, GraphicsFormat.R16_SFloat, 1, true); + + // FSR2_ReconstructedPrevNearestDepth: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R32_UINT, FFX_RESOURCE_FLAGS_ALIASABLE + commandBuffer.GetTemporaryRT(UavReconstructedPrevNearestDepth, maxRenderSize.x, maxRenderSize.y, 0, FilterMode.Point, GraphicsFormat.R32_UInt, 1, true); + + // FSR2_DilatedDepth: FFX_RESOURCE_USAGE_RENDERTARGET | FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R32_FLOAT, FFX_RESOURCE_FLAGS_ALIASABLE + commandBuffer.GetTemporaryRT(UavDilatedDepth, maxRenderSize.x, maxRenderSize.y, 0, FilterMode.Point, GraphicsFormat.R32_SFloat, 1, true); + + // FSR2_LockInputLuma: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R16_FLOAT, FFX_RESOURCE_FLAGS_ALIASABLE + commandBuffer.GetTemporaryRT(UavLockInputLuma, displaySize.x, displaySize.y, 0, FilterMode.Point, GraphicsFormat.R16_SFloat, 1, true); } public static void UnregisterResources(CommandBuffer commandBuffer) @@ -73,6 +86,9 @@ namespace FidelityFX commandBuffer.ReleaseTemporaryRT(UavSpdAtomicCount); commandBuffer.ReleaseTemporaryRT(UavExposureMipLumaChange); commandBuffer.ReleaseTemporaryRT(UavExposureMip5); + commandBuffer.ReleaseTemporaryRT(UavReconstructedPrevNearestDepth); + commandBuffer.ReleaseTemporaryRT(UavDilatedDepth); + commandBuffer.ReleaseTemporaryRT(UavLockInputLuma); } protected void LoadComputeShader(string name) @@ -127,7 +143,7 @@ namespace FidelityFX LoadComputeShader("FSR2/ffx_fsr2_compute_luminance_pyramid_pass"); } - public override void ScheduleDispatch(CommandBuffer commandBuffer, Fsr2.DispatchDescription dispatchParams, int dispatchX, int dispatchY) + public override void ScheduleDispatch(CommandBuffer commandBuffer, Fsr2.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) { // Problems to solve: // - How do resources (render textures) relate to SRV/UAV bindings? How are those tied together? @@ -135,7 +151,7 @@ namespace FidelityFX // - How do we clear the resources that need to be cleared at dispatch? (SetBufferData) // - Shouldn't we use a ComputeBuffer for resources that are one-dimensional and clearly not image data? e.g. SPD atomic counter & Lanczos LUT data - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, SrvInputColor, dispatchParams.Input); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, SrvInputColor, new RenderTargetIdentifier(BuiltinRenderTextureType.CurrentActive)); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, UavAutoExposure, _autoExposure); commandBuffer.SetComputeConstantBufferParam(ComputeShader, CbFsr2, Constants, 0, Marshal.SizeOf()); commandBuffer.SetComputeConstantBufferParam(ComputeShader, CbSpd, _spdConstants, 0, Marshal.SizeOf()); @@ -146,27 +162,24 @@ namespace FidelityFX internal class Fsr2ReconstructPreviousDepthPipeline : Fsr2Pipeline { - public Fsr2ReconstructPreviousDepthPipeline(Fsr2.ContextDescription contextDescription, ComputeBuffer constants) + private readonly RenderTexture[] _dilatedMotionVectors; + + public Fsr2ReconstructPreviousDepthPipeline(Fsr2.ContextDescription contextDescription, ComputeBuffer constants, RenderTexture[] dilatedMotionVectors) : base(contextDescription, constants) { + _dilatedMotionVectors = dilatedMotionVectors; + LoadComputeShader("FSR2/ffx_fsr2_reconstruct_previous_depth_pass"); } - public override void ScheduleDispatch(CommandBuffer commandBuffer, Fsr2.DispatchDescription dispatchParams, int dispatchX, int dispatchY) + public override void ScheduleDispatch(CommandBuffer commandBuffer, Fsr2.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) { - /* -#define FSR2_BIND_SRV_INPUT_MOTION_VECTORS 0 -#define FSR2_BIND_SRV_INPUT_DEPTH 1 -#define FSR2_BIND_SRV_INPUT_COLOR 2 -#define FSR2_BIND_SRV_INPUT_EXPOSURE 3 - -#define FSR2_BIND_UAV_RECONSTRUCTED_PREV_NEAREST_DEPTH 0 -#define FSR2_BIND_UAV_DILATED_MOTION_VECTORS 1 -#define FSR2_BIND_UAV_DILATED_DEPTH 2 -#define FSR2_BIND_UAV_LOCK_INPUT_LUMA 3 - -#define FSR2_BIND_CB_FSR2 0 - */ + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, SrvInputMotionVectors, new RenderTargetIdentifier(BuiltinRenderTextureType.MotionVectors)); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, SrvInputDepth, new RenderTargetIdentifier(BuiltinRenderTextureType.Depth)); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, SrvInputColor, new RenderTargetIdentifier(BuiltinRenderTextureType.CurrentActive)); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, SrvInputExposure, dispatchParams.Exposure); + + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, UavDilatedMotionVectors, _dilatedMotionVectors[frameIndex]); commandBuffer.SetComputeConstantBufferParam(ComputeShader, CbFsr2, Constants, 0, Marshal.SizeOf()); @@ -182,7 +195,7 @@ namespace FidelityFX LoadComputeShader("FSR2/ffx_fsr2_accumulate_pass"); } - public override void ScheduleDispatch(CommandBuffer commandBuffer, Fsr2.DispatchDescription dispatchParams, int dispatchX, int dispatchY) + public override void ScheduleDispatch(CommandBuffer commandBuffer, Fsr2.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) { //throw new NotImplementedException(); } @@ -205,12 +218,12 @@ namespace FidelityFX _shaderCopy.EnableKeyword("FFX_FSR2_OPTION_APPLY_SHARPENING"); } - public override void ScheduleDispatch(CommandBuffer commandBuffer, Fsr2.DispatchDescription dispatchParams, int dispatchX, int dispatchY) + public override void ScheduleDispatch(CommandBuffer commandBuffer, Fsr2.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) { // Temporarily swap around the shaders so that the dispatch will bind and execute the correct one ComputeShader tmp = ComputeShader; ComputeShader = _shaderCopy; - base.ScheduleDispatch(commandBuffer, dispatchParams, dispatchX, dispatchY); + base.ScheduleDispatch(commandBuffer, dispatchParams, frameIndex, dispatchX, dispatchY); ComputeShader = tmp; } @@ -234,7 +247,7 @@ namespace FidelityFX LoadComputeShader("FSR2/ffx_fsr2_rcas_pass"); } - public override void ScheduleDispatch(CommandBuffer commandBuffer, Fsr2.DispatchDescription dispatchParams, int dispatchX, int dispatchY) + public override void ScheduleDispatch(CommandBuffer commandBuffer, Fsr2.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) { // Run the RCAS sharpening filter on the upscaled image commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, SrvInputExposure, dispatchParams.Exposure);