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
277 lines
11 KiB
using System;
|
|
using System.Runtime.InteropServices;
|
|
using FidelityFX;
|
|
using FidelityFX.FSR2;
|
|
using UnityEngine.Experimental.Rendering;
|
|
|
|
namespace UnityEngine.Rendering.PostProcessing
|
|
{
|
|
internal abstract class Upscaler
|
|
{
|
|
public abstract void CreateContext(PostProcessRenderContext context, Upscaling config);
|
|
|
|
public virtual void DestroyContext()
|
|
{
|
|
DestroyRenderTexture(ref _reactiveMask);
|
|
DestroyConstantsBuffer(ref _reactiveMaskConstants);
|
|
DestroyConstantsBuffer(ref _sharpeningConstants);
|
|
}
|
|
|
|
public abstract void Render(PostProcessRenderContext context, Upscaling config);
|
|
|
|
private ConstantsBuffer<PrepareInputsConstants> _prepareInputsConstants;
|
|
|
|
protected bool PrepareInputs(CommandBuffer cmd, PostProcessRenderContext context, Upscaling config, RenderTexture rwColor = null, RenderTexture rwDepth = null, RenderTexture rwMotionVectors = null)
|
|
{
|
|
ComputeShader shader = context.resources.computeShaders.prepareInputs;
|
|
if (shader == null)
|
|
return false;
|
|
|
|
_prepareInputsConstants ??= ConstantsBuffer<PrepareInputsConstants>.Create();
|
|
|
|
Vector2Int scaledRenderSize = config.GetScaledRenderSize(context.camera);
|
|
|
|
const int threadGroupWorkRegionDim = 8;
|
|
int dispatchSrcX = (scaledRenderSize.x + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
|
|
int dispatchSrcY = (scaledRenderSize.y + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
|
|
|
|
cmd.BeginSample("Prepare Inputs");
|
|
|
|
_prepareInputsConstants.Value.prepareColor = rwColor != null ? 1 : 0;
|
|
_prepareInputsConstants.Value.prepareDepth = rwDepth != null ? 1 : 0;
|
|
_prepareInputsConstants.Value.prepareMotionVectors = rwMotionVectors != null ? 1 : 0;
|
|
_prepareInputsConstants.UpdateBufferData(cmd);
|
|
|
|
int kernelIndex = shader.FindKernel("CS");
|
|
cmd.SetComputeTextureParam(shader, kernelIndex, "InColor", context.source, 0, RenderTextureSubElement.Color);
|
|
cmd.SetComputeTextureParam(shader, kernelIndex, "InDepth", Upscaling.GetDepthTexture(context.camera), 0, RenderTextureSubElement.Depth);
|
|
cmd.SetComputeTextureParam(shader, kernelIndex, "InMotionVectors", BuiltinRenderTextureType.MotionVectors);
|
|
|
|
cmd.SetComputeTextureParam(shader, kernelIndex, "OutColor", rwColor != null ? rwColor : BuiltinRenderTextureType.None);
|
|
cmd.SetComputeTextureParam(shader, kernelIndex, "OutDepth", rwDepth != null ? rwDepth : BuiltinRenderTextureType.None);
|
|
cmd.SetComputeTextureParam(shader, kernelIndex, "OutMotionVectors", rwMotionVectors != null ? rwMotionVectors : BuiltinRenderTextureType.None);
|
|
|
|
cmd.SetComputeConstantBufferParam(shader, "Params", _prepareInputsConstants, 0, Marshal.SizeOf<PrepareInputsConstants>());
|
|
|
|
cmd.DispatchCompute(shader, kernelIndex, dispatchSrcX, dispatchSrcY, 1);
|
|
|
|
cmd.EndSample("Prepare Inputs");
|
|
return true;
|
|
}
|
|
|
|
[Serializable, StructLayout(LayoutKind.Sequential)]
|
|
internal struct PrepareInputsConstants
|
|
{
|
|
public int prepareColor;
|
|
public int prepareDepth;
|
|
public int prepareMotionVectors;
|
|
public int pad;
|
|
}
|
|
|
|
private ConstantsBuffer<GenerateReactiveConstants> _reactiveMaskConstants;
|
|
private RenderTexture _reactiveMask;
|
|
|
|
/// <summary>
|
|
/// Generalized standalone version of the FSR2 reactive mask auto-generating pass that can be used without needing an active FSR2 context.
|
|
/// This allows auto-generated reactive masks to be reused for other non-FSR upscaling techniques.
|
|
/// </summary>
|
|
protected Texture GenerateReactiveMask(CommandBuffer cmd, PostProcessRenderContext context, Upscaling config, GraphicsFormat format = GraphicsFormat.R8_UNorm)
|
|
{
|
|
ComputeShader shader = context.resources.computeShaders.fsr2Upscaler?.autoGenReactivePass;
|
|
if (shader == null)
|
|
return Texture2D.blackTexture;
|
|
|
|
_reactiveMaskConstants ??= ConstantsBuffer<GenerateReactiveConstants>.Create();
|
|
|
|
if (_reactiveMask == null)
|
|
{
|
|
// Use a persistent RT so it can easily be passed to native render plugins
|
|
CreateRenderTexture(ref _reactiveMask, "Reactive Mask", config.MaxRenderSize, format, true);
|
|
}
|
|
|
|
Vector2Int scaledRenderSize = config.GetScaledRenderSize(context.camera);
|
|
|
|
const int threadGroupWorkRegionDim = 8;
|
|
int dispatchX = (scaledRenderSize.x + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
|
|
int dispatchY = (scaledRenderSize.y + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
|
|
|
|
cmd.BeginSample("Generate Reactive Mask");
|
|
|
|
_reactiveMaskConstants.Value.scale = config.generateReactiveParameters.scale;
|
|
_reactiveMaskConstants.Value.threshold = config.generateReactiveParameters.cutoffThreshold;
|
|
_reactiveMaskConstants.Value.binaryValue = config.generateReactiveParameters.binaryValue;
|
|
_reactiveMaskConstants.Value.flags = (uint)config.generateReactiveParameters.flags;
|
|
_reactiveMaskConstants.UpdateBufferData(cmd);
|
|
|
|
int kernelIndex = shader.FindKernel("CS");
|
|
cmd.SetComputeTextureParam(shader, kernelIndex, Fsr2ShaderIDs.SrvOpaqueOnly, config.ColorOpaqueOnly);
|
|
cmd.SetComputeTextureParam(shader, kernelIndex, Fsr2ShaderIDs.SrvInputColor, context.source);
|
|
cmd.SetComputeTextureParam(shader, kernelIndex, Fsr2ShaderIDs.UavAutoReactive, _reactiveMask);
|
|
|
|
cmd.SetComputeConstantBufferParam(shader, Fsr2ShaderIDs.CbGenReactive, _reactiveMaskConstants, 0, Marshal.SizeOf<GenerateReactiveConstants>());
|
|
|
|
cmd.DispatchCompute(shader, kernelIndex, dispatchX, dispatchY, 1);
|
|
|
|
cmd.EndSample("Generate Reactive Mask");
|
|
return _reactiveMask;
|
|
}
|
|
|
|
[Serializable, StructLayout(LayoutKind.Sequential)]
|
|
internal struct GenerateReactiveConstants
|
|
{
|
|
public float scale;
|
|
public float threshold;
|
|
public float binaryValue;
|
|
public uint flags;
|
|
}
|
|
|
|
private ConstantsBuffer<CasConstants> _sharpeningConstants;
|
|
private static readonly int CasInputColor = Shader.PropertyToID("r_input_color");
|
|
private static readonly int CasOutputColor = Shader.PropertyToID("rw_output_color");
|
|
private static readonly int CasConstantBuffer = Shader.PropertyToID("cbCAS");
|
|
|
|
/// <summary>
|
|
/// Generalized standalone version of the CAS sharpening filter that can be applied after any non-FSR upscaling technique.
|
|
/// </summary>
|
|
protected void ApplySharpening(CommandBuffer cmd, PostProcessRenderContext context, in Vector2Int imageSize, float sharpness, RenderTargetIdentifier input, RenderTargetIdentifier output)
|
|
{
|
|
ComputeShader shader = context.resources.computeShaders.casSharpening;
|
|
if (shader == null)
|
|
{
|
|
cmd.CopyTexture(input, output);
|
|
return;
|
|
}
|
|
|
|
const int threadGroupWorkRegionDimRcas = 16;
|
|
int threadGroupsX = (imageSize.x + threadGroupWorkRegionDimRcas - 1) / threadGroupWorkRegionDimRcas;
|
|
int threadGroupsY = (imageSize.y + threadGroupWorkRegionDimRcas - 1) / threadGroupWorkRegionDimRcas;
|
|
|
|
cmd.BeginSample("CAS Sharpening");
|
|
|
|
// Compute the constants
|
|
_sharpeningConstants ??= ConstantsBuffer<CasConstants>.Create();
|
|
int sharpnessIndex = Mathf.RoundToInt(Mathf.Clamp01(sharpness) * (CasConfigs.Length - 1));
|
|
_sharpeningConstants.Value = CasConfigs[sharpnessIndex];
|
|
_sharpeningConstants.UpdateBufferData(cmd);
|
|
|
|
// Dispatch CAS
|
|
int kernelIndex = shader.FindKernel("CS");
|
|
cmd.SetComputeTextureParam(shader, kernelIndex, CasInputColor, input);
|
|
cmd.SetComputeTextureParam(shader, kernelIndex, CasOutputColor, output);
|
|
|
|
cmd.SetComputeConstantBufferParam(shader, CasConstantBuffer, _sharpeningConstants, 0, Marshal.SizeOf<CasConstants>());
|
|
|
|
cmd.DispatchCompute(shader, kernelIndex, threadGroupsX, threadGroupsY, 1);
|
|
|
|
cmd.EndSample("CAS Sharpening");
|
|
}
|
|
|
|
[Serializable, StructLayout(LayoutKind.Sequential)]
|
|
private struct CasConstants
|
|
{
|
|
public CasConstants(uint sharpness, uint halfSharp)
|
|
{
|
|
// Since we don't use CAS for scaling, most of these values end up being constants
|
|
scaling0 = scaling1 = 1065353216;
|
|
scaling2 = scaling3 = 0;
|
|
sharpness0 = sharpness;
|
|
sharpness1 = halfSharp;
|
|
sharpness2 = 1090519040;
|
|
sharpness3 = 0;
|
|
}
|
|
|
|
public readonly uint scaling0;
|
|
public readonly uint scaling1;
|
|
public readonly uint scaling2;
|
|
public readonly uint scaling3;
|
|
public readonly uint sharpness0;
|
|
public readonly uint sharpness1;
|
|
public readonly uint sharpness2;
|
|
public readonly uint sharpness3;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The FidelityFX C++ codebase uses floats bitwise converted to ints to pass sharpness parameters to the CAS shader.
|
|
/// This is not possible in C# without enabling unsafe code compilation, so to avoid that we instead use a table of precomputed values.
|
|
/// </summary>
|
|
private static readonly CasConstants[] CasConfigs =
|
|
{
|
|
new(3187671040u, 45056u),
|
|
new(3187831332u, 45075u),
|
|
new(3187997869u, 45095u),
|
|
new(3188171023u, 45117u),
|
|
new(3188351197u, 45139u),
|
|
new(3188538827u, 45161u),
|
|
new(3188734385u, 45185u),
|
|
new(3188938384u, 45210u),
|
|
new(3189151382u, 45236u),
|
|
new(3189373991u, 45263u),
|
|
new(3189606873u, 45292u),
|
|
new(3189850757u, 45322u),
|
|
new(3190106443u, 45353u),
|
|
new(3190374807u, 45386u),
|
|
new(3190656816u, 45420u),
|
|
new(3190953540u, 45456u),
|
|
new(3191266159u, 45494u),
|
|
new(3191595985u, 45535u),
|
|
new(3191944482u, 45577u),
|
|
new(3192313280u, 45622u),
|
|
new(3192704205u, 45670u),
|
|
};
|
|
|
|
protected static bool CreateRenderTexture(ref RenderTexture rt, string name, in Vector2Int size, GraphicsFormat format, bool enableRandomWrite = false)
|
|
{
|
|
rt = new RenderTexture(size.x, size.y, 0, format) { name = name, enableRandomWrite = enableRandomWrite };
|
|
return rt.Create();
|
|
}
|
|
|
|
protected static void CreateRenderTextureArray(RenderTexture[] rts, string name, in Vector2Int size, GraphicsFormat format, bool enableRandomWrite = false)
|
|
{
|
|
for (int i = 0; i < rts.Length; ++i)
|
|
{
|
|
CreateRenderTexture(ref rts[i], $"{name}_{i + 1}", size, format, enableRandomWrite);
|
|
}
|
|
}
|
|
|
|
protected static bool CreateRenderTexture(ref RenderTexture rt, string name, in Vector2Int size, RenderTextureFormat format, bool enableRandomWrite = false)
|
|
{
|
|
rt = new RenderTexture(size.x, size.y, 0, format) { name = name, enableRandomWrite = enableRandomWrite };
|
|
return rt.Create();
|
|
}
|
|
|
|
protected static void CreateRenderTextureArray(RenderTexture[] rts, string name, in Vector2Int size, RenderTextureFormat format, bool enableRandomWrite = false)
|
|
{
|
|
for (int i = 0; i < rts.Length; ++i)
|
|
{
|
|
CreateRenderTexture(ref rts[i], $"{name}_{i + 1}", size, format, enableRandomWrite);
|
|
}
|
|
}
|
|
|
|
protected static void DestroyRenderTexture(ref RenderTexture rt)
|
|
{
|
|
if (rt == null)
|
|
return;
|
|
|
|
rt.Release();
|
|
rt = null;
|
|
}
|
|
|
|
protected static void DestroyRenderTextureArray(RenderTexture[] rts)
|
|
{
|
|
for (int i = 0; i < rts.Length; ++i)
|
|
{
|
|
DestroyRenderTexture(ref rts[i]);
|
|
}
|
|
}
|
|
|
|
protected static void DestroyConstantsBuffer<TConst>(ref ConstantsBuffer<TConst> cb)
|
|
where TConst: struct
|
|
{
|
|
if (cb == null)
|
|
return;
|
|
|
|
cb.Destroy();
|
|
cb = null;
|
|
}
|
|
}
|
|
}
|