// Copyright (c) 2024 Nico de Poel // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. using UnityEngine; using UnityEngine.Rendering; namespace FidelityFX.FSR2 { /// /// Base class for all of the compute passes that make up the FSR2 process. /// This loosely matches the FfxPipelineState struct from the original FSR2 codebase, wrapped in an object-oriented blanket. /// These classes are responsible for loading compute shaders, managing temporary resources, binding resources to shader kernels and dispatching said shaders. /// internal abstract class Fsr2Pass: FfxPassWithFlags { internal const int ShadingChangeMipLevel = 4; // This matches the FFX_FSR2_SHADING_CHANGE_MIP_LEVEL define protected readonly Fsr2Resources Resources; protected readonly ComputeBuffer Constants; protected Fsr2Pass(in Fsr2.ContextDescription contextDescription, Fsr2Resources resources, ComputeBuffer constants) : base("FSR2", contextDescription.Flags) { Resources = resources; Constants = constants; } public void ScheduleDispatch(CommandBuffer commandBuffer, in Fsr2.DispatchDescription dispatchParams, int bufferIndex, int dispatchX, int dispatchY, int dispatchZ = 1) { using (ProfilerSample(commandBuffer)) { Dispatch(commandBuffer, dispatchParams, bufferIndex, dispatchX, dispatchY, dispatchZ); } } protected abstract void Dispatch(CommandBuffer commandBuffer, in Fsr2.DispatchDescription dispatchParams, int bufferIndex, int dispatchX, int dispatchY, int dispatchZ); protected override void SetupShaderKeywords() { bool useLut = false; #if UNITY_2022_1_OR_NEWER // This will also work in 2020.3.43+ and 2021.3.14+ if (SystemInfo.computeSubGroupSize == 64) { useLut = true; } #endif // This matches the permutation rules from the CreatePipeline* functions if ((Flags & Fsr2.InitializationFlags.EnableHighDynamicRange) != 0) ComputeShader.EnableKeyword("FFX_FSR2_OPTION_HDR_COLOR_INPUT"); if ((Flags & Fsr2.InitializationFlags.EnableDisplayResolutionMotionVectors) == 0) ComputeShader.EnableKeyword("FFX_FSR2_OPTION_LOW_RESOLUTION_MOTION_VECTORS"); if ((Flags & Fsr2.InitializationFlags.EnableMotionVectorsJitterCancellation) != 0) ComputeShader.EnableKeyword("FFX_FSR2_OPTION_JITTERED_MOTION_VECTORS"); if ((Flags & Fsr2.InitializationFlags.EnableDepthInverted) != 0) ComputeShader.EnableKeyword("FFX_FSR2_OPTION_INVERTED_DEPTH"); if (useLut) ComputeShader.EnableKeyword("FFX_FSR2_OPTION_REPROJECT_USE_LANCZOS_TYPE"); if ((Flags & Fsr2.InitializationFlags.EnableFP16Usage) != 0) ComputeShader.EnableKeyword("FFX_HALF"); } } internal sealed class Fsr2ComputeLuminancePyramidPass : Fsr2Pass { private readonly ComputeBuffer _spdConstants; public Fsr2ComputeLuminancePyramidPass(in Fsr2.ContextDescription contextDescription, Fsr2Resources resources, ComputeBuffer constants, ComputeBuffer spdConstants) : base(contextDescription, resources, constants) { _spdConstants = spdConstants; InitComputeShader("Compute Luminance Pyramid", contextDescription.Shaders.computeLuminancePyramidPass); } protected override void Dispatch(CommandBuffer commandBuffer, in Fsr2.DispatchDescription dispatchParams, int bufferIndex, int dispatchX, int dispatchY, int dispatchZ) { commandBuffer.SetComputeResourceParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvInputColor, dispatchParams.Color); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.UavSpdAtomicCount, Resources.SpdAtomicCounter); commandBuffer.SetComputeTextureMipParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.UavExposureMipLumaChange, Resources.SceneLuminance, ShadingChangeMipLevel); commandBuffer.SetComputeTextureMipParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.UavExposureMip5, Resources.SceneLuminance, 5); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.UavAutoExposure, Resources.AutoExposure); commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr2ShaderIDs.CbFsr2, Constants); commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr2ShaderIDs.CbSpd, _spdConstants); commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, dispatchZ); } } internal sealed class Fsr2ReconstructPreviousDepthPass : Fsr2Pass { public Fsr2ReconstructPreviousDepthPass(in Fsr2.ContextDescription contextDescription, Fsr2Resources resources, ComputeBuffer constants) : base(contextDescription, resources, constants) { InitComputeShader("Reconstruct & Dilate", contextDescription.Shaders.reconstructPreviousDepthPass); } protected override void Dispatch(CommandBuffer commandBuffer, in Fsr2.DispatchDescription dispatchParams, int bufferIndex, int dispatchX, int dispatchY, int dispatchZ) { commandBuffer.SetComputeResourceParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvInputColor, dispatchParams.Color); commandBuffer.SetComputeResourceParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvInputDepth, dispatchParams.Depth); commandBuffer.SetComputeResourceParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvInputMotionVectors, dispatchParams.MotionVectors); commandBuffer.SetComputeResourceParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvInputExposure, dispatchParams.Exposure); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.UavDilatedMotionVectors, Resources.DilatedMotionVectors[bufferIndex]); commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr2ShaderIDs.CbFsr2, Constants); commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, dispatchZ); } } internal sealed class Fsr2DepthClipPass : Fsr2Pass { public Fsr2DepthClipPass(in Fsr2.ContextDescription contextDescription, Fsr2Resources resources, ComputeBuffer constants) : base(contextDescription, resources, constants) { InitComputeShader("Depth Clip", contextDescription.Shaders.depthClipPass); } protected override void Dispatch(CommandBuffer commandBuffer, in Fsr2.DispatchDescription dispatchParams, int bufferIndex, int dispatchX, int dispatchY, int dispatchZ) { commandBuffer.SetComputeResourceParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvInputColor, dispatchParams.Color); commandBuffer.SetComputeResourceParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvInputDepth, dispatchParams.Depth); commandBuffer.SetComputeResourceParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvInputMotionVectors, dispatchParams.MotionVectors); commandBuffer.SetComputeResourceParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvInputExposure, dispatchParams.Exposure); commandBuffer.SetComputeResourceParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvReactiveMask, dispatchParams.Reactive); commandBuffer.SetComputeResourceParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvTransparencyAndCompositionMask, dispatchParams.TransparencyAndComposition); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvReconstructedPrevNearestDepth, Fsr2ShaderIDs.UavReconstructedPrevNearestDepth); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvDilatedMotionVectors, Resources.DilatedMotionVectors[bufferIndex]); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvDilatedDepth, Fsr2ShaderIDs.UavDilatedDepth); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvPrevDilatedMotionVectors, Resources.DilatedMotionVectors[bufferIndex ^ 1]); commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr2ShaderIDs.CbFsr2, Constants); commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, dispatchZ); } } internal sealed class Fsr2LockPass : Fsr2Pass { public Fsr2LockPass(in Fsr2.ContextDescription contextDescription, Fsr2Resources resources, ComputeBuffer constants) : base(contextDescription, resources, constants) { InitComputeShader("Create Locks", contextDescription.Shaders.lockPass); } protected override void Dispatch(CommandBuffer commandBuffer, in Fsr2.DispatchDescription dispatchParams, int bufferIndex, int dispatchX, int dispatchY, int dispatchZ) { commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvLockInputLuma, Fsr2ShaderIDs.UavLockInputLuma); commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr2ShaderIDs.CbFsr2, Constants); commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, dispatchZ); } } internal sealed class Fsr2AccumulatePass : Fsr2Pass { private const string SharpeningKeyword = "FFX_FSR2_OPTION_APPLY_SHARPENING"; #if UNITY_2021_2_OR_NEWER private readonly LocalKeyword _sharpeningKeyword; #endif public Fsr2AccumulatePass(in Fsr2.ContextDescription contextDescription, Fsr2Resources resources, ComputeBuffer constants) : base(contextDescription, resources, constants) { InitComputeShader("Reproject & Accumulate", contextDescription.Shaders.accumulatePass); #if UNITY_2021_2_OR_NEWER _sharpeningKeyword = new LocalKeyword(ComputeShader, SharpeningKeyword); #endif } protected override void Dispatch(CommandBuffer commandBuffer, in Fsr2.DispatchDescription dispatchParams, int bufferIndex, int dispatchX, int dispatchY, int dispatchZ) { #if UNITY_2021_2_OR_NEWER if (dispatchParams.EnableSharpening) commandBuffer.EnableKeyword(ComputeShader, _sharpeningKeyword); else commandBuffer.DisableKeyword(ComputeShader, _sharpeningKeyword); #else if (dispatchParams.EnableSharpening) commandBuffer.EnableShaderKeyword(SharpeningKeyword); else commandBuffer.DisableShaderKeyword(SharpeningKeyword); #endif if ((Flags & Fsr2.InitializationFlags.EnableDisplayResolutionMotionVectors) == 0) { commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvDilatedMotionVectors, Resources.DilatedMotionVectors[bufferIndex]); } else { commandBuffer.SetComputeResourceParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvInputMotionVectors, dispatchParams.MotionVectors); } commandBuffer.SetComputeResourceParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvInputExposure, dispatchParams.Exposure); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvDilatedReactiveMasks, Fsr2ShaderIDs.UavDilatedReactiveMasks); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvInternalUpscaled, Resources.InternalUpscaled[bufferIndex ^ 1]); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvLockStatus, Resources.LockStatus[bufferIndex ^ 1]); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvPreparedInputColor, Fsr2ShaderIDs.UavPreparedInputColor); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvLanczosLut, Resources.LanczosLut); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvUpscaleMaximumBiasLut, Resources.MaximumBiasLut); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvSceneLuminanceMips, Resources.SceneLuminance); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvAutoExposure, Resources.AutoExposure); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvLumaHistory, Resources.LumaHistory[bufferIndex ^ 1]); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.UavInternalUpscaled, Resources.InternalUpscaled[bufferIndex]); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.UavLockStatus, Resources.LockStatus[bufferIndex]); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.UavLumaHistory, Resources.LumaHistory[bufferIndex]); commandBuffer.SetComputeResourceParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.UavUpscaledOutput, dispatchParams.Output); commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr2ShaderIDs.CbFsr2, Constants); commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, dispatchZ); } } internal sealed class Fsr2SharpenPass : Fsr2Pass { private readonly ComputeBuffer _rcasConstants; public Fsr2SharpenPass(in Fsr2.ContextDescription contextDescription, Fsr2Resources resources, ComputeBuffer constants, ComputeBuffer rcasConstants) : base(contextDescription, resources, constants) { _rcasConstants = rcasConstants; InitComputeShader("RCAS Sharpening", contextDescription.Shaders.sharpenPass); } protected override void Dispatch(CommandBuffer commandBuffer, in Fsr2.DispatchDescription dispatchParams, int bufferIndex, int dispatchX, int dispatchY, int dispatchZ) { commandBuffer.SetComputeResourceParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvInputExposure, dispatchParams.Exposure); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvRcasInput, Resources.InternalUpscaled[bufferIndex]); commandBuffer.SetComputeResourceParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.UavUpscaledOutput, dispatchParams.Output); commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr2ShaderIDs.CbFsr2, Constants); commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr2ShaderIDs.CbRcas, _rcasConstants); commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, dispatchZ); } } internal sealed class Fsr2GenerateReactivePass : Fsr2Pass { private readonly ComputeBuffer _generateReactiveConstants; public Fsr2GenerateReactivePass(in Fsr2.ContextDescription contextDescription, Fsr2Resources resources, ComputeBuffer generateReactiveConstants) : base(contextDescription, resources, null) { _generateReactiveConstants = generateReactiveConstants; InitComputeShader("Auto-Generate Reactive Mask", contextDescription.Shaders.autoGenReactivePass); } protected override void Dispatch(CommandBuffer commandBuffer, in Fsr2.DispatchDescription dispatchParams, int bufferIndex, int dispatchX, int dispatchY, int dispatchZ) { } public void ScheduleDispatch(CommandBuffer commandBuffer, in Fsr2.GenerateReactiveDescription dispatchParams, int dispatchX, int dispatchY) { using (ProfilerSample(commandBuffer)) { commandBuffer.SetComputeResourceParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvOpaqueOnly, dispatchParams.ColorOpaqueOnly); commandBuffer.SetComputeResourceParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvInputColor, dispatchParams.ColorPreUpscale); commandBuffer.SetComputeResourceParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.UavAutoReactive, dispatchParams.OutReactive); commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr2ShaderIDs.CbGenReactive, _generateReactiveConstants); commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); } } } internal sealed class Fsr2TcrAutogeneratePass : Fsr2Pass { private readonly ComputeBuffer _tcrAutogenerateConstants; public Fsr2TcrAutogeneratePass(in Fsr2.ContextDescription contextDescription, Fsr2Resources resources, ComputeBuffer constants, ComputeBuffer tcrAutogenerateConstants) : base(contextDescription, resources, constants) { _tcrAutogenerateConstants = tcrAutogenerateConstants; InitComputeShader("Auto-Generate Transparency & Composition Mask", contextDescription.Shaders.tcrAutoGenPass); } protected override void Dispatch(CommandBuffer commandBuffer, in Fsr2.DispatchDescription dispatchParams, int bufferIndex, int dispatchX, int dispatchY, int dispatchZ) { commandBuffer.SetComputeResourceParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvOpaqueOnly, dispatchParams.ColorOpaqueOnly); commandBuffer.SetComputeResourceParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvInputColor, dispatchParams.Color); commandBuffer.SetComputeResourceParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvInputMotionVectors, dispatchParams.MotionVectors); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvPrevColorPreAlpha, Resources.PrevPreAlpha[bufferIndex ^ 1]); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvPrevColorPostAlpha, Resources.PrevPostAlpha[bufferIndex ^ 1]); commandBuffer.SetComputeResourceParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvReactiveMask, dispatchParams.Reactive); commandBuffer.SetComputeResourceParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvTransparencyAndCompositionMask, dispatchParams.TransparencyAndComposition); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.UavAutoReactive, Resources.AutoReactive); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.UavAutoComposition, Resources.AutoComposition); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.UavPrevColorPreAlpha, Resources.PrevPreAlpha[bufferIndex]); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.UavPrevColorPostAlpha, Resources.PrevPostAlpha[bufferIndex]); commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr2ShaderIDs.CbFsr2, Constants); commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr2ShaderIDs.CbGenReactive, _tcrAutogenerateConstants); commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, dispatchZ); } } }