Browse Source

Perform render scaling by manipulating the Camera viewport rect, which removes the need for a second game object and camera altogether.

This eliminates the stupid full screen quad drawing that the second camera was doing, while also making the entire setup a lot simpler.
mac-autoexp
Nico de Poel 3 years ago
parent
commit
158d5bc176
  1. 1
      Assets/Scripts/Fsr2.cs
  2. 2
      Assets/Scripts/Fsr2Context.cs
  3. 157
      Assets/Scripts/Fsr2Controller.cs

1
Assets/Scripts/Fsr2.cs

@ -150,6 +150,7 @@ namespace FidelityFX
public Vector2 JitterOffset;
public Vector2 MotionVectorScale;
public Vector2Int RenderSize;
public Vector2Int InputResourceSize;
public bool EnableSharpening;
public float Sharpness;
public float FrameTimeDelta; // in seconds

2
Assets/Scripts/Fsr2Context.cs

@ -230,7 +230,7 @@ namespace FidelityFX
constants.jitterOffset = dispatchParams.JitterOffset;
constants.renderSize = dispatchParams.RenderSize;
constants.maxRenderSize = _contextDescription.MaxRenderSize;
constants.inputColorResourceDimensions = dispatchParams.RenderSize; // We have no way to query the actual width & height of the input resources, so trust that it matches the render size
constants.inputColorResourceDimensions = dispatchParams.InputResourceSize; // We have no way to query the actual width & height of the input resources, so trust that it matches the render size
// TODO: if we want to support dynamic resolution, we may have to differentiate between renderSize and inputColorResourceDimensions again
// TODO: come to think of it, couldn't we use Unity's ScalableBufferManager for dealing with render scale altogether? So we don't have to deal with this nasty double camera business?

157
Assets/Scripts/Fsr2Controller.cs

