Browse Source

Moved all compute shader-related code to the Fsr2Context class, renamed FSR2Thing to Fsr2Controller and made the callbacks a static field in the Fsr2 class.

It's becoming more clear now who is responsible for what, and where things should be implemented.
mac-autoexp
Nico de Poel 3 years ago
parent
commit
fc233b935f
  1. 254
      Assets/Scripts/FSR2Thing.cs
  2. 7
      Assets/Scripts/Fsr2.cs
  3. 148
      Assets/Scripts/Fsr2Context.cs
  4. 119
      Assets/Scripts/Fsr2Controller.cs
  5. 0
      Assets/Scripts/Fsr2Controller.cs.meta
  6. 23
      Assets/Scripts/SubsampleTest.cs

254
Assets/Scripts/FSR2Thing.cs

@ -1,254 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using FidelityFX;
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;
public class FSR2Thing : MonoBehaviour
{
[SerializeField]
private bool performSharpenPass = true;
[SerializeField, Range(0, 1)]
private float sharpness = 0.8f;
[HideInInspector]
public Camera gameCamera;
[HideInInspector]
public Camera outputCamera;
[HideInInspector]
public float renderScale;
public Fsr2Callbacks callbacks;
private RenderTexture _upscaleRT;
private RenderTexture _rcasOutput;
private Texture2D _exposure;
private Material _testMaterial;
private Material TestMaterial
{
get
{
if (_testMaterial == null)
{
var testShader = callbacks.LoadShader("FSR2/FSRTest");
_testMaterial = new Material(testShader);
}
return _testMaterial;
}
}
private ComputeShader _rcasComputeShader;
private ComputeShader RCASComputeShader
{
get
{
if (_rcasComputeShader == null)
{
_rcasComputeShader = callbacks.LoadComputeShader("FSR2/ffx_fsr2_rcas_pass");
}
return _rcasComputeShader;
}
}
private ComputeBuffer _fsr2ConstantsBuffer;
private readonly Fsr2Constants[] _fsr2ConstantsArray = { new Fsr2Constants() };
private ComputeBuffer _spdConstantsBuffer;
private readonly SpdConstants[] _spdConstantsArray = { new SpdConstants() };
private ComputeBuffer _rcasConstantsBuffer;
private readonly RcasConstants[] _rcasConstantsArray = new RcasConstants[1];
private void OnEnable()
{
RenderPipelineManager.endContextRendering += OnEndContextRendering;
_upscaleRT = new RenderTexture(Screen.width, Screen.height, 24, RenderTextureFormat.ARGBHalf);
_upscaleRT.Create();
_rcasOutput = new RenderTexture(Screen.width, Screen.height, 24, RenderTextureFormat.ARGBHalf);
_rcasOutput.enableRandomWrite = true;
_rcasOutput.Create();
_exposure = new Texture2D(1, 1);
_exposure.name = "FSR2 Exposure";
_exposure.SetPixel(0, 0, Color.white);
_exposure.Apply();
_fsr2ConstantsBuffer = new ComputeBuffer(1, Marshal.SizeOf<Fsr2Constants>(), ComputeBufferType.Constant);
_spdConstantsBuffer = new ComputeBuffer(1, Marshal.SizeOf<SpdConstants>(), ComputeBufferType.Constant);
_rcasConstantsBuffer = new ComputeBuffer(1, Marshal.SizeOf<RcasConstants>(), ComputeBufferType.Constant);
}
private void OnDisable()
{
if (_rcasConstantsBuffer != null)
{
_rcasConstantsBuffer.Release();
_rcasConstantsBuffer = null;
}
if (_spdConstantsBuffer != null)
{
_spdConstantsBuffer.Release();
_spdConstantsBuffer = null;
}
if (_fsr2ConstantsBuffer != null)
{
_fsr2ConstantsBuffer.Release();
_fsr2ConstantsBuffer = null;
}
if (_exposure != null)
{
Destroy(_exposure);
_exposure = null;
}
if (_rcasOutput != null)
{
_rcasOutput.Release();
_rcasOutput = null;
}
if (_upscaleRT != null)
{
_upscaleRT.Release();
_upscaleRT = null;
}
RenderPipelineManager.endContextRendering -= OnEndContextRendering;
}
// For scriptable rendering pipeline
private void OnEndContextRendering(ScriptableRenderContext context, List<Camera> cameras)
{
Debug.Log($"OnEndContentRendering, cameras = {string.Join(", ", cameras.Select(c => c.name))}");
}
// For legacy built-in render pipeline
private void OnRenderImage(RenderTexture src, RenderTexture dest)
{
// Do a dumb upscale first
Graphics.Blit(gameCamera.targetTexture, _upscaleRT, TestMaterial);
_fsr2ConstantsArray[0].preExposure = 1.0f;
_fsr2ConstantsBuffer.SetData(_fsr2ConstantsArray);
if (performSharpenPass)
{
int sharpnessIndex = Mathf.RoundToInt(Mathf.Clamp01(sharpness) * (RcasConfigs.Count - 1));
_rcasConstantsArray[0] = RcasConfigs[sharpnessIndex];
_rcasConstantsBuffer.SetData(_rcasConstantsArray);
// Run the RCAS sharpening filter on the upscaled image
int rcasKernel = RCASComputeShader.FindKernel("CS");
RCASComputeShader.SetTexture(rcasKernel, "r_exposure", _exposure);
RCASComputeShader.SetTexture(rcasKernel, "r_rcas_input", _upscaleRT);
RCASComputeShader.SetTexture(rcasKernel, "rw_upscaled_output", _rcasOutput);
RCASComputeShader.SetConstantBuffer("cbFSR2", _fsr2ConstantsBuffer, 0, Marshal.SizeOf<Fsr2Constants>());
RCASComputeShader.SetConstantBuffer("cbRCAS", _rcasConstantsBuffer, 0, Marshal.SizeOf<RcasConstants>());
const int threadGroupWorkRegionDimRcas = 16;
int threadGroupsX = (Screen.width + threadGroupWorkRegionDimRcas - 1) / threadGroupWorkRegionDimRcas;
int threadGroupsY = (Screen.height + threadGroupWorkRegionDimRcas - 1) / threadGroupWorkRegionDimRcas;
RCASComputeShader.Dispatch(rcasKernel, threadGroupsX, threadGroupsY, 1);
// Output sharpened image to screen
Graphics.Blit(_rcasOutput, dest);
}
else
{
Graphics.Blit(_upscaleRT, dest);
}
}
[Serializable, StructLayout(LayoutKind.Sequential)]
private struct RcasConstants
{
public RcasConstants(uint sharpness, uint halfSharp)
{
this.sharpness = sharpness;
this.halfSharp = halfSharp;
dummy0 = dummy1 = 0;
}
public readonly uint sharpness;
public readonly uint halfSharp;
public readonly uint dummy0;
public readonly uint dummy1;
}
[Serializable, StructLayout(LayoutKind.Sequential)]
private struct Fsr2Constants
{
public Vector2Int renderSize;
public Vector2Int displaySize;
public uint lumaMipDimensionsX, lumaMipDimensionsY;
public uint lumaMipLevelToUse;
public uint frameIndex;
public Vector2 displaySizeRcp;
public Vector2 jitterOffset;
public Vector4 deviceToViewDepth;
public Vector2 depthClipUVScale;
public Vector2 postLockStatusUVScale;
public Vector2 reactiveMaskDimRcp;
public Vector2 motionVectorScale;
public Vector2 downscaleFactor;
public float preExposure;
public float tanHalfFOV;
public Vector2 motionVectorJitterCancellation;
public float jitterPhaseCount;
public float lockInitialLifetime;
public float lockTickDelta;
public float deltaTime;
public float dynamicResChangeFactor;
public float lumaMipRcp;
}
[Serializable, StructLayout(LayoutKind.Sequential)]
private struct SpdConstants
{
public uint mips;
public uint numWorkGroups;
public uint workGroupOffsetX, workGroupOffsetY;
public uint renderSizeX, renderSizeY;
}
private static readonly List<RcasConstants> RcasConfigs = new()
{
new(1048576000u, 872428544u),
new(1049178080u, 877212745u),
new(1049823372u, 882390168u),
new(1050514979u, 887895276u),
new(1051256227u, 893859143u),
new(1052050675u, 900216232u),
new(1052902144u, 907032080u),
new(1053814727u, 914306687u),
new(1054792807u, 922105590u),
new(1055841087u, 930494326u),
new(1056964608u, 939538432u),
new(1057566688u, 944322633u),
new(1058211980u, 949500056u),
new(1058903587u, 955005164u),
new(1059644835u, 960969031u),
new(1060439283u, 967326120u),
new(1061290752u, 974141968u),
new(1062203335u, 981416575u),
new(1063181415u, 989215478u),
new(1064229695u, 997604214u),
new(1065353216u, 1006648320),
};
}

