using FidelityFX;
using FidelityFX.FSR2;
namespace UnityEngine.Rendering.HighDefinition.AMD.FSR2
{
///
/// Custom upscaler plugin that uses the open source port of FSR2 to Unity.
/// This eschews using native plugins, instead using portable code that will work on any platform that supports compute shaders.
///
public class FSR2UpscalerPlugin: UpscalerPlugin
{
private Fsr2Assets _assets;
public override string name => "FSR 2.2";
public override bool isSupported => SystemInfo.supportsComputeShaders;
public override bool Load()
{
if (_assets != null)
return true;
_assets = Resources.Load("FSR2 Assets");
return _assets != null;
}
public override bool IsLoaded() => _assets != null;
public override UpscalerContext CreateContext(in FSR2CommandInitializationData initSettings)
{
var context = new FSR2UpscalerContext(in initSettings);
context.Init(_assets);
return context;
}
public override void DestroyContext(UpscalerContext context)
{
((FSR2UpscalerContext)context).Destroy();
}
public override bool GetRenderResolutionFromQualityMode(FSR2Quality qualityMode, uint displayWidth, uint displayHeight, out uint renderWidth, out uint renderHeight)
{
Fsr2.GetRenderResolutionFromQualityMode(out int rw, out int rh, (int)displayWidth, (int)displayHeight, (Fsr2.QualityMode)((int)qualityMode + 2));
renderWidth = (uint)rw;
renderHeight = (uint)rh;
return true;
}
public override float GetUpscaleRatioFromQualityMode(FSR2Quality qualityMode)
{
return Fsr2.GetUpscaleRatioFromQualityMode((Fsr2.QualityMode)((int)qualityMode + 2));
}
}
public class FSR2UpscalerContext : UpscalerContext
{
private readonly Fsr2Context _context = new();
private readonly Fsr2.DispatchDescription _dispatchDescription = new();
private readonly FSR2CommandInitializationData _initData;
internal FSR2UpscalerContext(in FSR2CommandInitializationData initSettings)
{
_initData = initSettings;
}
internal void Init(Fsr2Assets assets)
{
Fsr2.InitializationFlags flags = 0;
if (_initData.GetFlag(FfxFsr2InitializationFlags.EnableHighDynamicRange)) flags |= Fsr2.InitializationFlags.EnableHighDynamicRange;
if (_initData.GetFlag(FfxFsr2InitializationFlags.EnableDisplayResolutionMotionVectors)) flags |= Fsr2.InitializationFlags.EnableDisplayResolutionMotionVectors;
if (_initData.GetFlag(FfxFsr2InitializationFlags.EnableMotionVectorsJitterCancellation)) flags |= Fsr2.InitializationFlags.EnableMotionVectorsJitterCancellation;
if (_initData.GetFlag(FfxFsr2InitializationFlags.DepthInverted)) flags |= Fsr2.InitializationFlags.EnableDepthInverted;
if (_initData.GetFlag(FfxFsr2InitializationFlags.EnableDepthInfinite)) flags |= Fsr2.InitializationFlags.EnableDepthInfinite;
if (_initData.GetFlag(FfxFsr2InitializationFlags.EnableAutoExposure)) flags |= Fsr2.InitializationFlags.EnableAutoExposure;
if (_initData.GetFlag(FfxFsr2InitializationFlags.EnableDynamicResolution)) flags |= Fsr2.InitializationFlags.EnableDynamicResolution;
Debug.Log($"Initializing FSR2 with max render size: {_initData.maxRenderSizeWidth}x{_initData.maxRenderSizeHeight}, display size: {_initData.displaySizeWidth}x{_initData.displaySizeHeight}, flags: {flags}");
_context.Create(new Fsr2.ContextDescription
{
DisplaySize = new Vector2Int((int)_initData.displaySizeWidth, (int)_initData.displaySizeHeight),
MaxRenderSize = new Vector2Int((int)_initData.maxRenderSizeWidth, (int)_initData.maxRenderSizeHeight),
Flags = flags,
Shaders = assets.shaders,
});
}
internal void Destroy()
{
_context.Destroy();
}
public override void Execute(CommandBuffer cmd, in FSR2CommandExecutionData executeData, in FSR2TextureTable textures)
{
_dispatchDescription.Color = new ResourceView(textures.colorInput);
_dispatchDescription.Depth = new ResourceView(textures.depth);
_dispatchDescription.MotionVectors = new ResourceView(textures.motionVectors);
_dispatchDescription.Exposure = new ResourceView(textures.exposureTexture);
_dispatchDescription.Reactive = new ResourceView(textures.biasColorMask);
_dispatchDescription.TransparencyAndComposition = new ResourceView(textures.transparencyMask);
_dispatchDescription.Output = new ResourceView(textures.colorOutput);
_dispatchDescription.JitterOffset = new Vector2(executeData.jitterOffsetX, executeData.jitterOffsetY);
_dispatchDescription.MotionVectorScale = new Vector2(executeData.MVScaleX, executeData.MVScaleY);
_dispatchDescription.RenderSize = new Vector2Int((int)executeData.renderSizeWidth, (int)executeData.renderSizeHeight);
_dispatchDescription.InputResourceSize = new Vector2Int((int)executeData.renderSizeWidth, (int)executeData.renderSizeHeight);
_dispatchDescription.EnableSharpening = executeData.enableSharpening != 0;
_dispatchDescription.Sharpness = executeData.sharpness;
_dispatchDescription.FrameTimeDelta = executeData.frameTimeDelta / 1000f;
_dispatchDescription.PreExposure = executeData.preExposure;
_dispatchDescription.Reset = executeData.reset != 0;
_dispatchDescription.CameraNear = executeData.cameraNear;
_dispatchDescription.CameraFar = executeData.cameraFar;
_dispatchDescription.CameraFovAngleVertical = executeData.cameraFovAngleVertical;
_dispatchDescription.ViewSpaceToMetersFactor = 1.0f; // 1 unit is 1 meter in Unity
_dispatchDescription.UseTextureArrays = TextureXR.useTexArray && textures.colorInput.dimension == TextureDimension.Tex2DArray;
_context.Dispatch(_dispatchDescription, cmd);
}
}
}