diff --git a/Assets/Resources/FSR2/ffx_fsr2_accumulate_pass.compute b/Assets/Resources/FSR2/ffx_fsr2_accumulate_pass.compute
index d99637e..6265645 100644
--- a/Assets/Resources/FSR2/ffx_fsr2_accumulate_pass.compute
+++ b/Assets/Resources/FSR2/ffx_fsr2_accumulate_pass.compute
@@ -11,4 +11,10 @@
#define FFX_GPU // Compiling for GPU
#define FFX_HLSL // Compile for plain HLSL
+// Ensure the correct value is defined for this keyword, as it is used to select one of multiple sampler functions
+#ifdef FFX_FSR2_OPTION_REPROJECT_USE_LANCZOS_TYPE
+#undef FFX_FSR2_OPTION_REPROJECT_USE_LANCZOS_TYPE
+#define FFX_FSR2_OPTION_REPROJECT_USE_LANCZOS_TYPE 1
+#endif
+
#include "shaders/ffx_fsr2_accumulate_pass.hlsl"
diff --git a/Assets/Scripts/Fsr2.cs b/Assets/Scripts/Fsr2.cs
index 6ea08ef..5fb654a 100644
--- a/Assets/Scripts/Fsr2.cs
+++ b/Assets/Scripts/Fsr2.cs
@@ -13,7 +13,7 @@ namespace FidelityFX
///
/// Creates a new FSR2 context with standard parameters that are appropriate for the current platform.
///
- public static Fsr2Context CreateContext(InitializationFlags flags = 0)
+ public static Fsr2Context CreateContext(Vector2Int displaySize, Vector2Int maxRenderSize, InitializationFlags flags = 0)
{
if (SystemInfo.usesReversedZBuffer)
flags |= InitializationFlags.EnableDepthInverted;
@@ -23,7 +23,8 @@ namespace FidelityFX
var contextDescription = new ContextDescription
{
Flags = flags,
- DisplaySize = new Vector2Int(Screen.width, Screen.height),
+ DisplaySize = displaySize,
+ MaxRenderSize = maxRenderSize,
Callbacks = GlobalCallbacks,
};
diff --git a/Assets/Scripts/Fsr2Context.cs b/Assets/Scripts/Fsr2Context.cs
index 69eeaa1..de549aa 100644
--- a/Assets/Scripts/Fsr2Context.cs
+++ b/Assets/Scripts/Fsr2Context.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine;
+using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering;
namespace FidelityFX
@@ -23,6 +24,9 @@ namespace FidelityFX
private Fsr2Pipeline _generateReactivePipeline;
private Fsr2Pipeline _tcrAutogeneratePipeline;
+ private Texture2D _lanczosLutResource;
+ private RenderTexture _autoExposureResource;
+
private ComputeBuffer _fsr2ConstantsBuffer;
private readonly Fsr2.Fsr2Constants[] _fsr2ConstantsArray = { new Fsr2.Fsr2Constants() };
private ref Fsr2.Fsr2Constants Constants => ref _fsr2ConstantsArray[0];
@@ -58,10 +62,16 @@ namespace FidelityFX
Constants.displaySize = _contextDescription.DisplaySize;
+ CreateResources();
+ CreatePipelines();
+ }
+
+ private void CreateResources()
+ {
// Generate the data for the LUT
- const uint lanczos2LutWidth = 128;
+ const int lanczos2LutWidth = 128;
short[] lanczos2Weights = new short[lanczos2LutWidth];
- for (uint currentLanczosWidthIndex = 0; currentLanczosWidthIndex < lanczos2LutWidth; ++currentLanczosWidthIndex)
+ for (int currentLanczosWidthIndex = 0; currentLanczosWidthIndex < lanczos2LutWidth; ++currentLanczosWidthIndex)
{
float x = 2.0f * currentLanczosWidthIndex / (lanczos2LutWidth - 1);
float y = Fsr2.Lanczos2(x);
@@ -73,10 +83,18 @@ namespace FidelityFX
// UAVs *may* be an issue with the PS4 not handling simultaneous reading and writing to an RT properly
// Unity does have Graphics.SetRandomWriteTarget for enabling UAV on ComputeBuffers or RTs
// Unity doesn't do 1D textures so just default to Texture2D
-
- CreatePipelines();
- }
+ // 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();
+
+ // 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();
+ }
+
// private void InitShaders()
// {
// LoadComputeShader("FSR2/ffx_fsr2_compute_luminance_pyramid_pass", ref _computeLuminancePyramidShader, out _computeLuminancePyramidKernel);
@@ -92,7 +110,8 @@ namespace FidelityFX
private void CreatePipelines()
{
- _computeLuminancePyramidPipeline = new Fsr2ComputeLuminancePyramidPipeline(_contextDescription.Callbacks, _contextDescription.Flags, _fsr2ConstantsBuffer, _spdConstantsBuffer);
+ _computeLuminancePyramidPipeline =
+ new Fsr2ComputeLuminancePyramidPipeline(_contextDescription.Callbacks, _contextDescription.Flags, _fsr2ConstantsBuffer, _spdConstantsBuffer, _autoExposureResource);
_accumulatePipeline = new Fsr2AccumulatePipeline(_contextDescription.Callbacks, _contextDescription.Flags, _fsr2ConstantsBuffer);
_accumulateSharpenPipeline = new Fsr2AccumulateSharpenPipeline(_contextDescription.Callbacks, _contextDescription.Flags, _fsr2ConstantsBuffer);
_rcasPipeline = new Fsr2RcasPipeline(_contextDescription.Callbacks, _contextDescription.Flags, _fsr2ConstantsBuffer, _rcasConstantsBuffer);
@@ -110,6 +129,9 @@ namespace FidelityFX
DestroyPipeline(ref _reconstructPreviousDepthPipeline);
DestroyPipeline(ref _depthClipPipeline);
+ DestroyResource(ref _autoExposureResource);
+ DestroyResource(ref _lanczosLutResource);
+
DestroyConstantBuffer(ref _rcasConstantsBuffer);
DestroyConstantBuffer(ref _spdConstantsBuffer);
DestroyConstantBuffer(ref _fsr2ConstantsBuffer);
@@ -214,32 +236,24 @@ namespace FidelityFX
dispatchParams.RenderSize.x > 0 ? dispatchParams.RenderSize.x : dispatchParams.Input.width,
dispatchParams.RenderSize.y > 0 ? dispatchParams.RenderSize.y : dispatchParams.Input.height);
constants.maxRenderSize = _contextDescription.MaxRenderSize;
- constants.inputColorResourceDimensions =
- new Vector2Int(dispatchParams.Input.width, dispatchParams.Input.height);
+ constants.inputColorResourceDimensions = new Vector2Int(dispatchParams.Input.width, dispatchParams.Input.height);
// Compute the horizontal FOV for the shader from the vertical one
float aspectRatio = (float)dispatchParams.RenderSize.x / dispatchParams.RenderSize.y;
float cameraAngleHorizontal = Mathf.Atan(Mathf.Tan(dispatchParams.CameraFovAngleVertical / 2.0f) * aspectRatio) * 2.0f;
constants.tanHalfFOV = Mathf.Tan(cameraAngleHorizontal * 0.5f);
- constants.viewSpaceToMetersFactor =
- (dispatchParams.ViewSpaceToMetersFactor > 0.0f) ? dispatchParams.ViewSpaceToMetersFactor : 1.0f;
+ constants.viewSpaceToMetersFactor = (dispatchParams.ViewSpaceToMetersFactor > 0.0f) ? dispatchParams.ViewSpaceToMetersFactor : 1.0f;
// Compute params to enable device depth to view space depth computation in shader
constants.deviceToViewDepth = SetupDeviceDepthToViewSpaceDepthParams(dispatchParams);
// To be updated if resource is larger than the actual image size
- constants.downscaleFactor = new Vector2(
- (float)constants.renderSize.x / _contextDescription.DisplaySize.x,
- (float)constants.renderSize.y / _contextDescription.DisplaySize.y);
+ constants.downscaleFactor = new Vector2((float)constants.renderSize.x / _contextDescription.DisplaySize.x, (float)constants.renderSize.y / _contextDescription.DisplaySize.y);
constants.previousFramePreExposure = constants.preExposure;
constants.preExposure = (dispatchParams.PreExposure != 0) ? dispatchParams.PreExposure : 1.0f;
// Motion vector data
- Vector2Int motionVectorsTargetSize =
- (_contextDescription.Flags & Fsr2.InitializationFlags.EnableDisplayResolutionMotionVectors) != 0
- ? constants.displaySize
- : constants.renderSize;
-
+ Vector2Int motionVectorsTargetSize = (_contextDescription.Flags & Fsr2.InitializationFlags.EnableDisplayResolutionMotionVectors) != 0 ? constants.displaySize : constants.renderSize;
constants.motionVectorScale = dispatchParams.MotionVectorScale / motionVectorsTargetSize;
// Compute jitter cancellation
@@ -302,8 +316,7 @@ namespace FidelityFX
// Revert x and y coords
float aspect = (float)dispatchParams.RenderSize.x / dispatchParams.RenderSize.y;
- float cotHalfFovY = Mathf.Cos(0.5f * dispatchParams.CameraFovAngleVertical) /
- Mathf.Sin(0.5f * dispatchParams.CameraFovAngleVertical);
+ float cotHalfFovY = Mathf.Cos(0.5f * dispatchParams.CameraFovAngleVertical) / Mathf.Sin(0.5f * dispatchParams.CameraFovAngleVertical);
int matrixIndex = (inverted ? 2 : 0) + (infinite ? 1 : 0);
return new Vector4(
@@ -395,6 +408,33 @@ namespace FidelityFX
bufferRef = null;
}
+ private static void DestroyResource(ref ComputeBuffer resource)
+ {
+ if (resource == null)
+ return;
+
+ resource.Release();
+ resource = null;
+ }
+
+ private static void DestroyResource(ref Texture2D resource)
+ {
+ if (resource == null)
+ return;
+
+ UnityEngine.Object.Destroy(resource);
+ resource = null;
+ }
+
+ private static void DestroyResource(ref RenderTexture resource)
+ {
+ if (resource == null)
+ return;
+
+ resource.Release();
+ resource = null;
+ }
+
private static void DestroyPipeline(ref Fsr2Pipeline pipeline)
{
if (pipeline == null)
diff --git a/Assets/Scripts/Fsr2Controller.cs b/Assets/Scripts/Fsr2Controller.cs
index 73096a6..971a8c9 100644
--- a/Assets/Scripts/Fsr2Controller.cs
+++ b/Assets/Scripts/Fsr2Controller.cs
@@ -28,6 +28,9 @@ public class Fsr2Controller : MonoBehaviour
[HideInInspector]
public float renderScale;
+ private Vector2Int DisplaySize => new Vector2Int(Screen.width, Screen.height);
+ private Vector2Int RenderSize => new Vector2Int(Mathf.FloorToInt(Screen.width * renderScale), Mathf.FloorToInt(Screen.height * renderScale));
+
private Fsr2Context _context;
private readonly Fsr2.DispatchDescription _dispatchDescription = new Fsr2.DispatchDescription();
@@ -54,7 +57,8 @@ public class Fsr2Controller : MonoBehaviour
{
RenderPipelineManager.endContextRendering += OnEndContextRendering;
- _context = Fsr2.CreateContext();
+ // TODO: destroy and recreate context on screen resolution and/or quality mode change
+ _context = Fsr2.CreateContext(DisplaySize, RenderSize);
_upscaleRT = new RenderTexture(Screen.width, Screen.height, 24, RenderTextureFormat.ARGBHalf);
_upscaleRT.Create();
@@ -118,6 +122,7 @@ public class Fsr2Controller : MonoBehaviour
_dispatchDescription.Sharpness = sharpness;
_dispatchDescription.MotionVectorScale.x = gameCamera.pixelWidth;
_dispatchDescription.MotionVectorScale.y = gameCamera.pixelHeight;
+ _dispatchDescription.RenderSize = RenderSize;
_dispatchDescription.FrameTimeDelta = Time.unscaledDeltaTime;
_dispatchDescription.CameraNear = gameCamera.nearClipPlane;
_dispatchDescription.CameraFar = gameCamera.farClipPlane;
diff --git a/Assets/Scripts/Fsr2Pipeline.cs b/Assets/Scripts/Fsr2Pipeline.cs
index 73b3764..20c54db 100644
--- a/Assets/Scripts/Fsr2Pipeline.cs
+++ b/Assets/Scripts/Fsr2Pipeline.cs
@@ -1,6 +1,7 @@
using System;
using System.Runtime.InteropServices;
using UnityEngine;
+using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering;
namespace FidelityFX
@@ -58,13 +59,15 @@ namespace FidelityFX
shaderRef = _callbacks.LoadComputeShader(name);
kernelIndex = shaderRef.FindKernel("CS");
-
+
+ bool useLut = (SystemInfo.computeSubGroupSize == 64);
+
// This mirrors the permutation rules from the CreatePipeline* functions
if ((flags & Fsr2.InitializationFlags.EnableHighDynamicRange) != 0) shaderRef.EnableKeyword("FFX_FSR2_OPTION_HDR_COLOR_INPUT");
if ((flags & Fsr2.InitializationFlags.EnableDisplayResolutionMotionVectors) == 0) shaderRef.EnableKeyword("FFX_FSR2_OPTION_LOW_RESOLUTION_MOTION_VECTORS");
if ((flags & Fsr2.InitializationFlags.EnableMotionVectorsJitterCancellation) != 0) shaderRef.EnableKeyword("FFX_FSR2_OPTION_JITTERED_MOTION_VECTORS");
if ((flags & Fsr2.InitializationFlags.EnableDepthInverted) != 0) shaderRef.EnableKeyword("FFX_FSR2_OPTION_INVERTED_DEPTH");
- // TODO: enable FFX_FSR2_OPTION_REPROJECT_USE_LANCZOS_TYPE if the device capabilities allow (default subgroup size == 32 or 64)
+ if (useLut) shaderRef.EnableKeyword("FFX_FSR2_OPTION_REPROJECT_USE_LANCZOS_TYPE");
// TODO: enable FFX_HALF if FP16 is supported (except RCAS)
}
@@ -86,21 +89,44 @@ namespace FidelityFX
internal class Fsr2ComputeLuminancePyramidPipeline : Fsr2Pipeline
{
private readonly ComputeBuffer _spdConstants;
+ private readonly RenderTexture _autoExposure;
- public Fsr2ComputeLuminancePyramidPipeline(Fsr2Callbacks callbacks, Fsr2.InitializationFlags flags, ComputeBuffer constants, ComputeBuffer spdConstants)
+ public Fsr2ComputeLuminancePyramidPipeline(Fsr2Callbacks callbacks, Fsr2.InitializationFlags flags, ComputeBuffer constants, ComputeBuffer spdConstants, RenderTexture autoExposure)
: base(callbacks, constants)
{
_spdConstants = spdConstants;
+ _autoExposure = autoExposure;
LoadComputeShader("FSR2/ffx_fsr2_compute_luminance_pyramid_pass", flags);
}
public override void ScheduleDispatch(CommandBuffer commandBuffer, Fsr2.DispatchDescription dispatchParams, int dispatchX, int dispatchY)
{
+ // Problems to solve:
+ // - How do resources (render textures) relate to SRV/UAV bindings? How are those tied together?
+ // - What about the SRV/UAVs that are not related to any resources? Where are those filled in?
+ // - 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);
+ // Resource FSR2_SpdAtomicCounter: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R32_UINT, FFX_RESOURCE_FLAGS_ALIASABLE
+ commandBuffer.GetTemporaryRT(UavSpdAtomicCount, 1, 1, 0, FilterMode.Point, GraphicsFormat.R32_UInt, 1, true); // FSR2_BIND_UAV_SPD_GLOBAL_ATOMIC
+ // Resource FSR2_ExposureMips: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R16_FLOAT, FFX_RESOURCE_FLAGS_ALIASABLE, mipCount = 0
+ // See `scheduleDispatch` for the song and dance to bind UAV mip levels to each luminance mipmap... and this shader specifically wants mip levels 4 and 5
+ // Looks like we can just bind two separate resources here, shouldn't be necessary to bother with mipmapping nonsense. Be sure to get the right dimensions though.
+ commandBuffer.GetTemporaryRT(UavExposureMipLumaChange, dispatchParams.RenderSize.x >> 4, dispatchParams.RenderSize.y >> 4, 0, FilterMode.Point, GraphicsFormat.R16_SFloat, 1, true); // FSR2_BIND_UAV_EXPOSURE_MIP_LUMA_CHANGE
+ commandBuffer.GetTemporaryRT(UavExposureMip5, dispatchParams.RenderSize.x >> 5, dispatchParams.RenderSize.y >> 5, 0, FilterMode.Point, GraphicsFormat.R16_SFloat, 1, true); // FSR2_BIND_UAV_EXPOSURE_MIP_5
+ commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, UavAutoExposure, _autoExposure); // FSR2_BIND_UAV_AUTO_EXPOSURE
commandBuffer.SetComputeConstantBufferParam(ComputeShader, CbFsr2, Constants, 0, Marshal.SizeOf());
commandBuffer.SetComputeConstantBufferParam(ComputeShader, CbSpd, _spdConstants, 0, Marshal.SizeOf());
commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1);
+
+ // NOTE: since these temp RTs are not bound to a specific shader or kernel, we can set them globally one time and release them after dispatch.
+ // That way we can share aliasable resources between shaders without any complicated management.
+ commandBuffer.ReleaseTemporaryRT(UavSpdAtomicCount);
+ commandBuffer.ReleaseTemporaryRT(UavExposureMipLumaChange);
+ commandBuffer.ReleaseTemporaryRT(UavExposureMip5);
}
}