Browse Source

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.
mac-autoexp
Nico de Poel 3 years ago
parent
commit
8633cdcfc2
  1. 2
      Assets/Scripts/Fsr2.cs
  2. 51
      Assets/Scripts/Fsr2Context.cs
  3. 59
      Assets/Scripts/Fsr2Pipeline.cs

2
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;

51
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;

59
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<Fsr2.Fsr2Constants>());
commandBuffer.SetComputeConstantBufferParam(ComputeShader, CbSpd, _spdConstants, 0, Marshal.SizeOf<Fsr2.SpdConstants>());
@ -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<Fsr2.Fsr2Constants>());
@ -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);

Loading…
Cancel
Save