using System; using System.Collections.Generic; using FidelityFX; using FidelityFX.FSR3; using UnityEngine.Experimental.Rendering; #if UNITY_STANDALONE_WIN using AMDUP = UnityEngine.AMD; #endif namespace UnityEngine.Rendering.HighDefinition.AMD.FSR3 { /// /// Custom upscaler plugin that uses the open source port of FSR3 to Unity. /// This eschews using native plugins, instead using portable code that will work on any platform that supports compute shaders. /// public class FSR3UpscalerPlugin: UpscalerPlugin { public static bool EnableDebugView { get; set; } = false; private Fsr3UpscalerAssets _assets; private bool _nativePluginLoaded; public override string name => "FSR 3.1"; public override bool isSupported => SystemInfo.supportsComputeShaders; public override bool includesSharpening => true; public override bool supportsAlpha => !_nativePluginLoaded; // Managed Unity port has alpha upscaling built-in, the native AMD implementation doesn't public override bool Load() { // Guess we're using native plugins after all! // On Windows with supported graphics APIs, we use a drop-in replacement for Unity's plugin that implements FSR 3.1 through AMD's upgradable FidelityFX API. // This allows AMD's GPU driver to hijack the plugin and transparently upgrade our implementation to FSR4 on supported graphics cards! _nativePluginLoaded = false; #if UNITY_STANDALONE_WIN if (!Application.isEditor && SystemInfo.operatingSystemFamily == OperatingSystemFamily.Windows && SystemInfo.graphicsDeviceType is GraphicsDeviceType.Direct3D12 or GraphicsDeviceType.Vulkan) { try { if (AMDUP.AMDUnityPlugin.IsLoaded() || AMDUP.AMDUnityPlugin.Load()) { _nativePluginLoaded = AMDUP.GraphicsDevice.device != null || AMDUP.GraphicsDevice.CreateGraphicsDevice() != null; if (_nativePluginLoaded) return true; } } catch (DllNotFoundException) { _nativePluginLoaded = false; } } #endif if (_assets != null) return true; _assets = Resources.Load("FSR3 Upscaler Assets"); return _assets != null; } public override bool IsLoaded() => _nativePluginLoaded || _assets != null; public override UpscalerContext CreateContext(in FSR2CommandInitializationData initSettings) { #if UNITY_STANDALONE_WIN if (_nativePluginLoaded) { var context = new FSR3NativeUpscalerContext(in initSettings); context.Init(); return context; } else #endif { var context = new FSR3UpscalerContext(in initSettings); context.Init(_assets); return context; } } public override void DestroyContext(UpscalerContext context) { switch (context) { #if UNITY_STANDALONE_WIN case FSR3NativeUpscalerContext nativeContext: nativeContext.Destroy(); break; #endif case FSR3UpscalerContext managedContext: managedContext.Destroy(); break; } } public override bool GetRenderResolutionFromQualityMode(FSR2Quality qualityMode, uint displayWidth, uint displayHeight, out uint renderWidth, out uint renderHeight) { Fsr3Upscaler.GetRenderResolutionFromQualityMode(out int rw, out int rh, (int)displayWidth, (int)displayHeight, (Fsr3Upscaler.QualityMode)((int)qualityMode + 2)); renderWidth = (uint)rw; renderHeight = (uint)rh; return true; } public override float GetUpscaleRatioFromQualityMode(FSR2Quality qualityMode) { return Fsr3Upscaler.GetUpscaleRatioFromQualityMode((Fsr3Upscaler.QualityMode)((int)qualityMode + 2)); } } public class FSR3UpscalerContext : UpscalerContext { private readonly Fsr3UpscalerContext _context = new(); private readonly Fsr3Upscaler.DispatchDescription _dispatchDescription = new(); private readonly FSR2CommandInitializationData _initData; private Texture2DArray _clearTextureArray; internal FSR3UpscalerContext(in FSR2CommandInitializationData initSettings) { _initData = initSettings; } internal void Init(Fsr3UpscalerAssets assets) { Fsr3Upscaler.InitializationFlags flags = 0; if (_initData.GetFlag(FfxFsr2InitializationFlags.EnableHighDynamicRange)) flags |= Fsr3Upscaler.InitializationFlags.EnableHighDynamicRange; if (_initData.GetFlag(FfxFsr2InitializationFlags.EnableDisplayResolutionMotionVectors)) flags |= Fsr3Upscaler.InitializationFlags.EnableDisplayResolutionMotionVectors; if (_initData.GetFlag(FfxFsr2InitializationFlags.EnableMotionVectorsJitterCancellation)) flags |= Fsr3Upscaler.InitializationFlags.EnableMotionVectorsJitterCancellation; if (_initData.GetFlag(FfxFsr2InitializationFlags.DepthInverted)) flags |= Fsr3Upscaler.InitializationFlags.EnableDepthInverted; if (_initData.GetFlag(FfxFsr2InitializationFlags.EnableDepthInfinite)) flags |= Fsr3Upscaler.InitializationFlags.EnableDepthInfinite; if (_initData.GetFlag(FfxFsr2InitializationFlags.EnableAutoExposure)) flags |= Fsr3Upscaler.InitializationFlags.EnableAutoExposure; if (_initData.GetFlag(FfxFsr2InitializationFlags.EnableDynamicResolution)) flags |= Fsr3Upscaler.InitializationFlags.EnableDynamicResolution; //Debug.Log($"Initializing FSR3 with max render size: {_initData.maxRenderSizeWidth}x{_initData.maxRenderSizeHeight}, display size: {_initData.displaySizeWidth}x{_initData.displaySizeHeight}, flags: {flags}"); _context.Create(new Fsr3Upscaler.ContextDescription { MaxUpscaleSize = 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.UpscaleSize = new Vector2Int((int)_initData.displaySizeWidth, (int)_initData.displaySizeHeight); _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.Flags = FSR3UpscalerPlugin.EnableDebugView ? Fsr3Upscaler.DispatchFlags.DrawDebugView : 0; _dispatchDescription.UseTextureArrays = useTextureArrays; _context.Dispatch(_dispatchDescription, cmd); } } #if UNITY_STANDALONE_WIN public class FSR3NativeUpscalerContext : UpscalerContext { private AMDUP.FSR2Context _nativeContext; private readonly FSR2CommandInitializationData _initData; internal FSR3NativeUpscalerContext(in FSR2CommandInitializationData initSettings) { _initData = initSettings; } internal void Init() { AMDUP.FSR2CommandInitializationData initSettings = new() { maxRenderSizeWidth = _initData.maxRenderSizeWidth, maxRenderSizeHeight = _initData.maxRenderSizeHeight, displaySizeWidth = _initData.displaySizeWidth, displaySizeHeight = _initData.displaySizeHeight, ffxFsrFlags = (AMDUP.FfxFsr2InitializationFlags)_initData.ffxFsrFlags, }; CommandBuffer cmd = new(); _nativeContext = AMDUP.GraphicsDevice.device.CreateFeature(cmd, in initSettings); Graphics.ExecuteCommandBuffer(cmd); cmd.Release(); } internal void Destroy() { if (_nativeContext == null) return; CommandBuffer cmd = new(); AMDUP.GraphicsDevice.device.DestroyFeature(cmd, _nativeContext); Graphics.ExecuteCommandBuffer(cmd); cmd.Release(); _nativeContext = null; } public override void Execute(CommandBuffer cmd, in FSR2CommandExecutionData executeData, in FSR2TextureTable textures) { ref var execData = ref _nativeContext.executeData; execData.jitterOffsetX = executeData.jitterOffsetX; execData.jitterOffsetY = executeData.jitterOffsetY; execData.MVScaleX = executeData.MVScaleX; execData.MVScaleY = executeData.MVScaleY; execData.renderSizeWidth = executeData.renderSizeWidth; execData.renderSizeHeight = executeData.renderSizeHeight; execData.enableSharpening = executeData.enableSharpening; execData.sharpness = executeData.sharpness; execData.frameTimeDelta = executeData.frameTimeDelta; execData.preExposure = executeData.preExposure; execData.reset = executeData.reset; execData.cameraNear = executeData.cameraNear; execData.cameraFar = executeData.cameraFar; execData.cameraFovAngleVertical = executeData.cameraFovAngleVertical; AMDUP.FSR2TextureTable textureTable = new() { colorInput = textures.colorInput, depth = textures.depth, motionVectors = textures.motionVectors, exposureTexture = textures.exposureTexture, biasColorMask = textures.biasColorMask, reactiveMask = textures.reactiveMask, transparencyMask = textures.transparencyMask, colorOutput = textures.colorOutput, }; AMDUP.GraphicsDevice.device.ExecuteFSR2(cmd, _nativeContext, in textureTable); } } #endif }