@ -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 _prevScreen Size ;
private Vector2Int _prevDisplay Size ;
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 : 3 2 ,
_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,34 +67,40 @@ 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 , _prevScreen Size . x ) ;
float biasOffset = Fsr2 . GetMipmapBiasOffset ( _renderSize . x , _prevDisplay Size . 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 )
{
_context . Destroy ( ) ;
@ -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 ) ;
_dispat chC ommandBuffer . GetTemporaryRT ( Fsr2Pipeline . UavUpscaledOutput , Screen . width , Screen . height , 0 , default , GraphicsFormat . R16G16B16A16_SFloat , 1 , true ) ;
}
C ontext. Dispatch ( D ispatchDescription, _commandBuffer ) ;
_c ontext. Dispatch ( _d ispatchDescription, _dispat chC ommandBuffer ) ;
// 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 ) ;
_dispat chC ommandBuffer . Blit ( Fsr2Pipeline . UavUpscaledOutput , dest ) ;
_dispat chC ommandBuffer . ReleaseTemporaryRT ( Fsr2Pipeline . UavUpscaledOutput ) ;
}
Graphics . ExecuteCommandBuffer ( _commandBuffer ) ;
Graphics . ExecuteCommandBuffer ( _dispat chC ommandBuffer ) ;
// Shut up the Unity warning about not writing to the destination texture
Graphics . SetRenderTarget ( dest ) ;