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 { 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; } public virtual uint version => 0x00; } public abstract class GraphicsDevice { public static GraphicsDevice device => AMDUnityPlugin.ActivePlugin.device; public static uint version => AMDUnityPlugin.ActivePlugin.version; public static GraphicsDevice CreateGraphicsDevice() { return AMDUnityPlugin.ActivePlugin.CreateGraphicsDevice(); } public abstract FSR2Context CreateFeature(CommandBuffer cmd, in FSR2CommandInitializationData initSettings); public abstract void DestroyFeature(CommandBuffer cmd, FSR2Context fsrContext); public abstract void ExecuteFSR2(CommandBuffer cmd, FSR2Context fsrContext, in FSR2TextureTable textures); public abstract bool GetRenderResolutionFromQualityMode(FSR2Quality qualityMode, uint displayWidth, uint displayHeight, out uint renderWidth, out uint renderHeight); public abstract float GetUpscaleRatioFromQualityMode(FSR2Quality qualityMode); } public abstract class FSR2Context { public abstract ref FSR2CommandInitializationData initData { get; } public abstract ref FSR2CommandExecutionData executeData { get; } } public struct FSR2CommandInitializationData { public uint displaySizeHeight; public uint displaySizeWidth; public FfxFsr2InitializationFlags ffxFsrFlags; public uint maxRenderSizeHeight; public uint maxRenderSizeWidth; public readonly bool GetFlag(FfxFsr2InitializationFlags flag) { return (ffxFsrFlags & flag) == flag; } public void SetFlag(FfxFsr2InitializationFlags flag, bool value) { if (value) ffxFsrFlags |= flag; else ffxFsrFlags &= ~flag; } } [Flags] public enum FfxFsr2InitializationFlags { EnableHighDynamicRange = 1, EnableDisplayResolutionMotionVectors = 2, EnableMotionVectorsJitterCancellation = 4, DepthInverted = 8, EnableDepthInfinite = 16, // 0x00000010 EnableAutoExposure = 32, // 0x00000020 EnableDynamicResolution = 64, // 0x00000040 EnableTexture1DUsage = 128, // 0x00000080 } public struct FSR2TextureTable { public Texture biasColorMask; public Texture colorInput; public Texture colorOutput; public Texture depth; public Texture exposureTexture; public Texture motionVectors; public Texture reactiveMask; public Texture transparencyMask; } public enum FSR2Quality { Quality, Balanced, Performance, UltraPerformance, } public struct FSR2CommandExecutionData { public float jitterOffsetX; public float jitterOffsetY; public float MVScaleX; public float MVScaleY; public uint renderSizeWidth; public uint renderSizeHeight; public int enableSharpening; public float sharpness; public float frameTimeDelta; public float preExposure; public int reset; public float cameraNear; public float cameraFar; public float cameraFovAngleVertical; } }