@ -1,8 +1,4 @@
using System ;
using System.Runtime.InteropServices ;
using FidelityFX ;
using FidelityFX.FSR2 ;
using UnityEngine.Experimental.Rendering ;
namespace UnityEngine.Rendering.PostProcessing
{
@ -10,195 +6,8 @@ namespace UnityEngine.Rendering.PostProcessing
{
public abstract void CreateContext ( PostProcessRenderContext context , Upscaling config ) ;
public virtual void DestroyContext ( )
{
DestroyRenderTexture ( ref _reactiveMask ) ;
DestroyConstantsBuffer ( ref _reactiveMaskConstants ) ;
DestroyConstantsBuffer ( ref _sharpeningConstants ) ;
}
public abstract void DestroyContext ( ) ;
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 = 1 6 ;
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 = 1 0 6 5 3 5 3 2 1 6 ;
scaling2 = scaling3 = 0 ;
sharpness0 = sharpness ;
sharpness1 = halfSharp ;
sharpness2 = 1 0 9 0 5 1 9 0 4 0 ;
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 ( 3 1 8 7 6 7 1 0 4 0 u , 4 5 0 5 6 u ) ,
new ( 3 1 8 7 8 3 1 3 3 2 u , 4 5 0 7 5 u ) ,
new ( 3 1 8 7 9 9 7 8 6 9 u , 4 5 0 9 5 u ) ,
new ( 3 1 8 8 1 7 1 0 2 3 u , 4 5 1 1 7 u ) ,
new ( 3 1 8 8 3 5 1 1 9 7 u , 4 5 1 3 9 u ) ,
new ( 3 1 8 8 5 3 8 8 2 7 u , 4 5 1 6 1 u ) ,
new ( 3 1 8 8 7 3 4 3 8 5 u , 4 5 1 8 5 u ) ,
new ( 3 1 8 8 9 3 8 3 8 4 u , 4 5 2 1 0 u ) ,
new ( 3 1 8 9 1 5 1 3 8 2 u , 4 5 2 3 6 u ) ,
new ( 3 1 8 9 3 7 3 9 9 1 u , 4 5 2 6 3 u ) ,
new ( 3 1 8 9 6 0 6 8 7 3 u , 4 5 2 9 2 u ) ,
new ( 3 1 8 9 8 5 0 7 5 7 u , 4 5 3 2 2 u ) ,
new ( 3 1 9 0 1 0 6 4 4 3 u , 4 5 3 5 3 u ) ,
new ( 3 1 9 0 3 7 4 8 0 7 u , 4 5 3 8 6 u ) ,
new ( 3 1 9 0 6 5 6 8 1 6 u , 4 5 4 2 0 u ) ,
new ( 3 1 9 0 9 5 3 5 4 0 u , 4 5 4 5 6 u ) ,
new ( 3 1 9 1 2 6 6 1 5 9 u , 4 5 4 9 4 u ) ,
new ( 3 1 9 1 5 9 5 9 8 5 u , 4 5 5 3 5 u ) ,
new ( 3 1 9 1 9 4 4 4 8 2 u , 4 5 5 7 7 u ) ,
new ( 3 1 9 2 3 1 3 2 8 0 u , 4 5 6 2 2 u ) ,
new ( 3 1 9 2 7 0 4 2 0 5 u , 4 5 6 7 0 u ) ,
} ;
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 ;
}
}
}