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.
305 lines
9.9 KiB
305 lines
9.9 KiB
using System;
|
|
using System.Runtime.InteropServices;
|
|
using UnityEngine.Experimental.Rendering;
|
|
|
|
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 PSSRPlugin.DispatchParams _dispatchParams;
|
|
private IntPtr _dispatchParamsBuffer;
|
|
|
|
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.InitPssr() < 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.CreatePssrContext(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);
|
|
_dispatchParamsBuffer = Marshal.AllocHGlobal(Marshal.SizeOf<PSSRPlugin.DispatchParams>());
|
|
_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;
|
|
|
|
PSSRPlugin.DestroyPssrContext(previousContext);
|
|
|
|
_contextInitialized = false;
|
|
}
|
|
|
|
if (_outputColor != null)
|
|
{
|
|
Object.Destroy(_outputColor);
|
|
_outputColor = null;
|
|
}
|
|
|
|
DestroyRenderTextureArray(_inputMotionVectors);
|
|
DestroyRenderTextureArray(_inputDepth);
|
|
DestroyRenderTexture(ref _inputColor);
|
|
|
|
if (_dispatchParamsBuffer != IntPtr.Zero)
|
|
{
|
|
Marshal.FreeHGlobal(_dispatchParamsBuffer);
|
|
_dispatchParamsBuffer = IntPtr.Zero;
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
_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;
|
|
|
|
Marshal.StructureToPtr(_dispatchParams, _dispatchParamsBuffer, false);
|
|
cmd.IssuePluginEventAndData(PSSRPlugin.GetRenderEventAndDataFunc(), 1, _dispatchParamsBuffer);
|
|
|
|
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;
|
|
}
|
|
|
|
private static class PSSRPlugin
|
|
{
|
|
public static readonly uint MaxNumContexts = 4;
|
|
|
|
private const string LibraryName = "PSSRPlugin.prx";
|
|
|
|
[DllImport(LibraryName)]
|
|
public static extern IntPtr GetRenderEventAndDataFunc();
|
|
|
|
[DllImport(LibraryName)]
|
|
public static extern int InitPssr();
|
|
|
|
[DllImport(LibraryName)]
|
|
public static extern void ReleasePssr();
|
|
|
|
[DllImport(LibraryName)]
|
|
public static extern int CreatePssrContext(ref InitParams initParams, out IntPtr outputColorTexturePtr);
|
|
|
|
[DllImport(LibraryName)]
|
|
public static extern void DestroyPssrContext(uint contextIndex);
|
|
|
|
[Serializable, StructLayout(LayoutKind.Sequential)]
|
|
public struct InitParams
|
|
{
|
|
public uint contextIndex;
|
|
|
|
public uint displayWidth;
|
|
public uint displayHeight;
|
|
public uint maxRenderWidth;
|
|
public uint maxRenderHeight;
|
|
|
|
public uint autoKeepCopies;
|
|
}
|
|
|
|
[Serializable, StructLayout(LayoutKind.Sequential)]
|
|
public struct DispatchParams
|
|
{
|
|
public uint contextIndex;
|
|
|
|
public IntPtr color;
|
|
public IntPtr depth;
|
|
public IntPtr prevDepth;
|
|
public IntPtr motionVectors;
|
|
public IntPtr prevMotionVectors;
|
|
public IntPtr exposure;
|
|
public IntPtr reactiveMask;
|
|
public IntPtr outputColor;
|
|
|
|
public uint renderWidth;
|
|
public uint renderHeight;
|
|
|
|
public Vector2 jitter;
|
|
public Vector2 motionVectorScale;
|
|
|
|
public Matrix4x4 camProjectionNoJitter;
|
|
public Vector3 camForward;
|
|
public Vector3 camUp;
|
|
public Vector3 camRight;
|
|
public double camPositionX;
|
|
public double camPositionY;
|
|
public double camPositionZ;
|
|
public float camNear;
|
|
public float camFar;
|
|
public float preExposure;
|
|
public uint resetHistory;
|
|
public OptionFlags flags;
|
|
|
|
public void FromCamera(Camera cam)
|
|
{
|
|
camProjectionNoJitter = cam.nonJitteredProjectionMatrix;
|
|
camForward = cam.transform.forward;
|
|
camUp = cam.transform.up;
|
|
camRight = cam.transform.right;
|
|
camPositionX = cam.transform.position.x;
|
|
camPositionY = cam.transform.position.y;
|
|
camPositionZ = cam.transform.position.z;
|
|
camNear = cam.nearClipPlane;
|
|
camFar = cam.farClipPlane;
|
|
}
|
|
}
|
|
|
|
[Flags]
|
|
public enum OptionFlags: uint
|
|
{
|
|
None = 0u,
|
|
ViewSpaceDepth = 1u << 14,
|
|
ReverseDepth = 1u << 15,
|
|
AutoExposure = 1u << 16,
|
|
PassThrough = 1u << 31,
|
|
}
|
|
}
|
|
#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
|
|
}
|
|
}
|