7
Assets/Scripts/Fsr2.cs

@ -5,6 +5,9 @@ namespace FidelityFX
{
public static class Fsr2
{
// Allow overriding of certain Unity resource management behaviors by the programmer
public static Fsr2Callbacks Callbacks { get; set; } = new Fsr2Callbacks();
public enum QualityMode
{
Quality = 1,
@ -28,7 +31,9 @@ namespace FidelityFX
public static Fsr2Context CreateContext()
{
throw new NotImplementedException();
var context = new Fsr2Context();
context.Create(Callbacks);
return context;
}
public static float GetUpscaleRatioFromQualityMode(QualityMode qualityMode)

148
Assets/Scripts/Fsr2Context.cs

@ -1,20 +1,160 @@
namespace FidelityFX
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine;
namespace FidelityFX
{
public class Fsr2Context
{
public void Create()
private ComputeShader _rcasComputeShader;
private ComputeBuffer _fsr2ConstantsBuffer;
private readonly Fsr2Constants[] _fsr2ConstantsArray = { new Fsr2Constants() };
private ComputeBuffer _spdConstantsBuffer;
private readonly SpdConstants[] _spdConstantsArray = { new SpdConstants() };
private ComputeBuffer _rcasConstantsBuffer;
private readonly RcasConstants[] _rcasConstantsArray = new RcasConstants[1];
public void Create(Fsr2Callbacks callbacks)
{
if (_rcasComputeShader == null)
_rcasComputeShader = callbacks.LoadComputeShader("FSR2/ffx_fsr2_rcas_pass");
_fsr2ConstantsBuffer = new ComputeBuffer(1, Marshal.SizeOf<Fsr2Constants>(), ComputeBufferType.Constant);
_spdConstantsBuffer = new ComputeBuffer(1, Marshal.SizeOf<SpdConstants>(), ComputeBufferType.Constant);
_rcasConstantsBuffer = new ComputeBuffer(1, Marshal.SizeOf<RcasConstants>(), ComputeBufferType.Constant);
}
public void Dispatch()
public void Dispatch(RenderTexture src, RenderTexture dest, Texture2D exposure, bool enableSharpening, float sharpness) // TODO: collect parameters into a single object
{
_fsr2ConstantsArray[0].preExposure = 1.0f;
_fsr2ConstantsBuffer.SetData(_fsr2ConstantsArray);
if (enableSharpening)
{
int sharpnessIndex = Mathf.RoundToInt(Mathf.Clamp01(sharpness) * (RcasConfigs.Count - 1));
_rcasConstantsArray[0] = RcasConfigs[sharpnessIndex];
_rcasConstantsBuffer.SetData(_rcasConstantsArray);
// Run the RCAS sharpening filter on the upscaled image
int rcasKernel = _rcasComputeShader.FindKernel("CS");
_rcasComputeShader.SetTexture(rcasKernel, "r_exposure", exposure);
_rcasComputeShader.SetTexture(rcasKernel, "r_rcas_input", src);
_rcasComputeShader.SetTexture(rcasKernel, "rw_upscaled_output", dest);
_rcasComputeShader.SetConstantBuffer("cbFSR2", _fsr2ConstantsBuffer, 0, Marshal.SizeOf<Fsr2Constants>());
_rcasComputeShader.SetConstantBuffer("cbRCAS", _rcasConstantsBuffer, 0, Marshal.SizeOf<RcasConstants>());
const int threadGroupWorkRegionDimRcas = 16;
int threadGroupsX = (Screen.width + threadGroupWorkRegionDimRcas - 1) / threadGroupWorkRegionDimRcas;
int threadGroupsY = (Screen.height + threadGroupWorkRegionDimRcas - 1) / threadGroupWorkRegionDimRcas;
_rcasComputeShader.Dispatch(rcasKernel, threadGroupsX, threadGroupsY, 1);
}
else
{
Graphics.Blit(src, dest);
}
}
public void Destroy()
{
if (_rcasConstantsBuffer != null)
{
_rcasConstantsBuffer.Release();
_rcasConstantsBuffer = null;
}
if (_spdConstantsBuffer != null)
{
_spdConstantsBuffer.Release();
_spdConstantsBuffer = null;
}
if (_fsr2ConstantsBuffer != null)
{
_fsr2ConstantsBuffer.Release();
_fsr2ConstantsBuffer = null;
}
}
[Serializable, StructLayout(LayoutKind.Sequential)]
private struct Fsr2Constants
{
public Vector2Int renderSize;
public Vector2Int displaySize;
public uint lumaMipDimensionsX, lumaMipDimensionsY;
public uint lumaMipLevelToUse;
public uint frameIndex;
public Vector2 displaySizeRcp;
public Vector2 jitterOffset;
public Vector4 deviceToViewDepth;
public Vector2 depthClipUVScale;
public Vector2 postLockStatusUVScale;
public Vector2 reactiveMaskDimRcp;
public Vector2 motionVectorScale;
public Vector2 downscaleFactor;
public float preExposure;
public float tanHalfFOV;
public Vector2 motionVectorJitterCancellation;
public float jitterPhaseCount;
public float lockInitialLifetime;
public float lockTickDelta;
public float deltaTime;
public float dynamicResChangeFactor;
public float lumaMipRcp;
}
[Serializable, StructLayout(LayoutKind.Sequential)]
private struct SpdConstants
{
public uint mips;
public uint numWorkGroups;
public uint workGroupOffsetX, workGroupOffsetY;
public uint renderSizeX, renderSizeY;
}
[Serializable, StructLayout(LayoutKind.Sequential)]
private struct RcasConstants
{
public RcasConstants(uint sharpness, uint halfSharp)
{
this.sharpness = sharpness;
this.halfSharp = halfSharp;
dummy0 = dummy1 = 0;
}
public readonly uint sharpness;
public readonly uint halfSharp;
public readonly uint dummy0;
public readonly uint dummy1;
}
private static readonly List<RcasConstants> RcasConfigs = new()
{
new(1048576000u, 872428544u),
new(1049178080u, 877212745u),
new(1049823372u, 882390168u),
new(1050514979u, 887895276u),
new(1051256227u, 893859143u),
new(1052050675u, 900216232u),
new(1052902144u, 907032080u),
new(1053814727u, 914306687u),
new(1054792807u, 922105590u),
new(1055841087u, 930494326u),
new(1056964608u, 939538432u),
new(1057566688u, 944322633u),
new(1058211980u, 949500056u),
new(1058903587u, 955005164u),
new(1059644835u, 960969031u),
new(1060439283u, 967326120u),
new(1061290752u, 974141968u),
new(1062203335u, 981416575u),
new(1063181415u, 989215478u),
new(1064229695u, 997604214u),
new(1065353216u, 1006648320),
};
}
}

119
Assets/Scripts/Fsr2Controller.cs

@ -0,0 +1,119 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using FidelityFX;
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;
/// <summary>
/// This class is responsible for hooking into various Unity events and translating them to the FSR2 subsystem.
/// This includes creation and destruction of the FSR2 context, as well as dispatching commands at the right time.
/// This class also exposes various FSR2 parameters to the Unity inspector.
/// </summary>
public class Fsr2Controller : MonoBehaviour
{
[SerializeField]
private bool performSharpenPass = true;
[SerializeField, Range(0, 1)]
private float sharpness = 0.8f;
[HideInInspector]
public Camera gameCamera;
[HideInInspector]
public Camera outputCamera;
[HideInInspector]
public float renderScale;
private Fsr2Context _context;
private RenderTexture _upscaleRT;
private RenderTexture _rcasOutput;
private Texture2D _exposure;
private Material _testMaterial;
private Material TestMaterial
{
get
{
if (_testMaterial == null)
{
var testShader = Fsr2.Callbacks.LoadShader("FSR2/FSRTest");
_testMaterial = new Material(testShader);
}
return _testMaterial;
}
}
private void OnEnable()
{
RenderPipelineManager.endContextRendering += OnEndContextRendering;
_context = Fsr2.CreateContext();
_upscaleRT = new RenderTexture(Screen.width, Screen.height, 24, RenderTextureFormat.ARGBHalf);
_upscaleRT.Create();
_rcasOutput = new RenderTexture(Screen.width, Screen.height, 24, RenderTextureFormat.ARGBHalf);
_rcasOutput.enableRandomWrite = true;
_rcasOutput.Create();
_exposure = new Texture2D(1, 1);
_exposure.name = "FSR2 Exposure";
_exposure.SetPixel(0, 0, Color.white);
_exposure.Apply();
}
private void OnDisable()
{
if (_exposure != null)
{
Destroy(_exposure);
_exposure = null;
}
if (_rcasOutput != null)
{
_rcasOutput.Release();
_rcasOutput = null;
}
if (_upscaleRT != null)
{
_upscaleRT.Release();
_upscaleRT = null;
}
if (_context != null)
{
_context.Destroy();
_context = null;
}
RenderPipelineManager.endContextRendering -= OnEndContextRendering;
}
// For scriptable rendering pipeline
private void OnEndContextRendering(ScriptableRenderContext context, List<Camera> cameras)
{
Debug.Log($"OnEndContentRendering, cameras = {string.Join(", ", cameras.Select(c => c.name))}");
}
// For legacy built-in render pipeline
private void OnRenderImage(RenderTexture src, RenderTexture dest)
{
// Do a dumb upscale first
Graphics.Blit(gameCamera.targetTexture, _upscaleRT, TestMaterial);
_context.Dispatch(_upscaleRT, _rcasOutput, _exposure, performSharpenPass, sharpness);
// Output sharpened image to screen
Graphics.Blit(_rcasOutput, dest);
}
}

0
Assets/Scripts/FSR2Thing.cs.meta → Assets/Scripts/Fsr2Controller.cs.meta

23
Assets/Scripts/SubsampleTest.cs

@ -18,9 +18,7 @@ public class SubsampleTest : MonoBehaviour
[SerializeField]
private bool enableJitter;
private Fsr2Callbacks callbacks = new Fsr2Callbacks();
private FSR2Thing fsr2Thing;
private Fsr2Controller _fsr2Controller;
private void OnEnable()
{
@ -35,12 +33,11 @@ public class SubsampleTest : MonoBehaviour
outputCamera = outputCameraObject.AddComponent<Camera>();
outputCamera.eventMask = 0;
fsr2Thing = outputCameraObject.AddComponent<FSR2Thing>();
fsr2Thing.callbacks = callbacks;
fsr2Thing.gameCamera = gameCamera;
fsr2Thing.outputCamera = outputCamera;
fsr2Thing.renderScale = renderScale;
fsr2Thing.enabled = true;
_fsr2Controller = outputCameraObject.AddComponent<Fsr2Controller>();
_fsr2Controller.gameCamera = gameCamera;
_fsr2Controller.outputCamera = outputCamera;
_fsr2Controller.renderScale = renderScale;
_fsr2Controller.enabled = true;
//outputCamera.CopyFrom(gameCamera);
@ -49,7 +46,7 @@ public class SubsampleTest : MonoBehaviour
}
else
{
fsr2Thing.enabled = true;
_fsr2Controller.enabled = true;
}
gameCamera.targetTexture = new RenderTexture(
@ -65,18 +62,18 @@ public class SubsampleTest : MonoBehaviour
// Adjust texture mipmap LOD bias by log2(renderResolution/displayResolution) - 1.0;
// May need to leave this to the game dev, as we don't know which textures do and don't belong to the 3D scene
float biasOffset = GetMipmapBiasOffset();
callbacks.ApplyMipmapBias(biasOffset);
Fsr2.Callbacks.ApplyMipmapBias(biasOffset);
}
private void OnDisable()
{
float biasOffset = GetMipmapBiasOffset();
callbacks.ApplyMipmapBias(-biasOffset);
Fsr2.Callbacks.ApplyMipmapBias(-biasOffset);
gameCamera.targetTexture.Release();
gameCamera.targetTexture = null;
fsr2Thing.enabled = false;
_fsr2Controller.enabled = false;
outputCamera.enabled = false;
}

Loading…
Cancel
Save