You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

277 lines
11 KiB

using System;
using System.Collections.Generic;
using FidelityFX;
// We use an old C# trick here to override the UnityEngine.AMD namespace and force FSR2Pass to use code from this namespace instead.
// By exactly mimicking the interface of the AMDUnityPlugin module, we can replace Unity's implementation of FSR2 with one of our own.
// Here we redirect all calls to the open source FSR3 Upscaler port with a bit of glue logic, making the entire thing cross-platform compatible.
namespace UnityEngine.Rendering.HighDefinition.AMD
{
public static class AMDUnityPlugin
{
internal static Fsr3UpscalerAssets Assets;
static AMDUnityPlugin()
{
_ = Load();
}
public static bool Load()
{
Unload();
Assets = Resources.Load<Fsr3UpscalerAssets>("Fsr3UpscalerAssets");
return Assets != null;
}
public static bool IsLoaded() => Assets != null;
internal static void Unload()
{
if (Assets != null)
{
Resources.UnloadAsset(Assets);
Assets = null;
}
}
}
public class GraphicsDevice
{
private static GraphicsDevice sGraphicsDevice;
public static GraphicsDevice device => sGraphicsDevice;
public static int version => 0x00;
private readonly List<FSR2Context> _contexts = new();
public static GraphicsDevice CreateGraphicsDevice()
{
if (sGraphicsDevice != null)
{
sGraphicsDevice.Destroy();
sGraphicsDevice = null;
}
var graphicsDevice = new GraphicsDevice();
if (graphicsDevice.Initialize())
{
sGraphicsDevice = graphicsDevice;
return graphicsDevice;
}
Debug.LogWarning("Failed to initialize FSR3 Upscaler Graphics Device");
return null;
}
private bool Initialize()
{
return AMDUnityPlugin.Load();
}
private void Destroy()
{
foreach (var context in _contexts)
{
context.Destroy();
}
_contexts.Clear();
AMDUnityPlugin.Unload();
}
public FSR2Context CreateFeature(CommandBuffer cmd, in FSR2CommandInitializationData initSettings)
{
var context = new FSR2Context();
if (!context.Initialize(initSettings))
return null;
_contexts.Add(context);
return context;
}
public void DestroyFeature(CommandBuffer cmd, FSR2Context fsrContext)
{
if (fsrContext == null)
return;
fsrContext.Destroy();
_contexts.Remove(fsrContext);
}
public void ExecuteFSR2(CommandBuffer cmd, FSR2Context fsrContext, in FSR2TextureTable textures)
{
if (fsrContext == null)
return;
fsrContext.Execute(cmd, textures);
}
public 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, ConvertQualityMode(qualityMode));
renderWidth = (uint)rw;
renderHeight = (uint)rh;
return true;
}
public float GetUpscaleRatioFromQualityMode(FSR2Quality qualityMode)
{
return Fsr3Upscaler.GetUpscaleRatioFromQualityMode(ConvertQualityMode(qualityMode));
}
private static Fsr3Upscaler.QualityMode ConvertQualityMode(FSR2Quality qualityMode)
{
// FSR3 offers two more quality modes (Native AA and Ultra Quality) than standard FSR2, so a conversion is needed
const int diff = (int)Fsr3Upscaler.QualityMode.Quality - (int)FSR2Quality.Quality;
return (Fsr3Upscaler.QualityMode)((int)qualityMode + diff);
}
}
public class FSR2Context
{
private FSR2CommandInitializationData _initData;
public ref readonly FSR2CommandInitializationData initData => ref _initData;
private FSR2CommandExecutionData _executeData;
public ref FSR2CommandExecutionData executeData => ref _executeData;
private readonly Fsr3UpscalerContext _context = new();
private readonly Fsr3Upscaler.DispatchDescription _dispatchDescription = new();
internal bool Initialize(in FSR2CommandInitializationData initSettings)
{
_initData = initSettings;
_executeData = new FSR2CommandExecutionData();
if (AMDUnityPlugin.Assets == null || AMDUnityPlugin.Assets.shaders == null)
return false;
Fsr3Upscaler.InitializationFlags flags = 0;
if (initSettings.GetFlag(FfxFsr2InitializationFlags.EnableHighDynamicRange)) flags |= Fsr3Upscaler.InitializationFlags.EnableHighDynamicRange;
if (initSettings.GetFlag(FfxFsr2InitializationFlags.EnableDisplayResolutionMotionVectors)) flags |= Fsr3Upscaler.InitializationFlags.EnableDisplayResolutionMotionVectors;
if (initSettings.GetFlag(FfxFsr2InitializationFlags.EnableMotionVectorsJitterCancellation)) flags |= Fsr3Upscaler.InitializationFlags.EnableMotionVectorsJitterCancellation;
if (initSettings.GetFlag(FfxFsr2InitializationFlags.DepthInverted)) flags |= Fsr3Upscaler.InitializationFlags.EnableDepthInverted;
if (initSettings.GetFlag(FfxFsr2InitializationFlags.EnableDepthInfinite)) flags |= Fsr3Upscaler.InitializationFlags.EnableDepthInfinite;
if (initSettings.GetFlag(FfxFsr2InitializationFlags.EnableAutoExposure)) flags |= Fsr3Upscaler.InitializationFlags.EnableAutoExposure;
if (initSettings.GetFlag(FfxFsr2InitializationFlags.EnableDynamicResolution)) flags |= Fsr3Upscaler.InitializationFlags.EnableDynamicResolution;
var contextDescription = new Fsr3Upscaler.ContextDescription
{
Flags = flags,
MaxRenderSize = new Vector2Int((int)initSettings.maxRenderSizeWidth, (int)initSettings.maxRenderSizeHeight),
DisplaySize = new Vector2Int((int)initSettings.displaySizeWidth, (int)initSettings.displaySizeHeight),
Shaders = AMDUnityPlugin.Assets.shaders,
};
_context.Create(contextDescription);
return true;
}
internal void Destroy()
{
_context.Destroy();
}
internal void Execute(CommandBuffer cmd, in FSR2TextureTable textures)
{
_dispatchDescription.Color = new ResourceView(textures.colorInput);
_dispatchDescription.Depth = new ResourceView(textures.depth);
_dispatchDescription.MotionVectors = new ResourceView(textures.motionVectors);
_dispatchDescription.Exposure = new ResourceView(textures.exposureTexture);
_dispatchDescription.Reactive = new ResourceView(textures.biasColorMask);
_dispatchDescription.TransparencyAndComposition = new ResourceView(textures.transparencyMask);
_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((int)_executeData.renderSizeWidth, (int)_executeData.renderSizeHeight);
_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 == 1 meter in Unity
_context.Dispatch(_dispatchDescription, cmd);
}
}
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;
}
}
public struct FSR2CommandExecutionData
{
public float cameraFar;
public float cameraFovAngleVertical;
public float cameraNear;
public int enableSharpening;
public float frameTimeDelta;
public float jitterOffsetX;
public float jitterOffsetY;
public float MVScaleX;
public float MVScaleY;
public float preExposure;
public uint renderSizeHeight;
public uint renderSizeWidth;
public int reset;
public float sharpness;
}
public struct FSR2TextureTable
{
public Texture biasColorMask;
public Texture colorInput;
public Texture colorOutput;
public Texture depth;
public Texture exposureTexture;
public Texture motionVectors;
public Texture reactiveMask; // Note: reactiveMask does not seem to be used at all by HDRP, instead we get a biasColorMask
public Texture transparencyMask;
}
[Flags]
public enum FfxFsr2InitializationFlags
{
EnableHighDynamicRange = 1 << 0,
EnableDisplayResolutionMotionVectors = 1 << 1,
EnableMotionVectorsJitterCancellation = 1 << 2,
DepthInverted = 1 << 3,
EnableDepthInfinite = 1 << 4,
EnableAutoExposure = 1 << 5,
EnableDynamicResolution = 1 << 6,
EnableTexture1DUsage = 1 << 7,
}
public enum FSR2Quality
{
Quality,
Balanced,
Performance,
UltraPerformance,
}
}