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.
204 lines
7.6 KiB
204 lines
7.6 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<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 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 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 void DestroyRenderTexture(ref RenderTexture rt)
|
|
{
|
|
if (rt == null)
|
|
return;
|
|
|
|
rt.Release();
|
|
rt = null;
|
|
}
|
|
|
|
protected void DestroyConstantsBuffer<TConst>(ref ConstantsBuffer<TConst> cb)
|
|
where TConst: struct
|
|
{
|
|
if (cb == null)
|
|
return;
|
|
|
|
cb.Destroy();
|
|
cb = null;
|
|
}
|
|
}
|
|
}
|