using System.Collections.Generic; using FidelityFX; using FidelityFX.FSR2; using UnityEngine.Experimental.Rendering; 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 includesSharpening => true; public override bool supportsAlpha => true; 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; private Texture2DArray _clearTextureArray; 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, }); _clearTextureArray = new Texture2DArray(1, 1, TextureXR.slices, GraphicsFormat.R32G32_SFloat, TextureCreationFlags.None); for (int i = 0; i < TextureXR.slices; ++i) _clearTextureArray.SetPixels(new[] { Color.clear, }, i); _clearTextureArray.Apply(); } internal void Destroy() { CoreUtils.Destroy(_clearTextureArray); _context.Destroy(); } public override void Execute(CommandBuffer cmd, in FSR2CommandExecutionData executeData, in FSR2TextureTable textures) { bool useTextureArrays = TextureXR.useTexArray && textures.colorInput.dimension == TextureDimension.Tex2DArray; ResourceView clearTexture = useTextureArrays ? new ResourceView(_clearTextureArray) : new ResourceView(); _dispatchDescription.Color = new ResourceView(textures.colorInput); _dispatchDescription.Depth = new ResourceView(textures.depth); _dispatchDescription.MotionVectors = new ResourceView(textures.motionVectors); _dispatchDescription.Exposure = textures.exposureTexture != null ? new ResourceView(textures.exposureTexture) : new ResourceView(); _dispatchDescription.Reactive = textures.biasColorMask != null ? new ResourceView(textures.biasColorMask) : clearTexture; _dispatchDescription.TransparencyAndComposition = textures.transparencyMask != null ? new ResourceView(textures.transparencyMask) : clearTexture; _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(textures.colorInput.width, textures.colorInput.height); _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 = useTextureArrays; _context.Dispatch(_dispatchDescription, cmd); } } }