using System; using System.Runtime.InteropServices; using UnityEngine; using UnityEngine.Experimental.Rendering; using UnityEngine.Rendering; namespace FidelityFX { /// /// 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 Fsr2Pipeline: IDisposable { internal const int ShadingChangeMipLevel = 4; // This matches the FFX_FSR2_SHADING_CHANGE_MIP_LEVEL define protected readonly Fsr2.ContextDescription ContextDescription; protected readonly Fsr2Resources Resources; protected readonly ComputeBuffer Constants; protected ComputeShader ComputeShader; protected int KernelIndex; // Shader resource views, i.e. read-only bindings internal static readonly int SrvInputColor = Shader.PropertyToID("r_input_color_jittered"); internal static readonly int SrvOpaqueOnly = Shader.PropertyToID("r_input_opaque_only"); internal static readonly int SrvInputMotionVectors = Shader.PropertyToID("r_input_motion_vectors"); internal static readonly int SrvInputDepth = Shader.PropertyToID("r_input_depth"); internal static readonly int SrvInputExposure = Shader.PropertyToID("r_input_exposure"); internal static readonly int SrvAutoExposure = Shader.PropertyToID("r_auto_exposure"); internal static readonly int SrvReactiveMask = Shader.PropertyToID("r_reactive_mask"); internal static readonly int SrvTransparencyAndCompositionMask = Shader.PropertyToID("r_transparency_and_composition_mask"); internal static readonly int SrvReconstructedPrevNearestDepth = Shader.PropertyToID("r_reconstructed_previous_nearest_depth"); internal static readonly int SrvDilatedMotionVectors = Shader.PropertyToID("r_dilated_motion_vectors"); internal static readonly int SrvPrevDilatedMotionVectors = Shader.PropertyToID("r_previous_dilated_motion_vectors"); internal static readonly int SrvDilatedDepth = Shader.PropertyToID("r_dilatedDepth"); internal static readonly int SrvInternalUpscaled = Shader.PropertyToID("r_internal_upscaled_color"); internal static readonly int SrvLockStatus = Shader.PropertyToID("r_lock_status"); internal static readonly int SrvLockInputLuma = Shader.PropertyToID("r_lock_input_luma"); internal static readonly int SrvPreparedInputColor = Shader.PropertyToID("r_prepared_input_color"); internal static readonly int SrvLumaHistory = Shader.PropertyToID("r_luma_history"); internal static readonly int SrvRcasInput = Shader.PropertyToID("r_rcas_input"); internal static readonly int SrvLanczosLut = Shader.PropertyToID("r_lanczos_lut"); internal static readonly int SrvSceneLuminanceMips = Shader.PropertyToID("r_imgMips"); internal static readonly int SrvUpscaleMaximumBiasLut = Shader.PropertyToID("r_upsample_maximum_bias_lut"); internal static readonly int SrvDilatedReactiveMasks = Shader.PropertyToID("r_dilated_reactive_masks"); // Unordered access views, i.e. random read/write bindings internal static readonly int UavReconstructedPrevNearestDepth = Shader.PropertyToID("rw_reconstructed_previous_nearest_depth"); internal static readonly int UavDilatedMotionVectors = Shader.PropertyToID("rw_dilated_motion_vectors"); internal static readonly int UavDilatedDepth = Shader.PropertyToID("rw_dilatedDepth"); internal static readonly int UavInternalUpscaled = Shader.PropertyToID("rw_internal_upscaled_color"); internal static readonly int UavLockStatus = Shader.PropertyToID("rw_lock_status"); internal static readonly int UavLockInputLuma = Shader.PropertyToID("rw_lock_input_luma"); internal static readonly int UavNewLocks = Shader.PropertyToID("rw_new_locks"); internal static readonly int UavPreparedInputColor = Shader.PropertyToID("rw_prepared_input_color"); internal static readonly int UavLumaHistory = Shader.PropertyToID("rw_luma_history"); internal static readonly int UavUpscaledOutput = Shader.PropertyToID("rw_upscaled_output"); internal static readonly int UavExposureMipLumaChange = Shader.PropertyToID("rw_img_mip_shading_change"); internal static readonly int UavExposureMip5 = Shader.PropertyToID("rw_img_mip_5"); internal static readonly int UavDilatedReactiveMasks = Shader.PropertyToID("rw_dilated_reactive_masks"); internal static readonly int UavAutoExposure = Shader.PropertyToID("rw_auto_exposure"); internal static readonly int UavSpdAtomicCount = Shader.PropertyToID("rw_spd_global_atomic"); internal static readonly int UavAutoReactive = Shader.PropertyToID("rw_output_autoreactive"); // Constant buffer bindings protected static readonly int CbFsr2 = Shader.PropertyToID("cbFSR2"); protected static readonly int CbSpd = Shader.PropertyToID("cbSPD"); protected static readonly int CbRcas = Shader.PropertyToID("cbRCAS"); protected static readonly int CbGenReactive = Shader.PropertyToID("cbGenerateReactive"); protected virtual bool AllowFP16 => true; protected Fsr2Pipeline(Fsr2.ContextDescription contextDescription, Fsr2Resources resources, ComputeBuffer constants) { ContextDescription = contextDescription; Resources = resources; Constants = constants; } public virtual void Dispose() { UnloadComputeShader(); } public abstract void ScheduleDispatch(CommandBuffer commandBuffer, Fsr2.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY); public static void RegisterResources(CommandBuffer commandBuffer, Fsr2.ContextDescription contextDescription, Fsr2.DispatchDescription dispatchParams) { Vector2Int displaySize = contextDescription.DisplaySize; Vector2Int maxRenderSize = contextDescription.MaxRenderSize; // Set up shared aliasable resources, i.e. temporary render textures // These do not need to persist between frames, but they do need to be available between passes // TODO: we could potentially gather *all* resource binding here, by using CommandBuffer.SetGlobalTexture for everything // Resource FSR2_SpdAtomicCounter: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R32_UINT, FFX_RESOURCE_FLAGS_ALIASABLE commandBuffer.GetTemporaryRT(UavSpdAtomicCount, 1, 1, 0, default, GraphicsFormat.R32_UInt, 1, true); // FSR2_ReconstructedPrevNearestDepth: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R32_UINT, FFX_RESOURCE_FLAGS_ALIASABLE commandBuffer.GetTemporaryRT(UavReconstructedPrevNearestDepth, maxRenderSize.x, maxRenderSize.y, 0, default, GraphicsFormat.R32_UInt, 1, true); // FSR2_DilatedDepth: FFX_RESOURCE_USAGE_RENDERTARGET | FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R32_FLOAT, FFX_RESOURCE_FLAGS_ALIASABLE commandBuffer.GetTemporaryRT(UavDilatedDepth, maxRenderSize.x, maxRenderSize.y, 0, default, GraphicsFormat.R32_SFloat, 1, true); // FSR2_LockInputLuma: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R16_FLOAT, FFX_RESOURCE_FLAGS_ALIASABLE commandBuffer.GetTemporaryRT(UavLockInputLuma, maxRenderSize.x, maxRenderSize.y, 0, default, GraphicsFormat.R16_SFloat, 1, true); // FSR2_DilatedReactiveMasks: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R8G8_UNORM, FFX_RESOURCE_FLAGS_ALIASABLE commandBuffer.GetTemporaryRT(UavDilatedReactiveMasks, maxRenderSize.x, maxRenderSize.y, 0, default, GraphicsFormat.R8G8_UNorm, 1, true); // FSR2_PreparedInputColor: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R16G16B16A16_FLOAT, FFX_RESOURCE_FLAGS_ALIASABLE commandBuffer.GetTemporaryRT(UavPreparedInputColor, maxRenderSize.x, maxRenderSize.y, 0, default, GraphicsFormat.R16G16B16A16_SFloat, 1, true); // FSR2_NewLocks: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R8_UNORM, FFX_RESOURCE_FLAGS_ALIASABLE commandBuffer.GetTemporaryRT(UavNewLocks, displaySize.x, displaySize.y, 0, default, GraphicsFormat.R8_UNorm, 1, true); } public static void UnregisterResources(CommandBuffer commandBuffer) { // Release all of the aliasable resources used this frame commandBuffer.ReleaseTemporaryRT(UavSpdAtomicCount); commandBuffer.ReleaseTemporaryRT(UavReconstructedPrevNearestDepth); commandBuffer.ReleaseTemporaryRT(UavDilatedDepth); commandBuffer.ReleaseTemporaryRT(UavLockInputLuma); commandBuffer.ReleaseTemporaryRT(UavDilatedReactiveMasks); commandBuffer.ReleaseTemporaryRT(UavPreparedInputColor); commandBuffer.ReleaseTemporaryRT(UavNewLocks); } protected void LoadComputeShader(string name) { LoadComputeShader(name, ContextDescription.Flags, ref ComputeShader, out KernelIndex); } private void LoadComputeShader(string name, Fsr2.InitializationFlags flags, ref ComputeShader shaderRef, out int kernelIndex) { if (shaderRef == null) shaderRef = ContextDescription.Callbacks.LoadComputeShader(name); kernelIndex = shaderRef.FindKernel("CS"); bool useLut = false; #if UNITY_2020_3_OR_NEWER if (SystemInfo.computeSubGroupSize == 64) { useLut = true; } #endif // Allow 16-bit floating point as a configuration option, except on passes that explicitly disable it bool supportedFP16 = ((flags & Fsr2.InitializationFlags.EnableFP16Usage) != 0 && AllowFP16); // This matches the permutation rules from the CreatePipeline* functions if ((flags & Fsr2.InitializationFlags.EnableHighDynamicRange) != 0) shaderRef.EnableKeyword("FFX_FSR2_OPTION_HDR_COLOR_INPUT"); if ((flags & Fsr2.InitializationFlags.EnableDisplayResolutionMotionVectors) == 0) shaderRef.EnableKeyword("FFX_FSR2_OPTION_LOW_RESOLUTION_MOTION_VECTORS"); if ((flags & Fsr2.InitializationFlags.EnableMotionVectorsJitterCancellation) != 0) shaderRef.EnableKeyword("FFX_FSR2_OPTION_JITTERED_MOTION_VECTORS"); if ((flags & Fsr2.InitializationFlags.EnableDepthInverted) != 0) shaderRef.EnableKeyword("FFX_FSR2_OPTION_INVERTED_DEPTH"); if (useLut) shaderRef.EnableKeyword("FFX_FSR2_OPTION_REPROJECT_USE_LANCZOS_TYPE"); if (supportedFP16) shaderRef.EnableKeyword("FFX_HALF"); } private void UnloadComputeShader() { UnloadComputeShader(ref ComputeShader); } private void UnloadComputeShader(ref ComputeShader shaderRef) { if (shaderRef == null) return; ContextDescription.Callbacks.UnloadComputeShader(shaderRef); shaderRef = null; } } internal class Fsr2ComputeLuminancePyramidPipeline : Fsr2Pipeline { private readonly ComputeBuffer _spdConstants; public Fsr2ComputeLuminancePyramidPipeline(Fsr2.ContextDescription contextDescription, Fsr2Resources resources, ComputeBuffer constants, ComputeBuffer spdConstants) : base(contextDescription, resources, constants) { _spdConstants = spdConstants; LoadComputeShader("FSR2/ffx_fsr2_compute_luminance_pyramid_pass"); } public override void ScheduleDispatch(CommandBuffer commandBuffer, Fsr2.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) { if (dispatchParams.Color.HasValue) commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, SrvInputColor, dispatchParams.Color.Value, 0, RenderTextureSubElement.Color); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, UavExposureMipLumaChange, Resources.SceneLuminance, ShadingChangeMipLevel); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, UavExposureMip5, Resources.SceneLuminance, 5); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, UavAutoExposure, Resources.AutoExposure); commandBuffer.SetComputeConstantBufferParam(ComputeShader, CbFsr2, Constants, 0, Marshal.SizeOf()); commandBuffer.SetComputeConstantBufferParam(ComputeShader, CbSpd, _spdConstants, 0, Marshal.SizeOf()); commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); } } internal class Fsr2ReconstructPreviousDepthPipeline : Fsr2Pipeline { public Fsr2ReconstructPreviousDepthPipeline(Fsr2.ContextDescription contextDescription, Fsr2Resources resources, ComputeBuffer constants) : base(contextDescription, resources, constants) { LoadComputeShader("FSR2/ffx_fsr2_reconstruct_previous_depth_pass"); } public override void ScheduleDispatch(CommandBuffer commandBuffer, Fsr2.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) { if (dispatchParams.Color.HasValue) commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, SrvInputColor, dispatchParams.Color.Value, 0, RenderTextureSubElement.Color); if (dispatchParams.Depth.HasValue) commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, SrvInputDepth, dispatchParams.Depth.Value, 0, RenderTextureSubElement.Depth); if (dispatchParams.MotionVectors.HasValue) commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, SrvInputMotionVectors, dispatchParams.MotionVectors.Value); if (dispatchParams.Exposure.HasValue) commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, SrvInputExposure, dispatchParams.Exposure.Value); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, UavDilatedMotionVectors, Resources.DilatedMotionVectors[frameIndex]); commandBuffer.SetComputeConstantBufferParam(ComputeShader, CbFsr2, Constants, 0, Marshal.SizeOf()); commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); } } internal class Fsr2DepthClipPipeline : Fsr2Pipeline { public Fsr2DepthClipPipeline(Fsr2.ContextDescription contextDescription, Fsr2Resources resources, ComputeBuffer constants) : base(contextDescription, resources, constants) { LoadComputeShader("FSR2/ffx_fsr2_depth_clip_pass"); } public override void ScheduleDispatch(CommandBuffer commandBuffer, Fsr2.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) { if (dispatchParams.Color.HasValue) commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, SrvInputColor, dispatchParams.Color.Value, 0, RenderTextureSubElement.Color); if (dispatchParams.Depth.HasValue) commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, SrvInputDepth, dispatchParams.Depth.Value, 0, RenderTextureSubElement.Depth); if (dispatchParams.MotionVectors.HasValue) commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, SrvInputMotionVectors, dispatchParams.MotionVectors.Value); if (dispatchParams.Exposure.HasValue) commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, SrvInputExposure, dispatchParams.Exposure.Value); if (dispatchParams.Reactive.HasValue) commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, SrvReactiveMask, dispatchParams.Reactive.Value); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, SrvReconstructedPrevNearestDepth, UavReconstructedPrevNearestDepth); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, SrvDilatedMotionVectors, Resources.DilatedMotionVectors[frameIndex]); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, SrvDilatedDepth, UavDilatedDepth); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, SrvTransparencyAndCompositionMask, Resources.DefaultReactive); // Default reactive mask, as we don't support TCR (yet) commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, SrvPrevDilatedMotionVectors, Resources.DilatedMotionVectors[frameIndex ^ 1]); commandBuffer.SetComputeConstantBufferParam(ComputeShader, CbFsr2, Constants, 0, Marshal.SizeOf()); commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); } } internal class Fsr2LockPipeline : Fsr2Pipeline { public Fsr2LockPipeline(Fsr2.ContextDescription contextDescription, Fsr2Resources resources, ComputeBuffer constants) : base(contextDescription, resources, constants) { LoadComputeShader("FSR2/ffx_fsr2_lock_pass"); } public override void ScheduleDispatch(CommandBuffer commandBuffer, Fsr2.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) { commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, SrvLockInputLuma, UavLockInputLuma); commandBuffer.SetComputeConstantBufferParam(ComputeShader, CbFsr2, Constants, 0, Marshal.SizeOf()); commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); } } internal class Fsr2AccumulatePipeline : Fsr2Pipeline { // Workaround: Disable FP16 path for the accumulate pass on NVIDIA due to reduced occupancy and high VRAM throughput. protected override bool AllowFP16 => SystemInfo.graphicsDeviceVendorID != 0x10DE; public Fsr2AccumulatePipeline(Fsr2.ContextDescription contextDescription, Fsr2Resources resources, ComputeBuffer constants) : base(contextDescription, resources, constants) { LoadComputeShader("FSR2/ffx_fsr2_accumulate_pass"); } public override void ScheduleDispatch(CommandBuffer commandBuffer, Fsr2.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) { if ((ContextDescription.Flags & Fsr2.InitializationFlags.EnableDisplayResolutionMotionVectors) == 0) commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, SrvDilatedMotionVectors, Resources.DilatedMotionVectors[frameIndex]); else if (dispatchParams.MotionVectors.HasValue) commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, SrvInputMotionVectors, dispatchParams.MotionVectors.Value); if (dispatchParams.Exposure.HasValue) commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, SrvInputExposure, dispatchParams.Exposure.Value); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, SrvDilatedReactiveMasks, UavDilatedReactiveMasks); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, SrvInternalUpscaled, Resources.InternalUpscaled[frameIndex ^ 1]); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, SrvLockStatus, Resources.LockStatus[frameIndex ^ 1]); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, SrvPreparedInputColor, UavPreparedInputColor); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, SrvLanczosLut, Resources.LanczosLut); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, SrvUpscaleMaximumBiasLut, Resources.MaximumBiasLut); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, SrvSceneLuminanceMips, Resources.SceneLuminance); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, SrvAutoExposure, Resources.AutoExposure); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, SrvLumaHistory, Resources.LumaHistory[frameIndex ^ 1]); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, UavInternalUpscaled, Resources.InternalUpscaled[frameIndex]); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, UavLockStatus, Resources.LockStatus[frameIndex]); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, UavLumaHistory, Resources.LumaHistory[frameIndex]); if (dispatchParams.Output.HasValue) commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, UavUpscaledOutput, dispatchParams.Output.Value); commandBuffer.SetComputeConstantBufferParam(ComputeShader, CbFsr2, Constants, 0, Marshal.SizeOf()); commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); } } internal class Fsr2AccumulateSharpenPipeline : Fsr2AccumulatePipeline { private readonly ComputeShader _shaderCopy; public Fsr2AccumulateSharpenPipeline(Fsr2.ContextDescription contextDescription, Fsr2Resources resources, ComputeBuffer constants) : base(contextDescription, resources, constants) { // Simply loading the accumulate_pass compute shader will give us the same instance as the non-sharpen pipeline // So we have to clone the shader instance and set the extra keyword on the new copy _shaderCopy = UnityEngine.Object.Instantiate(ComputeShader); foreach (var keyword in ComputeShader.shaderKeywords) { _shaderCopy.EnableKeyword(keyword); } _shaderCopy.EnableKeyword("FFX_FSR2_OPTION_APPLY_SHARPENING"); } public override void ScheduleDispatch(CommandBuffer commandBuffer, Fsr2.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) { // Temporarily swap around the shaders so that the dispatch will bind and execute the correct one ComputeShader tmp = ComputeShader; ComputeShader = _shaderCopy; base.ScheduleDispatch(commandBuffer, dispatchParams, frameIndex, dispatchX, dispatchY); ComputeShader = tmp; } public override void Dispose() { // Since we instantiated this copy, we have to destroy it instead of unloading the shader resource UnityEngine.Object.Destroy(_shaderCopy); base.Dispose(); } } internal class Fsr2RcasPipeline : Fsr2Pipeline { private readonly ComputeBuffer _rcasConstants; public Fsr2RcasPipeline(Fsr2.ContextDescription contextDescription, Fsr2Resources resources, ComputeBuffer constants, ComputeBuffer rcasConstants) : base(contextDescription, resources, constants) { _rcasConstants = rcasConstants; LoadComputeShader("FSR2/ffx_fsr2_rcas_pass"); } public override void ScheduleDispatch(CommandBuffer commandBuffer, Fsr2.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) { if (dispatchParams.Exposure.HasValue) commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, SrvInputExposure, dispatchParams.Exposure.Value); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, SrvRcasInput, Resources.InternalUpscaled[frameIndex]); if (dispatchParams.Output.HasValue) commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, UavUpscaledOutput, dispatchParams.Output.Value); commandBuffer.SetComputeConstantBufferParam(ComputeShader, CbFsr2, Constants, 0, Marshal.SizeOf()); commandBuffer.SetComputeConstantBufferParam(ComputeShader, CbRcas, _rcasConstants, 0, Marshal.SizeOf()); commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); } } internal class Fsr2GenerateReactivePipeline : Fsr2Pipeline { private readonly ComputeBuffer _generateReactiveConstants; public Fsr2GenerateReactivePipeline(Fsr2.ContextDescription contextDescription, Fsr2Resources resources, ComputeBuffer generateReactiveConstants) : base(contextDescription, resources, null) { _generateReactiveConstants = generateReactiveConstants; LoadComputeShader("FSR2/ffx_fsr2_autogen_reactive_pass"); } public override void ScheduleDispatch(CommandBuffer commandBuffer, Fsr2.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) { } public void ScheduleDispatch(CommandBuffer commandBuffer, Fsr2.GenerateReactiveDescription dispatchParams, int dispatchX, int dispatchY) { if (dispatchParams.ColorOpaqueOnly.HasValue) commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, SrvOpaqueOnly, dispatchParams.ColorOpaqueOnly.Value, 0, RenderTextureSubElement.Color); if (dispatchParams.ColorPreUpscale.HasValue) commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, SrvInputColor, dispatchParams.ColorPreUpscale.Value, 0, RenderTextureSubElement.Color); if (dispatchParams.OutReactive.HasValue) commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, UavAutoReactive, dispatchParams.OutReactive.Value); commandBuffer.SetComputeConstantBufferParam(ComputeShader, CbGenReactive, _generateReactiveConstants, 0, Marshal.SizeOf()); commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); } } }