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.
318 lines
12 KiB
318 lines
12 KiB
using System;
|
|
using System.Runtime.InteropServices;
|
|
|
|
#if UNITY_STANDALONE_WIN
|
|
namespace UnityEngine.Rendering.HighDefinition.AMD.XeSS
|
|
{
|
|
public class XeSSUpscalerPlugin: UpscalerPlugin
|
|
{
|
|
public override string name => "XeSS 2.1";
|
|
public override bool isSupported => SystemInfo.graphicsDeviceType == GraphicsDeviceType.Direct3D12;
|
|
public override bool isMLBased => true;
|
|
|
|
private static bool _libraryLoaded = false;
|
|
internal static int BaseEventId = -1;
|
|
|
|
public override bool Load()
|
|
{
|
|
if (_libraryLoaded)
|
|
return true;
|
|
|
|
if (!XeSSLibrary.InitApi())
|
|
return false;
|
|
|
|
BaseEventId = XeSSLibrary.GetBaseEventId();
|
|
_libraryLoaded = BaseEventId >= 0;
|
|
return _libraryLoaded;
|
|
}
|
|
|
|
public override bool IsLoaded()
|
|
{
|
|
return _libraryLoaded;
|
|
}
|
|
|
|
~XeSSUpscalerPlugin()
|
|
{
|
|
if (_libraryLoaded)
|
|
{
|
|
XeSSLibrary.ShutdownApi();
|
|
_libraryLoaded = false;
|
|
}
|
|
}
|
|
|
|
public override UpscalerContext CreateContext(in FSR2CommandInitializationData initSettings)
|
|
{
|
|
XeSSUpscalerContext context = new();
|
|
context.Init(in initSettings);
|
|
return context;
|
|
}
|
|
|
|
public override void DestroyContext(UpscalerContext context)
|
|
{
|
|
((XeSSUpscalerContext)context).Destroy();
|
|
}
|
|
|
|
public override bool GetRenderResolutionFromQualityMode(FSR2Quality qualityMode, uint displayWidth, uint displayHeight, out uint renderWidth, out uint renderHeight)
|
|
{
|
|
float ratio = GetUpscaleRatioFromQualityMode(qualityMode);
|
|
renderWidth = (uint)Mathf.RoundToInt(displayWidth / ratio);
|
|
renderHeight = (uint)Mathf.RoundToInt(displayHeight / ratio);
|
|
return true;
|
|
}
|
|
|
|
public override float GetUpscaleRatioFromQualityMode(FSR2Quality qualityMode)
|
|
{
|
|
return XeSSLibrary.ConvertQuality(qualityMode) switch
|
|
{
|
|
XeSSLibrary.QualitySetting.NativeAA => 1.0f,
|
|
XeSSLibrary.QualitySetting.UltraQualityPlus => 1.3f,
|
|
XeSSLibrary.QualitySetting.UltraQuality => 1.5f,
|
|
XeSSLibrary.QualitySetting.Quality => 1.7f,
|
|
XeSSLibrary.QualitySetting.Balanced => 2.0f,
|
|
XeSSLibrary.QualitySetting.Performance => 2.3f,
|
|
XeSSLibrary.QualitySetting.UltraPerformance => 3.0f,
|
|
_ => 1.0f,
|
|
};
|
|
}
|
|
}
|
|
|
|
public class XeSSUpscalerContext : UpscalerContext
|
|
{
|
|
private readonly NativeData<XeSSLibrary.InitParams> _nativeInitParams = new();
|
|
private readonly NativeData<XeSSLibrary.ExecuteParams> _nativeExecParams = new();
|
|
|
|
private bool _initialized;
|
|
|
|
internal void Init(in FSR2CommandInitializationData initSettings)
|
|
{
|
|
XeSSLibrary.InitFlags initFlags = XeSSLibrary.InitFlags.ResponsivePixelMask;
|
|
if (initSettings.GetFlag(FfxFsr2InitializationFlags.DepthInverted)) initFlags |= XeSSLibrary.InitFlags.InvertedDepth;
|
|
if (initSettings.GetFlag(FfxFsr2InitializationFlags.EnableDisplayResolutionMotionVectors)) initFlags |= XeSSLibrary.InitFlags.HighResMotionVectors;
|
|
if (initSettings.GetFlag(FfxFsr2InitializationFlags.EnableMotionVectorsJitterCancellation)) initFlags |= XeSSLibrary.InitFlags.JitteredMotionVectors;
|
|
if (!initSettings.GetFlag(FfxFsr2InitializationFlags.EnableHighDynamicRange)) initFlags |= XeSSLibrary.InitFlags.LDRInputColor;
|
|
if (initSettings.GetFlag(FfxFsr2InitializationFlags.EnableAutoExposure)) initFlags |= XeSSLibrary.InitFlags.EnableAutoExposure;
|
|
|
|
//Debug.Log($"Setting up XeSS with input size: {initSettings.maxRenderSizeWidth}x{initSettings.maxRenderSizeHeight}, output size: {initSettings.displaySizeWidth}x{initSettings.displaySizeHeight}, flags: {initFlags}");
|
|
|
|
ref var initParams = ref _nativeInitParams.Value;
|
|
initParams.maxRenderSizeWidth = initSettings.maxRenderSizeWidth;
|
|
initParams.maxRenderSizeHeight = initSettings.maxRenderSizeHeight;
|
|
initParams.displaySizeWidth = initSettings.displaySizeWidth;
|
|
initParams.displaySizeHeight = initSettings.displaySizeHeight;
|
|
initParams.flags = (int)initFlags;
|
|
initParams.featureSlot = _nativeExecParams.Value.featureSlot = XeSSLibrary.CreateFeatureSlot();
|
|
|
|
using CommandBuffer cmd = new();
|
|
cmd.IssuePluginEventAndData(XeSSLibrary.GetRenderEventAndDataFunc(), XeSSUpscalerPlugin.BaseEventId + (int)XeSSLibrary.PluginEvent.Init, _nativeInitParams.Ptr);
|
|
Graphics.ExecuteCommandBuffer(cmd);
|
|
|
|
_initialized = true;
|
|
}
|
|
|
|
internal void Destroy()
|
|
{
|
|
if (_initialized)
|
|
{
|
|
using CommandBuffer cmd = new();
|
|
cmd.IssuePluginEventAndData(XeSSLibrary.GetRenderEventAndDataFunc(), XeSSUpscalerPlugin.BaseEventId + (int)XeSSLibrary.PluginEvent.Destroy, (IntPtr)_nativeInitParams.Value.featureSlot);
|
|
Graphics.ExecuteCommandBuffer(cmd);
|
|
|
|
_initialized = false;
|
|
}
|
|
|
|
_nativeInitParams.Dispose();
|
|
_nativeExecParams.Dispose();
|
|
}
|
|
|
|
public override void Execute(CommandBuffer cmd, in FSR2CommandExecutionData executeData, in FSR2TextureTable textures)
|
|
{
|
|
cmd.BeginSample("XeSS");
|
|
|
|
SetTexture(cmd, XeSSLibrary.Texture.ColorInput, textures.colorInput, true);
|
|
SetTexture(cmd, XeSSLibrary.Texture.Depth, textures.depth);
|
|
SetTexture(cmd, XeSSLibrary.Texture.MotionVectors, textures.motionVectors);
|
|
SetTexture(cmd, XeSSLibrary.Texture.ExposureTexture, textures.exposureTexture);
|
|
SetTexture(cmd, XeSSLibrary.Texture.ReactiveMask, textures.reactiveMask);
|
|
SetTexture(cmd, XeSSLibrary.Texture.BiasColorMask, textures.biasColorMask);
|
|
SetTexture(cmd, XeSSLibrary.Texture.TransparencyMask, textures.transparencyMask);
|
|
SetTexture(cmd, XeSSLibrary.Texture.ColorOutput, textures.colorOutput);
|
|
|
|
ref var execParams = ref _nativeExecParams.Value;
|
|
execParams.jitterOffsetX = executeData.jitterOffsetX;
|
|
execParams.jitterOffsetY = executeData.jitterOffsetY;
|
|
execParams.MVScaleX = executeData.MVScaleX;
|
|
execParams.MVScaleY = executeData.MVScaleY;
|
|
execParams.renderSizeWidth = executeData.renderSizeWidth;
|
|
execParams.renderSizeHeight = executeData.renderSizeHeight;
|
|
execParams.preExposure = executeData.preExposure;
|
|
execParams.reset = executeData.reset;
|
|
|
|
cmd.IssuePluginEventAndData(XeSSLibrary.GetRenderEventAndDataFunc(), XeSSUpscalerPlugin.BaseEventId + (int)XeSSLibrary.PluginEvent.Execute, _nativeExecParams.Ptr);
|
|
cmd.EndSample("XeSS");
|
|
}
|
|
|
|
private void SetTexture(CommandBuffer cmd, XeSSLibrary.Texture textureSlot, Texture texture, bool clearTextureTable = false)
|
|
{
|
|
if (texture == null)
|
|
return;
|
|
|
|
uint userData = (_nativeInitParams.Value.featureSlot & ushort.MaxValue) << 16 | ((uint)textureSlot & (uint)short.MaxValue) << 1 | (clearTextureTable ? 1u : 0u);
|
|
cmd.IssuePluginCustomTextureUpdateV2(XeSSLibrary.GetSetTextureEventFunc(), texture, userData);
|
|
}
|
|
}
|
|
|
|
internal static class XeSSLibrary
|
|
{
|
|
private const string DllName = "XeSS2UnityPlugin";
|
|
|
|
[DllImport(DllName, EntryPoint = "XeSSUP_InitApi")]
|
|
public static extern bool InitApi();
|
|
|
|
[DllImport(DllName, EntryPoint = "XeSSUP_ShutdownApi")]
|
|
public static extern void ShutdownApi();
|
|
|
|
[DllImport(DllName, EntryPoint = "XeSSUP_GetRenderEventCallback")]
|
|
public static extern IntPtr GetRenderEventAndDataFunc();
|
|
|
|
[DllImport(DllName, EntryPoint = "XeSSUP_GetSetTextureEventCallback")]
|
|
public static extern IntPtr GetSetTextureEventFunc();
|
|
|
|
[DllImport(DllName, EntryPoint = "XeSSUP_CreateFeatureSlot")]
|
|
public static extern uint CreateFeatureSlot();
|
|
|
|
[DllImport(DllName, EntryPoint = "XeSSUP_GetBaseEventId")]
|
|
public static extern int GetBaseEventId();
|
|
|
|
public enum PluginEvent
|
|
{
|
|
Destroy,
|
|
Execute,
|
|
PostExecute,
|
|
Init,
|
|
};
|
|
|
|
public enum QualitySetting
|
|
{
|
|
UltraPerformance = 100,
|
|
Performance = 101,
|
|
Balanced = 102,
|
|
Quality = 103,
|
|
UltraQuality = 104,
|
|
UltraQualityPlus = 105,
|
|
NativeAA = 106,
|
|
}
|
|
|
|
[Flags]
|
|
public enum InitFlags
|
|
{
|
|
None = 0,
|
|
/** Use motion vectors at target resolution. */
|
|
HighResMotionVectors = 1 << 0,
|
|
/** Use inverted (increased precision) depth encoding */
|
|
InvertedDepth = 1 << 1,
|
|
/** Use exposure texture to scale input color. */
|
|
ExposureScaleTexture = 1 << 2,
|
|
/** Use responsive pixel mask texture. */
|
|
ResponsivePixelMask = 1 << 3,
|
|
/** Use velocity in NDC */
|
|
UseNDCVelocity = 1 << 4,
|
|
/** Use external descriptor heap */
|
|
ExternalDescriptorHeap = 1 << 5,
|
|
/** Disable tonemapping for input and output */
|
|
LDRInputColor = 1 << 6,
|
|
/** Remove jitter from input velocity*/
|
|
JitteredMotionVectors = 1 << 7,
|
|
/** Enable automatic exposure calculation. */
|
|
EnableAutoExposure = 1 << 8
|
|
}
|
|
|
|
public enum Texture
|
|
{
|
|
ColorInput,
|
|
ColorOutput,
|
|
Depth,
|
|
MotionVectors,
|
|
TransparencyMask,
|
|
ExposureTexture,
|
|
ReactiveMask,
|
|
BiasColorMask,
|
|
};
|
|
|
|
[Serializable, StructLayout(LayoutKind.Sequential)]
|
|
public struct InitParams
|
|
{
|
|
public uint maxRenderSizeWidth;
|
|
public uint maxRenderSizeHeight;
|
|
public uint displaySizeWidth;
|
|
public uint displaySizeHeight;
|
|
public int flags;
|
|
|
|
public uint featureSlot;
|
|
}
|
|
|
|
[Serializable, StructLayout(LayoutKind.Sequential)]
|
|
public struct ExecuteParams
|
|
{
|
|
public float jitterOffsetX;
|
|
public float jitterOffsetY;
|
|
public float MVScaleX;
|
|
public float MVScaleY;
|
|
public uint renderSizeWidth;
|
|
public uint renderSizeHeight;
|
|
public float preExposure;
|
|
public int reset;
|
|
|
|
public uint featureSlot;
|
|
}
|
|
|
|
public static QualitySetting ConvertQuality(FSR2Quality quality)
|
|
{
|
|
switch (quality)
|
|
{
|
|
case FSR2Quality.Quality:
|
|
return QualitySetting.Quality;
|
|
case FSR2Quality.Balanced:
|
|
return QualitySetting.Balanced;
|
|
case FSR2Quality.Performance:
|
|
return QualitySetting.Performance;
|
|
case FSR2Quality.UltraPerformance:
|
|
return QualitySetting.UltraPerformance;
|
|
default:
|
|
return QualitySetting.NativeAA;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal class NativeData<T> : IDisposable where T : struct
|
|
{
|
|
private IntPtr _marshalledValue = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(T)));
|
|
public T Value = new T();
|
|
|
|
public IntPtr Ptr
|
|
{
|
|
get
|
|
{
|
|
Marshal.StructureToPtr(Value, _marshalledValue, true);
|
|
return _marshalledValue;
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
Dispose(true);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
protected virtual void Dispose(bool disposing)
|
|
{
|
|
if (_marshalledValue == IntPtr.Zero)
|
|
return;
|
|
|
|
Marshal.FreeHGlobal(_marshalledValue);
|
|
_marshalledValue = IntPtr.Zero;
|
|
}
|
|
|
|
~NativeData() => Dispose(false);
|
|
}
|
|
}
|
|
#endif // UNITY_STANDALONE_WIN
|