@ -20,66 +20,38 @@ namespace FidelityFX
[SerializeField, Range(0, 1)] private float sharpness = 0.8f;
private Fsr2Context _context;
private Vector2Int _renderSize;
private Vector2Int _displaySize;
private bool _reset;
private Camera _renderCamera;
private RenderTexture _originalRenderTarget;
private DepthTextureMode _originalDepthTextureMode;
private Rect _originalRect;
private GameObject _displayCameraObject;
private Camera _displayCamera;
private Fsr2Dispatcher _dispatcher;
private readonly Fsr2.DispatchDescription _dispatchDescription = new Fsr2.DispatchDescription();
private readonly Fsr2.GenerateReactiveDescription _genReactiveDescription = new Fsr2.GenerateReactiveDescription();
private Fsr2.QualityMode _prevQualityMode;
private Vector2Int _prevScreenSize;
private Vector2Int _prevDisplaySize;
private CommandBuffer _dispatchCommandBuffer;
private CommandBuffer _opaqueOnlyCommandBuffer;
private CommandBuffer _inputsCommandBuffer;
private void OnEnable()
{
if (_displayCameraObject == null)
{
// Create a helper object with a camera that outputs at screen resolution
_displayCameraObject = new GameObject("FSR2 Camera Object");
_displayCameraObject.transform.SetParent(transform);
_displayCameraObject.transform.SetPositionAndRotation(Vector3.zero, Quaternion.identity);
//outputCameraObject.transform.hideFlags = HideFlags.HideInHierarchy | HideFlags.HideInInspector;
// Create a camera that does nothing except output the upscaled image
_displayCamera = _displayCameraObject.AddComponent<Camera>();
_displayCamera.backgroundColor = Color.clear;
_displayCamera.clearFlags = CameraClearFlags.Nothing;
_displayCamera.eventMask = 0;
_displayCamera.cullingMask = 0;
_displayCamera.useOcclusionCulling = false;
_displayCamera.orthographic = true;
_displayCamera.allowMSAA = false;
_displayCamera.renderingPath = RenderingPath.Forward;
_dispatcher = _displayCameraObject.AddComponent<Fsr2Dispatcher>();
_dispatcher.DispatchDescription = _dispatchDescription;
}
Fsr2.GetRenderResolutionFromQualityMode(out var renderWidth, out var renderHeight, Screen.width, Screen.height, qualityMode);
_context = Fsr2.CreateContext(new Vector2Int(Screen.width, Screen.height), new Vector2Int(renderWidth, renderHeight), Fsr2.InitializationFlags.EnableMotionVectorsJitterCancellation);
_dispatcher.Context = _context;
_dispatcher.enabled = true;
_displaySize = new Vector2Int(Screen.width, Screen.height);
Fsr2.GetRenderResolutionFromQualityMode(out var renderWidth, out var renderHeight, _displaySize.x, _displaySize.y, qualityMode);
_renderSize = new Vector2Int(renderWidth, renderHeight);
_context = Fsr2.CreateContext(_displaySize, _renderSize, Fsr2.InitializationFlags.EnableMotionVectorsJitterCancellation);
// Set up the original camera to output all of the required FSR2 input resources at the desired resolution
_renderCamera = GetComponent<Camera>();
_originalRenderTarget = _renderCamera.targetTexture; // TODO: if this isn't null, could maybe reuse this for the output texture?
_originalDepthTextureMode = _renderCamera.depthTextureMode;
_renderCamera.depthTextureMode = _originalDepthTextureMode | DepthTextureMode.Depth | DepthTextureMode.MotionVectors;
_renderCamera.targetTexture = new RenderTexture(
renderWidth, renderHeight,
_originalRenderTarget != null ? _originalRenderTarget.depth : 32,
_originalRenderTarget != null ? _originalRenderTarget.format : RenderTextureFormat.ARGBHalf) { name = "FSR2 Input Texture" };
_renderCamera.targetTexture.Create();
_renderCamera.depthTextureMode |= DepthTextureMode.Depth | DepthTextureMode.MotionVectors;
_dispatchCommandBuffer = new CommandBuffer { name = "FSR2 Dispatch" };
// Create command buffers to bind the camera's output at the right moments in the rendering pipeline
_opaqueOnlyCommandBuffer = new CommandBuffer { name = "FSR2 Opaque Input" };
@ -95,33 +67,39 @@ namespace FidelityFX
// 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 = Fsr2.GetMipmapBiasOffset(renderWidth, Screen.width);
float biasOffset = Fsr2.GetMipmapBiasOffset(_renderSize.x, _displaySize.x);
Fsr2.GlobalCallbacks.ApplyMipmapBias(biasOffset);
_prevScreenSize = new Vector2Int(Screen.width, Screen.height);
_prevDisplaySize = _displaySize;
_prevQualityMode = qualityMode;
}
private void OnDisable()
{
float biasOffset = Fsr2.GetMipmapBiasOffset(_renderCamera.targetTexture.width, _prevScreenSize.x);
float biasOffset = Fsr2.GetMipmapBiasOffset(_renderSize.x, _prevDisplaySize.x);
Fsr2.GlobalCallbacks.ApplyMipmapBias(-biasOffset);
if (_opaqueOnlyCommandBuffer != null)
{
_renderCamera.RemoveCommandBuffer(CameraEvent.BeforeImageEffectsOpaque, _opaqueOnlyCommandBuffer);
_opaqueOnlyCommandBuffer.Release();
_opaqueOnlyCommandBuffer = null;
}
if (_inputsCommandBuffer != null)
{
_renderCamera.RemoveCommandBuffer(CameraEvent.BeforeImageEffects, _inputsCommandBuffer);
_inputsCommandBuffer.Release();
_inputsCommandBuffer = null;
}
_renderCamera.targetTexture.Release();
_renderCamera.targetTexture = _originalRenderTarget;
_renderCamera.depthTextureMode = _originalDepthTextureMode;
if (_dispatchCommandBuffer != null)
{
_dispatchCommandBuffer.Release();
_dispatchCommandBuffer = null;
}
_dispatcher.Context = null;
_dispatcher.enabled = false;
_displayCamera.enabled = false;
_renderCamera.depthTextureMode = _originalDepthTextureMode;
if (_context != null)
{
@ -132,9 +110,7 @@ namespace FidelityFX
private void Update()
{
_displayCamera.enabled = _renderCamera.enabled;
if (Screen.width != _prevScreenSize.x || Screen.height != _prevScreenSize.y || qualityMode != _prevQualityMode)
if (Screen.width != _prevDisplaySize.x || Screen.height != _prevDisplaySize.y || qualityMode != _prevQualityMode)
{
// Force all resources to be destroyed and recreated with the new settings
OnDisable();
@ -147,16 +123,18 @@ namespace FidelityFX
_reset = true;
}
private Rect _tempRect;
private bool _reset;
private void OnPreRender()
{
_tempRect = _renderCamera.rect;
_renderCamera.aspect = (Screen.width * _tempRect.width) / (Screen.height * _tempRect.height);
_renderCamera.rect = new Rect(0, 0, 1, 1);
// Render to a smaller portion of the screen by manipulating the camera's viewport rect
_originalRect = _renderCamera.rect;
_renderCamera.aspect = (Screen.width * _originalRect.width) / (Screen.height * _originalRect.height);
_renderCamera.rect = new Rect(0, 0, _originalRect.width * _renderSize.x / _displaySize.x, _originalRect.height * _renderSize.y / _displaySize.y);
var targetTexture = _renderCamera.targetTexture;
_genReactiveDescription.RenderSize = _renderSize;
_genReactiveDescription.Scale = 1.0f;
_genReactiveDescription.CutoffThreshold = 0.2f;
_genReactiveDescription.BinaryValue = 0.9f;
_genReactiveDescription.Flags = 0; // ApplyTonemap, ApplyInverseTonemap, ApplyThreshold, UseComponentsMax
_dispatchDescription.Color = null;
_dispatchDescription.Depth = null;
@ -169,7 +147,7 @@ namespace FidelityFX
_dispatchDescription.Sharpness = sharpness;
_dispatchDescription.MotionVectorScale.x = -_renderCamera.pixelWidth;
_dispatchDescription.MotionVectorScale.y = -_renderCamera.pixelHeight;
_dispatchDescription.RenderSize = new Vector2Int(targetTexture.width, targetTexture.height);
_dispatchDescription.RenderSize = _renderSize;
_dispatchDescription.FrameTimeDelta = Time.unscaledDeltaTime;
_dispatchDescription.CameraNear = _renderCamera.nearClipPlane;
_dispatchDescription.CameraFar = _renderCamera.farClipPlane;
@ -179,76 +157,55 @@ namespace FidelityFX
_reset = false;
// Perform custom jittering of the camera's projection matrix according to FSR2's instructions
int jitterPhaseCount = Fsr2.GetJitterPhaseCount(targetTexture.width, Screen.width);
int jitterPhaseCount = Fsr2.GetJitterPhaseCount(_renderSize.x, _displaySize.x);
Fsr2.GetJitterOffset(out float jitterX, out float jitterY, Time.frameCount, jitterPhaseCount);
_dispatchDescription.JitterOffset = new Vector2(jitterX, jitterY);
jitterX = 2.0f * jitterX / targetTexture.width;
jitterY = 2.0f * jitterY / targetTexture.height;
jitterX = 2.0f * jitterX / _renderSize.x;
jitterY = 2.0f * jitterY / _renderSize.y;
var jitterTranslationMatrix = Matrix4x4.Translate(new Vector3(jitterX, jitterY, 0));
_renderCamera.projectionMatrix = jitterTranslationMatrix * _renderCamera.nonJitteredProjectionMatrix;
}
private void OnPostRender()
private void OnRenderImage(RenderTexture src, RenderTexture dest)
{
_renderCamera.rect = _tempRect;
// Restore the camera's viewport rect so we can output at full resolution
_renderCamera.rect = _originalRect;
_renderCamera.ResetProjectionMatrix();
}
}
/// <summary>
/// Helper class to dispatch FSR2 commands on the display camera object, and render the final output texture.
/// </summary>
internal class Fsr2Dispatcher : MonoBehaviour
{
public Fsr2Context Context;
public Fsr2.DispatchDescription DispatchDescription;
private CommandBuffer _commandBuffer;
private void OnEnable()
{
_commandBuffer = new CommandBuffer { name = "FSR2 Dispatch" };
}
_dispatchCommandBuffer.Clear();
private void OnDisable()
{
if (_commandBuffer != null)
{
_commandBuffer.Release();
_commandBuffer = null;
}
}
// UpscaleReactive: width, height, VK_FORMAT_R8_UNORM
//Context.GenerateReactiveMask();
private void OnRenderImage(RenderTexture src, RenderTexture dest)
{
_commandBuffer.Clear();
_dispatchDescription.InputResourceSize = new Vector2Int(src.width, src.height);
if (dest != null)
{
Debug.Log($"src = {src.width}x{src.height}, dest = {dest.width}x{dest.height}");
// We have more image effects lined up after this, so FSR2 can output straight to the intermediate render texture
// TODO: we should probably use a shader to include depth & motion vectors into the output
_commandBuffer.SetGlobalTexture(Fsr2Pipeline.UavUpscaledOutput, dest);
// TODO: we should probably use a shader to include depth & motion vectors into the output, making sure they match the expected output resolution
_dispatchCommandBuffer.SetGlobalTexture(Fsr2Pipeline.UavUpscaledOutput, dest);
}
else
{
// We are rendering to the backbuffer, so we need a temporary render texture for FSR2 to output to
_commandBuffer.GetTemporaryRT(Fsr2Pipeline.UavUpscaledOutput, Screen.width, Screen.height, 0, default, GraphicsFormat.R16G16B16A16_SFloat, 1, true);
_dispatchCommandBuffer.GetTemporaryRT(Fsr2Pipeline.UavUpscaledOutput, Screen.width, Screen.height, 0, default, GraphicsFormat.R16G16B16A16_SFloat, 1, true);
}
Context.Dispatch(DispatchDescription, _commandBuffer);
_context.Dispatch(_dispatchDescription, _dispatchCommandBuffer);
// Output upscaled image to screen
// TODO: if `dest` is null, we likely don't care about the depth & motion vectors anymore
if (dest == null)
{
_commandBuffer.Blit(Fsr2Pipeline.UavUpscaledOutput, dest);
_commandBuffer.ReleaseTemporaryRT(Fsr2Pipeline.UavUpscaledOutput);
_dispatchCommandBuffer.Blit(Fsr2Pipeline.UavUpscaledOutput, dest);
_dispatchCommandBuffer.ReleaseTemporaryRT(Fsr2Pipeline.UavUpscaledOutput);
}
Graphics.ExecuteCommandBuffer(_commandBuffer);
Graphics.ExecuteCommandBuffer(_dispatchCommandBuffer);
// Shut up the Unity warning about not writing to the destination texture
Graphics.SetRenderTarget(dest);

Loading…
Cancel
Save