From b99782a22df4ed9c0c034dff089cedf936617278 Mon Sep 17 00:00:00 2001 From: Nico de Poel Date: Wed, 5 Jun 2024 12:48:44 +0200 Subject: [PATCH] Implemented some of the plugin swapping logic, refactored some bits and added a bit of documentation. --- .../Upscalers/FSR2WrapperUpscaler.cs | 16 +++- .../RenderPass/Upscalers/FSR3Upscaler.cs | 28 +++++- .../RenderPass/Upscalers/UpscalerPlugin.cs | 88 +++++++++++++++++-- 3 files changed, 119 insertions(+), 13 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/Upscalers/FSR2WrapperUpscaler.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/Upscalers/FSR2WrapperUpscaler.cs index db35fd5a..6243751f 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/Upscalers/FSR2WrapperUpscaler.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/Upscalers/FSR2WrapperUpscaler.cs @@ -2,14 +2,24 @@ namespace UnityEngine.Rendering.HighDefinition.AMD.FSR2Wrapper { + /// + /// Wrapper plugin that simply forwards calls to the original Unity native plugin implementation of FSR2. + /// Use this to verify other plugins against Unity's reference implementation. + /// public class FSR2WrapperUpscaler: UpscalerPlugin { + public override string name => "FSR 2.1"; + + public override bool isSupported => IsLoaded(); + public override bool Load() => UnityEngine.AMD.AMDUnityPlugin.Load(); public override bool IsLoaded() => UnityEngine.AMD.AMDUnityPlugin.IsLoaded(); public override GraphicsDevice CreateGraphicsDevice() => new FSR2WrappedGraphicsDevice(UnityEngine.AMD.GraphicsDevice.CreateGraphicsDevice()); + public override void DestroyGraphicsDevice() { } // Noop, native plugin does not allow clearing the graphics device + public override GraphicsDevice device => new FSR2WrappedGraphicsDevice(UnityEngine.AMD.GraphicsDevice.device); public override uint version => UnityEngine.AMD.GraphicsDevice.version; @@ -44,14 +54,12 @@ namespace UnityEngine.Rendering.HighDefinition.AMD.FSR2Wrapper public override bool GetRenderResolutionFromQualityMode(FSR2Quality qualityMode, uint displayWidth, uint displayHeight, out uint renderWidth, out uint renderHeight) { - var quality = (UnityEngine.AMD.FSR2Quality)Math.Max(0, (int)qualityMode - 2); - return _wrappedDevice.GetRenderResolutionFromQualityMode(quality, displayWidth, displayHeight, out renderWidth, out renderHeight); + return _wrappedDevice.GetRenderResolutionFromQualityMode((UnityEngine.AMD.FSR2Quality)qualityMode, displayWidth, displayHeight, out renderWidth, out renderHeight); } public override float GetUpscaleRatioFromQualityMode(FSR2Quality qualityMode) { - var quality = (UnityEngine.AMD.FSR2Quality)Math.Max(0, (int)qualityMode - 2); - return _wrappedDevice.GetUpscaleRatioFromQualityMode(quality); + return _wrappedDevice.GetUpscaleRatioFromQualityMode((UnityEngine.AMD.FSR2Quality)qualityMode); } } diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/Upscalers/FSR3Upscaler.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/Upscalers/FSR3Upscaler.cs index e5b81547..3655235a 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/Upscalers/FSR3Upscaler.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/Upscalers/FSR3Upscaler.cs @@ -3,10 +3,18 @@ using FidelityFX; 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 { private static FSR3GraphicsDevice sGraphicsDeviceInstance; - + + public override string name => "FSR 3.0"; + + public override bool isSupported => SystemInfo.supportsComputeShaders; + public override bool Load() => true; public override bool IsLoaded() => true; @@ -31,6 +39,15 @@ namespace UnityEngine.Rendering.HighDefinition.AMD.FSR3 return null; } + public override void DestroyGraphicsDevice() + { + if (sGraphicsDeviceInstance != null) + { + sGraphicsDeviceInstance.Shutdown(); + sGraphicsDeviceInstance = null; + } + } + public override GraphicsDevice device => sGraphicsDeviceInstance; } @@ -51,19 +68,22 @@ namespace UnityEngine.Rendering.HighDefinition.AMD.FSR3 internal void Shutdown() { + foreach (var context in _contextPool) + { + context.Reset(); + } + if (_assets != null) { Resources.UnloadAsset(_assets); _assets = null; } - - // TODO? destroy all FSR3 contexts on the stack } public override FSR2Context CreateFeature(CommandBuffer cmd, in FSR2CommandInitializationData initSettings) { var context = _contextPool.Count != 0 ? _contextPool.Pop() : new FSR3Context(); - context.Init(initSettings, _assets); // TODO might need some way to distinguish between contexts (see featureSlot) + context.Init(initSettings, _assets); return context; } diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/Upscalers/UpscalerPlugin.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/Upscalers/UpscalerPlugin.cs index ca2d4969..08f44c9b 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/Upscalers/UpscalerPlugin.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/Upscalers/UpscalerPlugin.cs @@ -1,25 +1,105 @@ using System; +using System.Collections.Generic; +// This is placed in an AMD sub-namespace, to trick HDRP into using classes from here instead of from the UnityEngine.AMD namespace. +// This allows us to intercept HDRP's upscaler calls and forward them to our own implementations, without having to modify the HDRP code itself. namespace UnityEngine.Rendering.HighDefinition.AMD { public static class AMDUnityPlugin { - // TODO: allow dynamic switching between plugins (including shutdown of previous plugin) - // internal static readonly UpscalerPlugin ActivePlugin = new FSR2Wrapper.FSR2WrapperUpscaler(); - internal static readonly UpscalerPlugin ActivePlugin = new FSR3.FSR3UpscalerPlugin(); + private static readonly List AvailablePlugins = new() + { + new FSR3.FSR3UpscalerPlugin(), + new FSR2Wrapper.FSR2WrapperUpscaler(), + }; + + internal static UpscalerPlugin ActivePlugin = AvailablePlugins[0]; public static bool Load() => ActivePlugin.Load(); public static bool IsLoaded() => ActivePlugin.IsLoaded(); + + /// + /// Get the list of available upscaler plugins, as a read-only list. + /// Use this to display the available options and retrieve the index of the plugin you want to activate. + /// + public static IReadOnlyList GetAvailablePlugins() + { + return AvailablePlugins.AsReadOnly(); + } + + /// + /// Adds an upscaler plugin to the list of available plugins. + /// If a plugin of the same type already exists in the list, then the new plugin won't be added and the index of the existing plugin will be returned instead. + /// + /// The plugin to add + /// The index in the plugins list where the plugin can be found. Returns a negative number if the plugin could not be added. + public static int AddPlugin(UpscalerPlugin plugin) + { + if (plugin == null) + return -1; + + // Check if we already have a plugin of the same type + var pluginType = plugin.GetType(); + for (int i = 0; i < AvailablePlugins.Count; ++i) + { + if (AvailablePlugins[i].GetType() == pluginType) + return i; + } + + AvailablePlugins.Add(plugin); + return AvailablePlugins.Count - 1; + } + + /// + /// Sets the currently active upscaler plugin. + /// This deactivates the previously active plugin and creates a new device for the newly activated plugin. + /// If the chosen plugin is already active, then this won't do anything. + /// + /// The index in the available plugin list of the upscaler plugin to activate. + /// Whether the plugin was successfully changed and activated. + public static bool SetActivePlugin(int pluginIndex) + { + if (pluginIndex < 0 || pluginIndex >= AvailablePlugins.Count) + return false; + + var newPlugin = AvailablePlugins[pluginIndex]; + if (newPlugin == null || !newPlugin.isSupported) + return false; + + if (newPlugin == ActivePlugin) + return true; + + // If HDRP has previously created a graphics device, then it'll expect that to remain in place. + // Hence, make sure we automatically create a graphics device for the new plugin. + if (ActivePlugin != null && ActivePlugin.device != null) + { + var newDevice = newPlugin.CreateGraphicsDevice(); + if (newDevice == null) + return false; + + ActivePlugin.DestroyGraphicsDevice(); + // TODO do we need to create a new "Feature" as well? Does HDRP cache this anywhere? + } + + ActivePlugin = newPlugin; + return true; + } } public abstract class UpscalerPlugin { + public abstract string name { get; } + + public abstract bool isSupported { get; } + public abstract bool Load(); public abstract bool IsLoaded(); public abstract GraphicsDevice CreateGraphicsDevice(); + + public abstract void DestroyGraphicsDevice(); public abstract GraphicsDevice device { get; } @@ -108,8 +188,6 @@ namespace UnityEngine.Rendering.HighDefinition.AMD public enum FSR2Quality { - NativeAA, - UltraQuality, Quality, Balanced, Performance,