using System; #if UNITY_STANDALONE_WIN using AMDUP = UnityEngine.AMD; #endif using FidelityFX.FSR3; namespace UnityEngine.Rendering.HighDefinition.AMD.FSR4 { /// /// Custom upscaler plugin that uses a native plugin to integrate FSR4 into Unity. /// public class FSR4UpscalerPlugin: UpscalerPlugin { public override string name => "FSR 4.0"; public override bool isSupported => SystemInfo.supportsComputeShaders && SystemInfo.operatingSystemFamily == OperatingSystemFamily.Windows && SystemInfo.graphicsDeviceType == GraphicsDeviceType.Direct3D12 && SystemInfo.graphicsDeviceName.StartsWith("AMD Radeon RX 90"); // Limited to only AMD RDNA4 cards (RX 9000 series) at the moment public override bool includesSharpening => true; public override bool isMLBased => true; public override bool supportsAlpha => false; private bool _nativePluginLoaded; public override bool Load() { _nativePluginLoaded = false; #if UNITY_STANDALONE_WIN 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 return _nativePluginLoaded; } public override bool IsLoaded() => _nativePluginLoaded; public override UpscalerContext CreateContext(in FSR2CommandInitializationData initSettings) { if (_nativePluginLoaded) { var context = new FSR4NativeUpscalerContext(in initSettings); context.Init(); return context; } return null; } public override void DestroyContext(UpscalerContext context) { switch (context) { case FSR4NativeUpscalerContext nativeContext: nativeContext.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 FSR4NativeUpscalerContext : UpscalerContext { #if UNITY_STANDALONE_WIN // We reuse the native interop from Unity's AMD module, hence the references to FSR2 here private AMDUP.FSR2Context _nativeContext; private readonly FSR2CommandInitializationData _initData; internal FSR4NativeUpscalerContext(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); } #else internal FSR4NativeUpscalerContext(in FSR2CommandInitializationData initSettings) { } internal void Init() { } internal void Destroy() { } public override void Execute(CommandBuffer cmd, in FSR2CommandExecutionData executeData, in FSR2TextureTable textures) { } #endif } }