diff --git a/Assets/Scripts/Fsr2.cs b/Assets/Scripts/Fsr2.cs index 7821f12..745f6e2 100644 --- a/Assets/Scripts/Fsr2.cs +++ b/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 diff --git a/Assets/Scripts/Fsr2Context.cs b/Assets/Scripts/Fsr2Context.cs index 30d626d..63bbb3a 100644 --- a/Assets/Scripts/Fsr2Context.cs +++ b/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? diff --git a/Assets/Scripts/Fsr2Controller.cs b/Assets/Scripts/Fsr2Controller.cs index d2685f9..095cba7 100644 --- a/Assets/Scripts/Fsr2Controller.cs +++ b/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(); - _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(); - _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(); - _originalRenderTarget = _renderCamera.targetTexture; // TODO: if this isn't null, could maybe reuse this for the output texture? _originalDepthTextureMode = _renderCamera.depthTextureMode; - - _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; + _renderCamera.depthTextureMode = _originalDepthTextureMode | 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); - _renderCamera.RemoveCommandBuffer(CameraEvent.BeforeImageEffectsOpaque, _opaqueOnlyCommandBuffer); - _opaqueOnlyCommandBuffer.Release(); - _opaqueOnlyCommandBuffer = null; + 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.RemoveCommandBuffer(CameraEvent.BeforeImageEffects, _inputsCommandBuffer); - _inputsCommandBuffer.Release(); - _inputsCommandBuffer = null; + if (_dispatchCommandBuffer != null) + { + _dispatchCommandBuffer.Release(); + _dispatchCommandBuffer = null; + } - _renderCamera.targetTexture.Release(); - _renderCamera.targetTexture = _originalRenderTarget; _renderCamera.depthTextureMode = _originalDepthTextureMode; - - _dispatcher.Context = null; - _dispatcher.enabled = false; - _displayCamera.enabled = false; 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); - - var targetTexture = _renderCamera.targetTexture; + // 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); + + _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() - { - _renderCamera.rect = _tempRect; - _renderCamera.ResetProjectionMatrix(); - } - } - - /// - /// Helper class to dispatch FSR2 commands on the display camera object, and render the final output texture. - /// - internal class Fsr2Dispatcher : MonoBehaviour - { - public Fsr2Context Context; - public Fsr2.DispatchDescription DispatchDescription; - private CommandBuffer _commandBuffer; - - private void OnEnable() - { - _commandBuffer = new CommandBuffer { name = "FSR2 Dispatch" }; - } - - private void OnDisable() - { - if (_commandBuffer != null) - { - _commandBuffer.Release(); - _commandBuffer = null; - } - } - private void OnRenderImage(RenderTexture src, RenderTexture dest) { - _commandBuffer.Clear(); + // Restore the camera's viewport rect so we can output at full resolution + _renderCamera.rect = _originalRect; + _renderCamera.ResetProjectionMatrix(); + + _dispatchCommandBuffer.Clear(); + + // UpscaleReactive: width, height, VK_FORMAT_R8_UNORM + //Context.GenerateReactiveMask(); + + _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);