Development repository for FSR2 integration into Unity Post-Processing Stack V2.
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.

216 lines
7.6 KiB

using System;
using UnityEngine.Experimental.Rendering;
#if UNITY_PS5
using WW1.PlayStation;
#endif
namespace UnityEngine.Rendering.PostProcessing
{
internal class PSSRUpscaler : Upscaler
{
#if UNITY_PS5
public static bool IsSupported => UnityEngine.PS5.Utility.isTrinityMode && PluginInitialized;
private static readonly bool PluginInitialized;
private static uint _currentContext;
private readonly PSSRPlugin.NativeData<PSSRPlugin.DispatchParams> _dispatchParams = new();
private readonly PSSRPlugin.NativeData<PSSRPlugin.DestroyParams> _destroyParams = new();
private RenderTexture _inputColor;
private readonly RenderTexture[] _inputDepth = new RenderTexture[2];
private readonly RenderTexture[] _inputMotionVectors = new RenderTexture[2];
private Texture2D _outputColor;
private bool _contextInitialized;
private uint _frameCount;
static PSSRUpscaler()
{
try
{
if (PSSRPlugin.Init() < 0)
{
Debug.LogError("Failed to initialize PSSR plugin!");
PluginInitialized = false;
return;
}
}
catch (DllNotFoundException)
{
Debug.LogError("PSSR plugin not found!");
PluginInitialized = false;
return;
}
PluginInitialized = true;
_currentContext = 0;
}
public override void CreateContext(PostProcessRenderContext context, Upscaling config)
{
if (!PluginInitialized)
{
Debug.LogWarning("PSSR plugin is not initialized!");
return;
}
PSSRPlugin.InitParams initParams;
initParams.contextIndex = _currentContext;
initParams.displayWidth = (uint)config.UpscaleSize.x;
initParams.displayHeight = (uint)config.UpscaleSize.y;
initParams.maxRenderWidth = (uint)config.MaxRenderSize.x;
initParams.maxRenderHeight = (uint)config.MaxRenderSize.y;
initParams.autoKeepCopies = 0u; // We use double buffered depth and motion vector copies created by Unity
CreateRenderTexture(ref _inputColor, "PSSR Input Color", config.MaxRenderSize, context.sourceFormat, true);
CreateRenderTextureArray(_inputDepth, "PSSR Input Depth", config.MaxRenderSize, GraphicsFormat.R32_SFloat, true);
CreateRenderTextureArray(_inputMotionVectors, "PSSR Input Motion Vectors", config.MaxRenderSize, GraphicsFormat.R16G16_SFloat, true);
if (PSSRPlugin.CreateContext(ref initParams, out IntPtr outputColorTexturePtr) >= 0 && outputColorTexturePtr != IntPtr.Zero)
{
// PSSR requires an output color texture in a very particular format (k11_11_10Float with kStandard256B tile mode and a specific alignment) that Unity cannot create directly.
// So instead we let the plugin create that texture and then import it into Unity as a generic 32bpp texture from a native pointer.
_outputColor = Texture2D.CreateExternalTexture(config.UpscaleSize.x, config.UpscaleSize.y, TextureFormat.RGBA32, false, true, outputColorTexturePtr);
_dispatchParams.Initialize();
_destroyParams.Initialize();
_contextInitialized = true;
}
}
public override void DestroyContext()
{
base.DestroyContext();
if (_contextInitialized)
{
// Rotate between contexts to reduce the risk of race conditions between old and new contexts
uint previousContext = _currentContext;
_currentContext = (_currentContext + 1) % PSSRPlugin.MaxNumContexts;
CommandBuffer cmd = new();
PSSRPlugin.IssuePluginEvent(cmd, PSSRPlugin.Event.Destroy, new IntPtr(previousContext));
Graphics.ExecuteCommandBuffer(cmd);
cmd.Release();
_contextInitialized = false;
}
if (_outputColor != null)
{
Object.Destroy(_outputColor);
_outputColor = null;
}
DestroyRenderTextureArray(_inputMotionVectors);
DestroyRenderTextureArray(_inputDepth);
DestroyRenderTexture(ref _inputColor);
_destroyParams.Destroy();
_dispatchParams.Destroy();
}
public override void Render(PostProcessRenderContext context, Upscaling config)
{
var cmd = context.command;
if (!PluginInitialized || !_contextInitialized)
{
cmd.BlitFullscreenTriangle(context.source, context.destination);
return;
}
cmd.BeginSample("PSSR");
if (config.Reset || _frameCount == 0)
{
cmd.SetRenderTarget(_inputDepth[0]);
cmd.ClearRenderTarget(false, true, Color.clear);
cmd.SetRenderTarget(_inputDepth[1]);
cmd.ClearRenderTarget(false, true, Color.clear);
cmd.SetRenderTarget(_inputMotionVectors[0]);
cmd.ClearRenderTarget(false, true, Color.clear);
cmd.SetRenderTarget(_inputMotionVectors[1]);
cmd.ClearRenderTarget(false, true, Color.clear);
}
int frameIndex = (int)(_frameCount++ % 2);
// We need to provide copies of the previous depth and motion vector buffers anyway, so we can turn this otherwise wasteful copying into a benefit
PrepareInputs(cmd, context, config, _inputColor, _inputDepth[frameIndex], _inputMotionVectors[frameIndex]);
Texture reactiveMask = config.transparencyAndCompositionMask;
if (config.autoGenerateReactiveMask || config.autoGenerateTransparencyAndComposition)
{
reactiveMask = GenerateReactiveMask(cmd, context, config);
}
var flags = PSSRPlugin.OptionFlags.None;
if (SystemInfo.usesReversedZBuffer) flags |= PSSRPlugin.OptionFlags.ReverseDepth;
if (config.exposureSource == Upscaling.ExposureSource.Auto) flags |= PSSRPlugin.OptionFlags.AutoExposure;
ref var dispatchParams = ref _dispatchParams.Value;
dispatchParams.contextIndex = _currentContext;
dispatchParams.color = ToNativePtr(_inputColor);
dispatchParams.depth = ToNativePtr(_inputDepth[frameIndex]);
dispatchParams.prevDepth = ToNativePtr(_inputDepth[frameIndex ^ 1]);
dispatchParams.motionVectors = ToNativePtr(_inputMotionVectors[frameIndex]);
dispatchParams.prevMotionVectors = ToNativePtr(_inputMotionVectors[frameIndex ^ 1]);
dispatchParams.exposure = ToNativePtr(config.exposureSource switch
{
Upscaling.ExposureSource.Manual when config.exposure != null => config.exposure,
Upscaling.ExposureSource.Unity => context.autoExposureTexture,
_ => null,
});
dispatchParams.reactiveMask = ToNativePtr(reactiveMask);
dispatchParams.outputColor = ToNativePtr(_outputColor);
var scaledRenderSize = config.GetScaledRenderSize(context.camera);
dispatchParams.renderWidth = (uint)scaledRenderSize.x;
dispatchParams.renderHeight = (uint)scaledRenderSize.y;
dispatchParams.jitter = config.JitterOffset;
dispatchParams.motionVectorScale = new Vector2(-scaledRenderSize.x, -scaledRenderSize.y);
dispatchParams.FromCamera(context.camera);
dispatchParams.preExposure = config.preExposure;
dispatchParams.resetHistory = config.Reset ? 1u : 0u;
dispatchParams.flags = flags;
PSSRPlugin.IssuePluginEvent(cmd, PSSRPlugin.Event.Dispatch, _dispatchParams);
if (config.performSharpenPass)
{
// PSSR output is already pretty sharp, and we don't want to over-sharpen the image, so cut the sharpness range by half
ApplySharpening(cmd, context, config.UpscaleSize, config.sharpness * 0.5f, _outputColor, context.destination);
}
else
{
// Convert the PSSR output from R11G11B10 to the expected destination format
cmd.BlitFullscreenTriangle(_outputColor, context.destination);
}
cmd.EndSample("PSSR");
}
private static IntPtr ToNativePtr(Texture texture)
{
return texture != null ? texture.GetNativeTexturePtr() : IntPtr.Zero;
}
#else
public static bool IsSupported => false;
public override void CreateContext(PostProcessRenderContext context, Upscaling config)
{
throw new NotImplementedException();
}
public override void DestroyContext()
{
base.DestroyContext();
}
public override void Render(PostProcessRenderContext context, Upscaling config)
{
throw new NotImplementedException();
}
#endif
}
}